diff options
Diffstat (limited to 'libs')
102 files changed, 4511 insertions, 3140 deletions
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp index 7fbbb61e469d..b6622bf0fa60 100644 --- a/libs/WindowManager/Jetpack/Android.bp +++ b/libs/WindowManager/Jetpack/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_library_import { name: "window-sidecar", aars: ["window-sidecar-release.aar"], diff --git a/libs/WindowManager/OWNERS b/libs/WindowManager/OWNERS index 063d4594f2fa..2c61df96eb03 100644 --- a/libs/WindowManager/OWNERS +++ b/libs/WindowManager/OWNERS @@ -1,3 +1,3 @@ set noparent -include ../../services/core/java/com/android/server/wm/OWNERS
\ No newline at end of file +include /services/core/java/com/android/server/wm/OWNERS diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index b8934dc8c583..2987dabe4e30 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_library { name: "WindowManager-Shell", srcs: [ diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS index 4390004f5f93..e2c67fd8f8d4 100644 --- a/libs/WindowManager/Shell/OWNERS +++ b/libs/WindowManager/Shell/OWNERS @@ -1,4 +1,4 @@ # sysui owners hwwang@google.com -mrenouf@google.com -winsonc@google.com
\ No newline at end of file +winsonc@google.com +madym@google.com diff --git a/libs/WindowManager/Shell/tests/Android.bp b/libs/WindowManager/Shell/tests/Android.bp index 78fa45ebdf94..ca37ad75467d 100644 --- a/libs/WindowManager/Shell/tests/Android.bp +++ b/libs/WindowManager/Shell/tests/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "WindowManagerShellTests", diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS new file mode 100644 index 000000000000..2c6c7b358e3b --- /dev/null +++ b/libs/WindowManager/Shell/tests/OWNERS @@ -0,0 +1,2 @@ +# includes OWNERS from parent directories +natanieljr@google.com diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 02c85aa34f4b..581af0d0628a 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -14,6 +14,23 @@ // libandroidfw is partially built for the host (used by obbtool, aapt, and others) +package { + default_applicable_licenses: ["frameworks_base_libs_androidfw_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_libs_androidfw_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_defaults { name: "libandroidfw_defaults", cflags: [ @@ -61,6 +78,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 +89,14 @@ cc_library { "CursorWindow.cpp", ], shared_libs: [ - "libziparchive", "libbase", "libbinder", "liblog", "libcutils", + "libincfs", "libutils", "libz", + "libziparchive", ], static: { enabled: false, @@ -86,11 +107,11 @@ cc_library { enabled: false, }, static_libs: [ - "libziparchive", "libbase", - "liblog", "libcutils", + "liblog", "libutils", + "libziparchive", ], shared_libs: [ "libz", @@ -101,7 +122,7 @@ cc_library { }, }, sanitize: { - blacklist: "libandroidfw_blacklist.txt", + blocklist: "libandroidfw_blocklist.txt", }, } 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<FileMap> map = util::make_unique<FileMap>(); - 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 = - 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<FileMap> map = util::make_unique<FileMap>(); - 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 = 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<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd, } } - std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>(); - if (!file_map->create(path, fd, offset, static_cast<size_t>(length), true /*readOnly*/)) { + incfs::IncFsFileMap file_map; + if (!file_map.Create(fd, offset, static_cast<size_t>(length), path)) { LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': " << SystemErrorCodeToString(errno); return {}; @@ -456,8 +450,8 @@ std::unique_ptr<Asset> 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<const ApkAssets> ApkAssets::LoadImpl( @@ -493,15 +487,14 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl( loaded_apk->idmap_asset_ = std::move(idmap_asset); loaded_apk->loaded_idmap_ = std::move(idmap); - const StringPiece data( - reinterpret_cast<const char*>(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<const ApkAssets> 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<const char*>(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> 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> Asset::createFromUncompressedMap(std::unique_ptr<FileMap> 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> 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> Asset::createFromCompressedMap(std::unique_ptr<FileMap> 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<char>(); + 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<void> _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<void> _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<uint8_t>().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<void> _CompressedAsset::getIncFsBuffer(bool aligned) { + return incfs::map_ptr<void>(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<Asset> 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<incfs::IncFsFileMap> 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<size_t>(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 b9765ea7212c..bec80a7d605e 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -38,11 +38,43 @@ namespace android { +namespace { + +using EntryValue = std::variant<Res_value, incfs::verified_map_ptr<ResTable_map_entry>>; + +base::expected<EntryValue, IOError> GetEntryValue( + incfs::verified_map_ptr<ResTable_entry> 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<ResTable_map_entry>(); + 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<Res_value>(); + 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 resource table entry for this resource. - // If the size of the entry is > sizeof(ResTable_entry), it can be cast to - // a ResTable_map_entry and processed as a bag/map. - ResTable_entry_handle 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<ResTable_map_entry> for bag types. + EntryValue entry; // The configuration for which the resulting entry was defined. This is already swapped to host // endianness. @@ -267,7 +299,7 @@ const std::unordered_map<std::string, std::string>* } const PackageGroup& package_group = package_groups_[idx]; - if (package_group.packages_.size() == 0) { + if (package_group.packages_.empty()) { return nullptr; } @@ -312,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)); @@ -367,8 +399,8 @@ std::set<std::string> AssetManager2::GetNonSystemOverlayPaths() const { return non_system_overlays; } -std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) const { +base::expected<std::set<ResTable_config>, IOError> AssetManager2::GetResourceConfigurations( + bool exclude_system, bool exclude_mipmap) const { ATRACE_NAME("AssetManager::GetResourceConfigurations"); const auto non_system_overlays = (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>(); @@ -388,7 +420,10 @@ std::set<ResTable_config> 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; @@ -503,11 +538,11 @@ std::unique_ptr<Asset> 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<FindEntryResult, NullOrIOError> 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; @@ -525,96 +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; } - - if (overlay_entry.IsTableEntry()) { + if (overlay_entry.IsInlineValue()) { // The target resource is overlaid by an inline value not represented by a resource. - out_entry->entry = overlay_entry.GetTableEntry(); - 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 = std::move(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<FindEntryResult, NullOrIOError> 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<ResTable_type> 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::Step> resolution_steps; // If desired_config is the same as the set configuration, then we can use our filtered list @@ -634,17 +669,20 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro continue; } + auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx); + if (UNLIKELY(!entry_flags.has_value())) { + 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<ResTable_config>& 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. @@ -656,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, @@ -668,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 { @@ -688,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()}); @@ -704,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<ResTable_type>& 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; } @@ -726,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()}); @@ -753,24 +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()); } - out_entry->entry = ResTable_entry_handle::unmanaged(best_entry); - 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(); + const incfs::map_ptr<ResTable_entry> best_entry = *best_entry_result; + if (!best_entry) { + return base::unexpected(IOError::PAGES_MISSING); + } - return best_cookie; + 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 { @@ -791,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<Resolution::Step>& 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()) : "<unknown>"; } std::stringstream log_stream; @@ -867,207 +921,208 @@ 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::ResourceName, NullOrIOError> 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); -} - -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; + return ToResourceName(result->type_string_ref, + result->entry_string_ref, + *result->package_name); } -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::SelectedValue, NullOrIOError> 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()); } - const ResTable_entry* table_entry = *entry.entry; - if (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) { + auto result_map_entry = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&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); } - const Res_value* device_value = reinterpret_cast<const Res_value*>( - reinterpret_cast<const uint8_t*>(table_entry) + dtohs(table_entry->size)); - out_value->copyFrom_dtoh(*device_value); - // Convert the package ID to the runtime assigned package ID. - entry.dynamic_ref_table->lookupResourceValue(out_value); + Res_value value = std::get<Res_value>(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; +base::expected<std::monostate, NullOrIOError> AssetManager2::ResolveReference( + AssetManager2::SelectedValue& value, bool cache_value) const { + if (value.type != Res_value::TYPE_REFERENCE || value.data == 0U) { + // Not a reference. Nothing to do. + return {}; + } + + 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 {}; } - if (in_out_flags != nullptr) { - *in_out_flags |= new_flags; + } + + 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*/); + if (!result.has_value()) { + value.resid = resolve_resid; + return base::unexpected(result.error()); } - if (*out_last_reference == in_out_value->data) { + + // 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. - return cookie; + 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 {}; } + + combined_flags = result->flags; + resolve_resid = result->data; } - return cookie; } -const std::vector<uint32_t> AssetManager2::GetBagResIdStack(uint32_t resid) { +const std::vector<uint32_t> 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<uint32_t>(); - 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<uint32_t> 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<uint32_t>(); - auto bag = GetBag(resid, found_resids); +base::expected<const ResolvedBag*, NullOrIOError> 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<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(uint32_t resid) const { + std::vector<uint32_t> 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<uint32_t>& child_resids) { - auto cached_iter = cached_bags_.find(resid); - if (cached_iter != cached_bags_.end()) { +base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag( + uint32_t resid, std::vector<uint32_t>& 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()); } - // Check that the size of the entry header is at least as big as - // the desired ResTable_map_entry. Also verify that the entry - // was intended to be a map. - const ResTable_entry* table_entry = *entry.entry; - if (dtohs(table_entry->size) < sizeof(ResTable_map_entry) || - (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) { + auto entry_map = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&entry->entry); + if (entry_map == nullptr) { // Not a bag, nothing to do. - return nullptr; + return base::unexpected(std::nullopt); } - const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(table_entry); - const ResTable_map* map_entry = - reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(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<ResTable_map>(); + 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<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>( 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<uint32_t>(entry_count); ResolvedBag* result = new_bag.get(); cached_bags_[resid] = std::move(new_bag); @@ -1075,54 +1130,58 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& } // 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<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>( 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 { @@ -1142,25 +1201,29 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& // 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)); @@ -1184,11 +1247,12 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& } 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<uint32_t>(actual_count); ResolvedBag* result = new_bag.get(); cached_bags_[resid] = std::move(new_bag); @@ -1207,16 +1271,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<uint32_t, NullOrIOError> 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()) { @@ -1229,12 +1293,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"; @@ -1248,20 +1312,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<uint32_t, NullOrIOError> 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) { @@ -1281,8 +1349,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}); } } }); @@ -1308,6 +1375,8 @@ void AssetManager2::InvalidateCaches(uint32_t diff) { ++iter; } } + + cached_resolved_values_.clear(); } uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const { @@ -1353,16 +1422,16 @@ struct Theme::Package { std::array<util::unique_cptr<ThemeType>, kTypeCount> types; }; -bool Theme::ApplyStyle(uint32_t resid, bool force) { +base::expected<std::monostate, NullOrIOError> 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; @@ -1372,14 +1441,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 ResolvedBag::Entry*>; - 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 @@ -1427,20 +1496,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<AssetManager2::SelectedValue> 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(); @@ -1460,43 +1527,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<std::monostate, NullOrIOError> 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<AssetManager2::SelectedValue> 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, true /* cache_value */); + if (resolve_result.has_value()) { + result->flags |= value.flags; + value = *result; + } + return resolve_result; } void Theme::Clear() { @@ -1506,9 +1572,9 @@ void Theme::Clear() { } } -void Theme::SetTo(const Theme& o) { +base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) { if (this == &o) { - return; + return {}; } type_spec_flags_ = o.type_spec_flags_; @@ -1559,10 +1625,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<std::unique_ptr<const LoadedPackage>>& src_packages = - src_asset->GetLoadedArsc()->GetPackages(); - const std::vector<std::unique_ptr<const LoadedPackage>>& 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; @@ -1659,15 +1723,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<FindEntryResult, NullOrIOError> 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; } @@ -1711,6 +1780,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..c188712e5877 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<uint32_t>(cookie + 1) : static_cast<uint32_t>(-1); @@ -36,8 +39,7 @@ class XmlAttributeFinder : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> { 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 { @@ -61,136 +63,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<const ResolvedBag*, NullOrIOError> 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<AssetManager2::SelectedValue> 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<const ResolvedBag*, NullOrIOError> 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<AssetManager2::SelectedValue> 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<std::monostate, IOError> 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, true /* cache_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 +214,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<std::monostate, IOError> 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 +264,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, true /* cache_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<std::monostate, IOError> 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 +418,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<ResChunk_header> 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<const ResChunk_header*>( - reinterpret_cast<const uint8_t*>(this_chunk) + dtohl(this_chunk->size)); + next_chunk_ = this_chunk.offset(dtohl(this_chunk->size)).convert<ResChunk_header>(); 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<uintptr_t>(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/ConfigDescription.cpp b/libs/androidfw/ConfigDescription.cpp index 1f3a89edb8af..19ead9583eb2 100644 --- a/libs/androidfw/ConfigDescription.cpp +++ b/libs/androidfw/ConfigDescription.cpp @@ -887,13 +887,16 @@ bool ConfigDescription::Dominates(const ConfigDescription& o) const { } // Locale de-duping is not-trivial, disable for now (b/62409213). - if (diff(o) & CONFIG_LOCALE) { + // We must also disable de-duping for all configuration qualifiers with precedence higher than + // locale (b/171892595) + if (diff(o) & (CONFIG_LOCALE | CONFIG_MCC | CONFIG_MNC)) { return false; } if (*this == DefaultConfig()) { return true; } + return MatchWithDensity(o) && !o.MatchWithDensity(*this) && !isMoreSpecificThan(o) && !o.HasHigherPrecedenceThan(*this); } diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index 5f231ffe4786..a61309514143 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -36,16 +36,12 @@ using ::android::base::StringPrintf; namespace android { -static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) { - return dtohl(e1.target_id) < target_id; -} - -static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) { - return dtohl(e1.overlay_id) < overlay_id; +uint32_t round_to_4_bytes(uint32_t size) { + return size + (4U - (size % 4U)) % 4U; } size_t Idmap_header::Size() const { - return sizeof(Idmap_header) + sizeof(uint8_t) * dtohl(debug_info_size); + return sizeof(Idmap_header) + sizeof(uint8_t) * round_to_4_bytes(dtohl(debug_info_size)); } OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap) @@ -56,22 +52,22 @@ OverlayStringPool::~OverlayStringPool() { uninit(); } -const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const { +base::expected<StringPiece16, NullOrIOError> 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<StringPiece, NullOrIOError> 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 { @@ -88,7 +84,10 @@ OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_hea status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const { const Idmap_overlay_entry* first_entry = entries_; const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count); - auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries); + auto entry = std::lower_bound(first_entry, end_entry, *resId, + [](const Idmap_overlay_entry& e1, const uint32_t overlay_id) { + return dtohl(e1.overlay_id) < overlay_id; + }); if (entry == end_entry || dtohl(entry->overlay_id) != *resId) { // A mapping for the target resource id could not be found. @@ -96,7 +95,7 @@ status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const { } *resId = (0x00FFFFFFU & dtohl(entry->target_id)) - | (((uint32_t) target_assigned_package_id_) << 24); + | (((uint32_t) target_assigned_package_id_) << 24U); return NO_ERROR; } @@ -106,62 +105,58 @@ status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) cons IdmapResMap::IdmapResMap(const Idmap_data_header* data_header, const Idmap_target_entry* entries, + const Idmap_target_entry_inline* inline_entries, uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) : data_header_(data_header), entries_(entries), + inline_entries_(inline_entries), target_assigned_package_id_(target_assigned_package_id), - overlay_ref_table_(overlay_ref_table) { }; + overlay_ref_table_(overlay_ref_table) { } IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const { - if ((target_res_id >> 24) != target_assigned_package_id_) { + if ((target_res_id >> 24U) != target_assigned_package_id_) { // The resource id must have the same package id as the target package. return {}; } // The resource ids encoded within the idmap are build-time resource ids. target_res_id = (0x00FFFFFFU & target_res_id) - | (((uint32_t) data_header_->target_package_id) << 24); - - const Idmap_target_entry* first_entry = entries_; - const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count); - auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries); - - if (entry == end_entry || dtohl(entry->target_id) != target_res_id) { - // A mapping for the target resource id could not be found. - return {}; - } - - // A reference should be treated as an alias of the resource. Instead of returning the table - // entry, return the alias resource id to look up. The alias resource might not reside within the - // overlay package, so the resource id must be fixed with the dynamic reference table of the - // overlay before returning. - if (entry->type == Res_value::TYPE_REFERENCE - || entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) { - uint32_t overlay_resource_id = dtohl(entry->value); - + | (((uint32_t) data_header_->target_package_id) << 24U); + + // Check if the target resource is mapped to an overlay resource. + auto first_entry = entries_; + auto end_entry = entries_ + dtohl(data_header_->target_entry_count); + auto entry = std::lower_bound(first_entry, end_entry, target_res_id, + [](const Idmap_target_entry &e, const uint32_t target_id) { + return dtohl(e.target_id) < target_id; + }); + + if (entry != end_entry && dtohl(entry->target_id) == target_res_id) { + uint32_t overlay_resource_id = dtohl(entry->overlay_id); // Lookup the resource without rewriting the overlay resource id back to the target resource id // being looked up. overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id); return Result(overlay_resource_id); } - // Copy the type and value into the ResTable_entry structure needed by asset manager. - uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value); - auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size)); - memset(table_entry, 0, malloc_size); - table_entry->size = htods(sizeof(ResTable_entry)); - - auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry) - + sizeof(ResTable_entry)); - table_value->dataType = entry->type; - table_value->data = entry->value; - - return Result(ResTable_entry_handle::managed(table_entry, [](auto p) { free(p); })); + // Check if the target resources is mapped to an inline table entry. + auto first_inline_entry = inline_entries_; + auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count); + auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id, + [](const Idmap_target_entry_inline &e, + const uint32_t target_id) { + return dtohl(e.target_id) < target_id; + }); + + if (inline_entry != end_inline_entry && dtohl(inline_entry->target_id) == target_res_id) { + return Result(inline_entry->value); + } + return {}; } static bool is_word_aligned(const void* data) { - return (reinterpret_cast<uintptr_t>(data) & 0x03) == 0; + return (reinterpret_cast<uintptr_t>(data) & 0x03U) == 0U; } static bool IsValidIdmapHeader(const StringPiece& data) { @@ -175,7 +170,7 @@ static bool IsValidIdmapHeader(const StringPiece& data) { return false; } - const Idmap_header* header = reinterpret_cast<const Idmap_header*>(data.data()); + auto header = reinterpret_cast<const Idmap_header*>(data.data()); if (dtohl(header->magic) != kIdmapMagic) { LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)", dtohl(header->magic), kIdmapMagic); @@ -198,11 +193,13 @@ LoadedIdmap::LoadedIdmap(std::string&& idmap_path, const Idmap_header* header, const Idmap_data_header* data_header, const Idmap_target_entry* target_entries, + const Idmap_target_entry_inline* target_inline_entries, const Idmap_overlay_entry* overlay_entries, ResStringPool* string_pool) : header_(header), data_header_(data_header), target_entries_(target_entries), + target_inline_entries_(target_inline_entries), overlay_entries_(overlay_entries), string_pool_(string_pool), idmap_path_(std::move(idmap_path)), @@ -233,7 +230,7 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_pa data_ptr += sizeof(*data_header); data_size -= sizeof(*data_header); - // Make sure there is enough space for the target entries declared in the header. + // Make sure there is enough space for the target entries declared in the header const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr); if (data_size / sizeof(Idmap_target_entry) < static_cast<size_t>(dtohl(data_header->target_entry_count))) { @@ -248,6 +245,21 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_pa data_ptr += target_entry_size_bytes; data_size -= target_entry_size_bytes; + // Make sure there is enough space for the target entries declared in the header. + const auto target_inline_entries = reinterpret_cast<const Idmap_target_entry_inline*>(data_ptr); + if (data_size / sizeof(Idmap_target_entry_inline) < + static_cast<size_t>(dtohl(data_header->target_inline_entry_count))) { + LOG(ERROR) << StringPrintf("Idmap too small for the number of target inline entries (%d)", + (int)dtohl(data_header->target_inline_entry_count)); + return {}; + } + + // Advance the data pointer past the target entries. + const size_t target_inline_entry_size_bytes = + (dtohl(data_header->target_inline_entry_count) * sizeof(Idmap_target_entry_inline)); + data_ptr += target_inline_entry_size_bytes; + data_size -= target_inline_entry_size_bytes; + // Make sure there is enough space for the overlay entries declared in the header. const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr); if (data_size / sizeof(Idmap_overlay_entry) < @@ -257,22 +269,26 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_pa return {}; } - // Advance the data pointer past the target entries. + // Advance the data pointer past the overlay entries. const size_t overlay_entry_size_bytes = (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry)); data_ptr += overlay_entry_size_bytes; data_size -= overlay_entry_size_bytes; // Read the idmap string pool that holds the value of inline string entries. - if (data_size < dtohl(data_header->string_pool_length)) { + uint32_t string_pool_size = dtohl(*reinterpret_cast<const uint32_t*>(data_ptr)); + data_ptr += sizeof(uint32_t); + data_size -= sizeof(uint32_t); + + if (data_size < string_pool_size) { LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)", - (int)dtohl(data_header->string_pool_length)); + (int)string_pool_size); return {}; } auto idmap_string_pool = util::make_unique<ResStringPool>(); - if (dtohl(data_header->string_pool_length) > 0) { - status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length)); + if (string_pool_size > 0) { + status_t err = idmap_string_pool->setTo(data_ptr, string_pool_size); if (err != NO_ERROR) { LOG(ERROR) << "idmap string pool corrupt."; return {}; @@ -280,9 +296,10 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_pa } // Can't use make_unique because LoadedIdmap constructor is private. - std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>( + auto loaded_idmap = std::unique_ptr<LoadedIdmap>( new LoadedIdmap(idmap_path.to_string(), getFileModDate(idmap_path.data()), header, - data_header, target_entries, overlay_entries, idmap_string_pool.release())); + data_header, target_entries, target_inline_entries, overlay_entries, + idmap_string_pool.release())); return std::move(loaded_idmap); } 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<ResTable_typeSpec> header) : header_(header) { } - void AddType(const ResTable_type* type) { + void AddType(incfs::verified_map_ptr<ResTable_type> type) { types_.push_back(type); } TypeSpecPtr Build() { // Check for overflow. - using ElementType = const ResTable_type*; + using ElementType = incfs::verified_map_ptr<ResTable_type>; if ((std::numeric_limits<size_t>::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<const ResTable_type*> types_; + incfs::verified_map_ptr<ResTable_typeSpec> header_; + std::vector<incfs::verified_map_ptr<ResTable_type>> 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<ResTable_type> 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<std::monostate, NullOrIOError> VerifyResTableEntry( + incfs::verified_map_ptr<ResTable_type> 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<uint32_t>::max() - dtohl(type->entriesStart)) { + if (UNLIKELY(entry_offset > std::numeric_limits<uint32_t>::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<const ResTable_entry*>( - reinterpret_cast<const uint8_t*>(type) + entry_offset); + auto entry = type.offset(entry_offset).convert<ResTable_entry>(); + 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<Res_value>(); + if (UNLIKELY(!value)) { + return base::unexpected(IOError::PAGES_MISSING); } - const Res_value* value = - reinterpret_cast<const Res_value*>(reinterpret_cast<const uint8_t*>(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<const ResTable_map_entry*>(entry); + auto map = entry.convert<ResTable_map_entry>(); + 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<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntry( + incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) { + base::expected<uint32_t, NullOrIOError> 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<uint32_t, NullOrIOError> LoadedPackage::GetEntryOffset( + incfs::verified_map_ptr<ResTable_type> 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<const ResTable_sparseTypeEntry*>( - reinterpret_cast<const uint8_t*>(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<ResTable_sparseTypeEntry>().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<ResTable_sparseTypeEntry>& 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<ResTable_sparseTypeEntry> 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<const uint32_t*>( - reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset); - return dtohl(entry_offsets[entry_index]); + const auto entry_offset_ptr = type_chunk.offset(offsets_offset).convert<uint32_t>() + 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<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntryFromOffset( + incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset) { + auto valid = VerifyResTableEntry(type_chunk, offset); + if (UNLIKELY(!valid.has_value())) { + return base::unexpected(valid.error()); } - return reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type_chunk) + - offset + dtohl(type_chunk->entriesStart)); + return type_chunk.offset(offset + dtohl(type_chunk->entriesStart)).convert<ResTable_entry>(); } -void LoadedPackage::CollectConfigurations(bool exclude_mipmap, - std::set<ResTable_config>* out_configs) const { - const static std::u16string kMipMap = u"mipmap"; +base::expected<std::monostate, IOError> LoadedPackage::CollectConfigurations( + bool exclude_mipmap, std::set<ResTable_config>* 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<std::string>* out_locales) const { @@ -348,43 +384,53 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* 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<uint32_t, NullOrIOError> LoadedPackage::FindEntryByName( + const std::u16string& type_name, const std::u16string& entry_name) const { + const base::expected<size_t, NullOrIOError> 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<size_t, NullOrIOError> 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<ResTable_type>& 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<const uint32_t*>( - reinterpret_cast<const uint8_t*>(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<uint32_t>() + + 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<const ResTable_entry*>( - reinterpret_cast<const uint8_t*>(type) + dtohl(type->entriesStart) + offset); - if (dtohl(entry->key.index) == static_cast<uint32_t>(key_idx)) { + auto entry = type.offset(dtohl(type->entriesStart) + offset).convert<ResTable_entry>(); + if (!entry) { + return base::unexpected(IOError::PAGES_MISSING); + } + + if (dtohl(entry->key.index) == static_cast<uint32_t>(*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<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, // was added. constexpr size_t kMinPackageSize = sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset); - const ResTable_package* header = chunk.header<ResTable_package, kMinPackageSize>(); - if (header == nullptr) { + const incfs::map_ptr<ResTable_package> header = chunk.header<ResTable_package, kMinPackageSize>(); + if (!header) { LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE too small."; return {}; } @@ -453,10 +499,13 @@ std::unique_ptr<const LoadedPackage> 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<uintptr_t>(child_chunk.header<ResChunk_header>()); - const uintptr_t header_address = reinterpret_cast<uintptr_t>(header); - if (pool_address == header_address + dtohl(header->typeStrings)) { + const auto pool_address = child_chunk.header<ResChunk_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<ResChunk_header>()) { // This string pool is the type string pool. status_t err = loaded_package->type_string_pool_.setTo( child_chunk.header<ResStringPool_header>(), child_chunk.size()); @@ -464,7 +513,8 @@ std::unique_ptr<const LoadedPackage> 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<ResChunk_header>()) { // This string pool is the key string pool. status_t err = loaded_package->key_string_pool_.setTo( child_chunk.header<ResStringPool_header>(), child_chunk.size()); @@ -478,8 +528,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_TYPE_SPEC_TYPE: { - const ResTable_typeSpec* type_spec = child_chunk.header<ResTable_typeSpec>(); - if (type_spec == nullptr) { + const auto type_spec = child_chunk.header<ResTable_typeSpec>(); + if (!type_spec) { LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small."; return {}; } @@ -514,7 +564,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type_spec->id - 1]; if (builder_ptr == nullptr) { - builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec); + builder_ptr = util::make_unique<TypeSpecPtrBuilder>(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<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_TYPE_TYPE: { - const ResTable_type* type = child_chunk.header<ResTable_type, kResTableTypeMinSize>(); - if (type == nullptr) { + const auto type = child_chunk.header<ResTable_type, kResTableTypeMinSize>(); + if (!type) { LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small."; return {}; } @@ -536,7 +586,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, // Type chunks must be preceded by their TypeSpec chunks. std::unique_ptr<TypeSpecPtrBuilder>& 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<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_LIBRARY_TYPE: { - const ResTable_lib_header* lib = child_chunk.header<ResTable_lib_header>(); - if (lib == nullptr) { + const auto lib = child_chunk.header<ResTable_lib_header>(); + if (!lib) { LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small."; return {}; } @@ -559,10 +609,13 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, loaded_package->dynamic_package_map_.reserve(dtohl(lib->count)); - const ResTable_lib_entry* const entry_begin = - reinterpret_cast<const ResTable_lib_entry*>(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<ResTable_lib_entry>(); + 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<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_OVERLAYABLE_TYPE: { - const ResTable_overlayable_header* header = - child_chunk.header<ResTable_overlayable_header>(); - if (header == nullptr) { + const auto overlayable = child_chunk.header<ResTable_overlayable_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<const LoadedPackage> 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<ResTable_overlayable_policy_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<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, // Retrieve all the resource ids belonging to this policy chunk std::unordered_set<uint32_t> ids; - const auto ids_begin = - reinterpret_cast<const ResTable_ref*>(overlayable_child_chunk.data_ptr()); + const auto ids_begin = overlayable_child_chunk.data_ptr().convert<ResTable_ref>(); 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<const LoadedPackage> 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<const LoadedPackage> 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<ResTable_header>(); - if (header == nullptr) { + incfs::map_ptr<ResTable_header> header = chunk.header<ResTable_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<const LoadedArsc> LoadedArsc::Load(const StringPiece& data, +std::unique_ptr<const LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> 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<const LoadedArsc> LoadedArsc::Load(const StringPiece& data, // Not using make_unique because the constructor is private. std::unique_ptr<LoadedArsc> 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/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp index 6c6c5c9f4e22..8a10599b498f 100644 --- a/libs/androidfw/LocaleDataTables.cpp +++ b/libs/androidfw/LocaleDataTables.cpp @@ -64,42 +64,43 @@ const char SCRIPT_CODES[][4] = { /* 60 */ {'N', 'k', 'o', 'o'}, /* 61 */ {'N', 's', 'h', 'u'}, /* 62 */ {'O', 'g', 'a', 'm'}, - /* 63 */ {'O', 'r', 'k', 'h'}, - /* 64 */ {'O', 'r', 'y', 'a'}, - /* 65 */ {'O', 's', 'g', 'e'}, - /* 66 */ {'P', 'a', 'u', 'c'}, - /* 67 */ {'P', 'h', 'l', 'i'}, - /* 68 */ {'P', 'h', 'n', 'x'}, - /* 69 */ {'P', 'l', 'r', 'd'}, - /* 70 */ {'P', 'r', 't', 'i'}, - /* 71 */ {'R', 'u', 'n', 'r'}, - /* 72 */ {'S', 'a', 'm', 'r'}, - /* 73 */ {'S', 'a', 'r', 'b'}, - /* 74 */ {'S', 'a', 'u', 'r'}, - /* 75 */ {'S', 'g', 'n', 'w'}, - /* 76 */ {'S', 'i', 'n', 'h'}, - /* 77 */ {'S', 'o', 'g', 'd'}, - /* 78 */ {'S', 'o', 'r', 'a'}, - /* 79 */ {'S', 'o', 'y', 'o'}, - /* 80 */ {'S', 'y', 'r', 'c'}, - /* 81 */ {'T', 'a', 'l', 'e'}, - /* 82 */ {'T', 'a', 'l', 'u'}, - /* 83 */ {'T', 'a', 'm', 'l'}, - /* 84 */ {'T', 'a', 'n', 'g'}, - /* 85 */ {'T', 'a', 'v', 't'}, - /* 86 */ {'T', 'e', 'l', 'u'}, - /* 87 */ {'T', 'f', 'n', 'g'}, - /* 88 */ {'T', 'h', 'a', 'a'}, - /* 89 */ {'T', 'h', 'a', 'i'}, - /* 90 */ {'T', 'i', 'b', 't'}, - /* 91 */ {'U', 'g', 'a', 'r'}, - /* 92 */ {'V', 'a', 'i', 'i'}, - /* 93 */ {'W', 'c', 'h', 'o'}, - /* 94 */ {'X', 'p', 'e', 'o'}, - /* 95 */ {'X', 's', 'u', 'x'}, - /* 96 */ {'Y', 'i', 'i', 'i'}, - /* 97 */ {'~', '~', '~', 'A'}, - /* 98 */ {'~', '~', '~', 'B'}, + /* 63 */ {'O', 'l', 'c', 'k'}, + /* 64 */ {'O', 'r', 'k', 'h'}, + /* 65 */ {'O', 'r', 'y', 'a'}, + /* 66 */ {'O', 's', 'g', 'e'}, + /* 67 */ {'P', 'a', 'u', 'c'}, + /* 68 */ {'P', 'h', 'l', 'i'}, + /* 69 */ {'P', 'h', 'n', 'x'}, + /* 70 */ {'P', 'l', 'r', 'd'}, + /* 71 */ {'P', 'r', 't', 'i'}, + /* 72 */ {'R', 'u', 'n', 'r'}, + /* 73 */ {'S', 'a', 'm', 'r'}, + /* 74 */ {'S', 'a', 'r', 'b'}, + /* 75 */ {'S', 'a', 'u', 'r'}, + /* 76 */ {'S', 'g', 'n', 'w'}, + /* 77 */ {'S', 'i', 'n', 'h'}, + /* 78 */ {'S', 'o', 'g', 'd'}, + /* 79 */ {'S', 'o', 'r', 'a'}, + /* 80 */ {'S', 'o', 'y', 'o'}, + /* 81 */ {'S', 'y', 'r', 'c'}, + /* 82 */ {'T', 'a', 'l', 'e'}, + /* 83 */ {'T', 'a', 'l', 'u'}, + /* 84 */ {'T', 'a', 'm', 'l'}, + /* 85 */ {'T', 'a', 'n', 'g'}, + /* 86 */ {'T', 'a', 'v', 't'}, + /* 87 */ {'T', 'e', 'l', 'u'}, + /* 88 */ {'T', 'f', 'n', 'g'}, + /* 89 */ {'T', 'h', 'a', 'a'}, + /* 90 */ {'T', 'h', 'a', 'i'}, + /* 91 */ {'T', 'i', 'b', 't'}, + /* 92 */ {'U', 'g', 'a', 'r'}, + /* 93 */ {'V', 'a', 'i', 'i'}, + /* 94 */ {'W', 'c', 'h', 'o'}, + /* 95 */ {'X', 'p', 'e', 'o'}, + /* 96 */ {'X', 's', 'u', 'x'}, + /* 97 */ {'Y', 'i', 'i', 'i'}, + /* 98 */ {'~', '~', '~', 'A'}, + /* 99 */ {'~', '~', '~', 'B'}, }; @@ -120,7 +121,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x80600000u, 46u}, // ada -> Latn {0x90600000u, 46u}, // ade -> Latn {0xA4600000u, 46u}, // adj -> Latn - {0xBC600000u, 90u}, // adp -> Tibt + {0xBC600000u, 91u}, // adp -> Tibt {0xE0600000u, 17u}, // ady -> Cyrl {0xE4600000u, 46u}, // adz -> Latn {0x61650000u, 4u}, // ae -> Avst @@ -138,7 +139,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xB8E00000u, 0u}, // aho -> Ahom {0x99200000u, 46u}, // ajg -> Latn {0x616B0000u, 46u}, // ak -> Latn - {0xA9400000u, 95u}, // akk -> Xsux + {0xA9400000u, 96u}, // akk -> Xsux {0x81600000u, 46u}, // ala -> Latn {0xA1600000u, 46u}, // ali -> Latn {0xB5600000u, 46u}, // aln -> Latn @@ -163,7 +164,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xC9E00000u, 46u}, // aps -> Latn {0xE5E00000u, 46u}, // apz -> Latn {0x61720000u, 1u}, // ar -> Arab - {0x61725842u, 98u}, // ar-XB -> ~~~B + {0x61725842u, 99u}, // ar-XB -> ~~~B {0x8A200000u, 2u}, // arc -> Armi {0x9E200000u, 46u}, // arh -> Latn {0xB6200000u, 46u}, // arn -> Latn @@ -174,7 +175,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xE6200000u, 1u}, // arz -> Arab {0x61730000u, 7u}, // as -> Beng {0x82400000u, 46u}, // asa -> Latn - {0x92400000u, 75u}, // ase -> Sgnw + {0x92400000u, 76u}, // ase -> Sgnw {0x9A400000u, 46u}, // asg -> Latn {0xBA400000u, 46u}, // aso -> Latn {0xCE400000u, 46u}, // ast -> Latn @@ -231,7 +232,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xDC810000u, 46u}, // bex -> Latn {0xE4810000u, 46u}, // bez -> Latn {0x8CA10000u, 46u}, // bfd -> Latn - {0xC0A10000u, 83u}, // bfq -> Taml + {0xC0A10000u, 84u}, // bfq -> Taml {0xCCA10000u, 1u}, // bft -> Arab {0xE0A10000u, 18u}, // bfy -> Deva {0x62670000u, 17u}, // bg -> Cyrl @@ -265,7 +266,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xC1410000u, 46u}, // bkq -> Latn {0xD1410000u, 46u}, // bku -> Latn {0xD5410000u, 46u}, // bkv -> Latn - {0xCD610000u, 85u}, // blt -> Tavt + {0xCD610000u, 86u}, // blt -> Tavt {0x626D0000u, 46u}, // bm -> Latn {0x9D810000u, 46u}, // bmh -> Latn {0xA9810000u, 46u}, // bmk -> Latn @@ -275,7 +276,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x99A10000u, 46u}, // bng -> Latn {0xB1A10000u, 46u}, // bnm -> Latn {0xBDA10000u, 46u}, // bnp -> Latn - {0x626F0000u, 90u}, // bo -> Tibt + {0x626F0000u, 91u}, // bo -> Tibt {0xA5C10000u, 46u}, // boj -> Latn {0xB1C10000u, 46u}, // bom -> Latn {0xB5C10000u, 46u}, // bon -> Latn @@ -322,6 +323,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x9F210000u, 46u}, // bzh -> Latn {0xDB210000u, 46u}, // bzw -> Latn {0x63610000u, 46u}, // ca -> Latn + {0x8C020000u, 46u}, // cad -> Latn {0xB4020000u, 46u}, // can -> Latn {0xA4220000u, 46u}, // cbj -> Latn {0x9C420000u, 46u}, // cch -> Latn @@ -346,7 +348,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xE1420000u, 46u}, // cky -> Latn {0x81620000u, 46u}, // cla -> Latn {0x91820000u, 46u}, // cme -> Latn - {0x99820000u, 79u}, // cmg -> Soyo + {0x99820000u, 80u}, // cmg -> Soyo {0x636F0000u, 46u}, // co -> Latn {0xBDC20000u, 15u}, // cop -> Copt {0xC9E20000u, 46u}, // cps -> Latn @@ -360,7 +362,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x63730000u, 46u}, // cs -> Latn {0x86420000u, 46u}, // csb -> Latn {0xDA420000u, 10u}, // csw -> Cans - {0x8E620000u, 66u}, // ctd -> Pauc + {0x8E620000u, 67u}, // ctd -> Pauc {0x63750000u, 17u}, // cu -> Cyrl {0x63760000u, 17u}, // cv -> Cyrl {0x63790000u, 46u}, // cy -> Latn @@ -389,7 +391,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x91230000u, 46u}, // dje -> Latn {0xA5A30000u, 46u}, // dnj -> Latn {0x85C30000u, 46u}, // dob -> Latn - {0xA1C30000u, 1u}, // doi -> Arab + {0xA1C30000u, 18u}, // doi -> Deva {0xBDC30000u, 46u}, // dop -> Latn {0xD9C30000u, 46u}, // dow -> Latn {0x9E230000u, 56u}, // drh -> Mong @@ -404,12 +406,12 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x8A830000u, 46u}, // duc -> Latn {0x8E830000u, 46u}, // dud -> Latn {0x9A830000u, 46u}, // dug -> Latn - {0x64760000u, 88u}, // dv -> Thaa + {0x64760000u, 89u}, // dv -> Thaa {0x82A30000u, 46u}, // dva -> Latn {0xDAC30000u, 46u}, // dww -> Latn {0xBB030000u, 46u}, // dyo -> Latn {0xD3030000u, 46u}, // dyu -> Latn - {0x647A0000u, 90u}, // dz -> Tibt + {0x647A0000u, 91u}, // dz -> Tibt {0x9B230000u, 46u}, // dzg -> Latn {0xD0240000u, 46u}, // ebu -> Latn {0x65650000u, 46u}, // ee -> Latn @@ -422,7 +424,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x81840000u, 46u}, // ema -> Latn {0xA1840000u, 46u}, // emi -> Latn {0x656E0000u, 46u}, // en -> Latn - {0x656E5841u, 97u}, // en-XA -> ~~~A + {0x656E5841u, 98u}, // en-XA -> ~~~A {0xB5A40000u, 46u}, // enn -> Latn {0xC1A40000u, 46u}, // enq -> Latn {0x656F0000u, 46u}, // eo -> Latn @@ -438,6 +440,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x65750000u, 46u}, // eu -> Latn {0xBAC40000u, 46u}, // ewo -> Latn {0xCEE40000u, 46u}, // ext -> Latn + {0x83240000u, 46u}, // eza -> Latn {0x66610000u, 1u}, // fa -> Arab {0x80050000u, 46u}, // faa -> Latn {0x84050000u, 46u}, // fab -> Latn @@ -521,7 +524,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x95C60000u, 20u}, // gof -> Ethi {0xA1C60000u, 46u}, // goi -> Latn {0xB1C60000u, 18u}, // gom -> Deva - {0xB5C60000u, 86u}, // gon -> Telu + {0xB5C60000u, 87u}, // gon -> Telu {0xC5C60000u, 46u}, // gor -> Latn {0xC9C60000u, 46u}, // gos -> Latn {0xCDC60000u, 24u}, // got -> Goth @@ -566,7 +569,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xAD070000u, 46u}, // hil -> Latn {0x81670000u, 46u}, // hla -> Latn {0xD1670000u, 32u}, // hlu -> Hluw - {0x8D870000u, 69u}, // hmd -> Plrd + {0x8D870000u, 70u}, // hmd -> Plrd {0xCD870000u, 46u}, // hmt -> Latn {0x8DA70000u, 1u}, // hnd -> Arab {0x91A70000u, 18u}, // hne -> Deva @@ -601,7 +604,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x69670000u, 46u}, // ig -> Latn {0x84C80000u, 46u}, // igb -> Latn {0x90C80000u, 46u}, // ige -> Latn - {0x69690000u, 96u}, // ii -> Yiii + {0x69690000u, 97u}, // ii -> Yiii {0xA5280000u, 46u}, // ijj -> Latn {0x696B0000u, 46u}, // ik -> Latn {0xA9480000u, 46u}, // ikk -> Latn @@ -626,6 +629,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x6A610000u, 36u}, // ja -> Jpan {0x84090000u, 46u}, // jab -> Latn {0xB0090000u, 46u}, // jam -> Latn + {0xC4090000u, 46u}, // jar -> Latn {0xB8290000u, 46u}, // jbo -> Latn {0xD0290000u, 46u}, // jbu -> Latn {0xB4890000u, 46u}, // jen -> Latn @@ -661,7 +665,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x906A0000u, 46u}, // kde -> Latn {0x9C6A0000u, 1u}, // kdh -> Arab {0xAC6A0000u, 46u}, // kdl -> Latn - {0xCC6A0000u, 89u}, // kdt -> Thai + {0xCC6A0000u, 90u}, // kdt -> Thai {0x808A0000u, 46u}, // kea -> Latn {0xB48A0000u, 46u}, // ken -> Latn {0xE48A0000u, 46u}, // kez -> Latn @@ -673,7 +677,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x94CA0000u, 46u}, // kgf -> Latn {0xBCCA0000u, 46u}, // kgp -> Latn {0x80EA0000u, 46u}, // kha -> Latn - {0x84EA0000u, 82u}, // khb -> Talu + {0x84EA0000u, 83u}, // khb -> Talu {0xB4EA0000u, 18u}, // khn -> Deva {0xC0EA0000u, 46u}, // khq -> Latn {0xC8EA0000u, 46u}, // khs -> Latn @@ -766,7 +770,8 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x82EA0000u, 46u}, // kxa -> Latn {0x8AEA0000u, 20u}, // kxc -> Ethi {0x92EA0000u, 46u}, // kxe -> Latn - {0xB2EA0000u, 89u}, // kxm -> Thai + {0xAEEA0000u, 18u}, // kxl -> Deva + {0xB2EA0000u, 90u}, // kxm -> Thai {0xBEEA0000u, 1u}, // kxp -> Arab {0xDAEA0000u, 46u}, // kxw -> Latn {0xE6EA0000u, 46u}, // kxz -> Latn @@ -775,6 +780,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x6B795452u, 46u}, // ky-TR -> Latn {0x930A0000u, 46u}, // kye -> Latn {0xDF0A0000u, 46u}, // kyx -> Latn + {0x9F2A0000u, 1u}, // kzh -> Arab {0xA72A0000u, 46u}, // kzj -> Latn {0xC72A0000u, 46u}, // kzr -> Latn {0xCF2A0000u, 46u}, // kzt -> Latn @@ -790,7 +796,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xD02B0000u, 46u}, // lbu -> Latn {0xD82B0000u, 46u}, // lbw -> Latn {0xB04B0000u, 46u}, // lcm -> Latn - {0xBC4B0000u, 89u}, // lcp -> Thai + {0xBC4B0000u, 90u}, // lcp -> Thai {0x846B0000u, 46u}, // ldb -> Latn {0x8C8B0000u, 46u}, // led -> Latn {0x908B0000u, 46u}, // lee -> Latn @@ -814,7 +820,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xCD4B0000u, 46u}, // lkt -> Latn {0x916B0000u, 46u}, // lle -> Latn {0xB56B0000u, 46u}, // lln -> Latn - {0xB58B0000u, 86u}, // lmn -> Telu + {0xB58B0000u, 87u}, // lmn -> Telu {0xB98B0000u, 46u}, // lmo -> Latn {0xBD8B0000u, 46u}, // lmp -> Latn {0x6C6E0000u, 46u}, // ln -> Latn @@ -836,7 +842,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xE28B0000u, 46u}, // luy -> Latn {0xE68B0000u, 1u}, // luz -> Arab {0x6C760000u, 46u}, // lv -> Latn - {0xAECB0000u, 89u}, // lwl -> Thai + {0xAECB0000u, 90u}, // lwl -> Thai {0x9F2B0000u, 28u}, // lzh -> Hans {0xE72B0000u, 46u}, // lzz -> Latn {0x8C0C0000u, 46u}, // mad -> Latn @@ -927,7 +933,6 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xBA2C0000u, 57u}, // mro -> Mroo {0x6D730000u, 46u}, // ms -> Latn {0x6D734343u, 1u}, // ms-CC -> Arab - {0x6D734944u, 1u}, // ms-ID -> Arab {0x6D740000u, 46u}, // mt -> Latn {0x8A6C0000u, 46u}, // mtc -> Latn {0x966C0000u, 46u}, // mtf -> Latn @@ -1006,11 +1011,11 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x9DAD0000u, 46u}, // nnh -> Latn {0xA9AD0000u, 46u}, // nnk -> Latn {0xB1AD0000u, 46u}, // nnm -> Latn - {0xBDAD0000u, 93u}, // nnp -> Wcho + {0xBDAD0000u, 94u}, // nnp -> Wcho {0x6E6F0000u, 46u}, // no -> Latn {0x8DCD0000u, 44u}, // nod -> Lana {0x91CD0000u, 18u}, // noe -> Deva - {0xB5CD0000u, 71u}, // non -> Runr + {0xB5CD0000u, 72u}, // non -> Runr {0xBDCD0000u, 46u}, // nop -> Latn {0xD1CD0000u, 46u}, // nou -> Latn {0xBA0D0000u, 60u}, // nqo -> Nkoo @@ -1044,18 +1049,18 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xB5AE0000u, 46u}, // onn -> Latn {0xC9AE0000u, 46u}, // ons -> Latn {0xB1EE0000u, 46u}, // opm -> Latn - {0x6F720000u, 64u}, // or -> Orya + {0x6F720000u, 65u}, // or -> Orya {0xBA2E0000u, 46u}, // oro -> Latn {0xD22E0000u, 1u}, // oru -> Arab {0x6F730000u, 17u}, // os -> Cyrl - {0x824E0000u, 65u}, // osa -> Osge + {0x824E0000u, 66u}, // osa -> Osge {0x826E0000u, 1u}, // ota -> Arab - {0xAA6E0000u, 63u}, // otk -> Orkh + {0xAA6E0000u, 64u}, // otk -> Orkh {0xB32E0000u, 46u}, // ozm -> Latn {0x70610000u, 27u}, // pa -> Guru {0x7061504Bu, 1u}, // pa-PK -> Arab {0x980F0000u, 46u}, // pag -> Latn - {0xAC0F0000u, 67u}, // pal -> Phli + {0xAC0F0000u, 68u}, // pal -> Phli {0xB00F0000u, 46u}, // pam -> Latn {0xBC0F0000u, 46u}, // pap -> Latn {0xD00F0000u, 46u}, // pau -> Latn @@ -1065,11 +1070,11 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x886F0000u, 46u}, // pdc -> Latn {0xCC6F0000u, 46u}, // pdt -> Latn {0x8C8F0000u, 46u}, // ped -> Latn - {0xB88F0000u, 94u}, // peo -> Xpeo + {0xB88F0000u, 95u}, // peo -> Xpeo {0xDC8F0000u, 46u}, // pex -> Latn {0xACAF0000u, 46u}, // pfl -> Latn {0xACEF0000u, 1u}, // phl -> Arab - {0xB4EF0000u, 68u}, // phn -> Phnx + {0xB4EF0000u, 69u}, // phn -> Phnx {0xAD0F0000u, 46u}, // pil -> Latn {0xBD0F0000u, 46u}, // pip -> Latn {0x814F0000u, 8u}, // pka -> Brah @@ -1105,7 +1110,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xB4D10000u, 46u}, // rgn -> Latn {0x98F10000u, 1u}, // rhg -> Arab {0x81110000u, 46u}, // ria -> Latn - {0x95110000u, 87u}, // rif -> Tfng + {0x95110000u, 88u}, // rif -> Tfng {0x95114E4Cu, 46u}, // rif-NL -> Latn {0xC9310000u, 18u}, // rjs -> Deva {0xCD510000u, 7u}, // rkt -> Beng @@ -1135,9 +1140,9 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x9C120000u, 17u}, // sah -> Cyrl {0xC0120000u, 46u}, // saq -> Latn {0xC8120000u, 46u}, // sas -> Latn - {0xCC120000u, 46u}, // sat -> Latn + {0xCC120000u, 63u}, // sat -> Olck {0xD4120000u, 46u}, // sav -> Latn - {0xE4120000u, 74u}, // saz -> Saur + {0xE4120000u, 75u}, // saz -> Saur {0x80320000u, 46u}, // sba -> Latn {0x90320000u, 46u}, // sbe -> Latn {0xBC320000u, 46u}, // sbp -> Latn @@ -1161,11 +1166,11 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xD8D20000u, 20u}, // sgw -> Ethi {0xE4D20000u, 46u}, // sgz -> Latn {0x73680000u, 46u}, // sh -> Latn - {0xA0F20000u, 87u}, // shi -> Tfng + {0xA0F20000u, 88u}, // shi -> Tfng {0xA8F20000u, 46u}, // shk -> Latn {0xB4F20000u, 58u}, // shn -> Mymr {0xD0F20000u, 1u}, // shu -> Arab - {0x73690000u, 76u}, // si -> Sinh + {0x73690000u, 77u}, // si -> Sinh {0x8D120000u, 46u}, // sid -> Latn {0x99120000u, 46u}, // sig -> Latn {0xAD120000u, 46u}, // sil -> Latn @@ -1184,7 +1189,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x81920000u, 46u}, // sma -> Latn {0xA5920000u, 46u}, // smj -> Latn {0xB5920000u, 46u}, // smn -> Latn - {0xBD920000u, 72u}, // smp -> Samr + {0xBD920000u, 73u}, // smp -> Samr {0xC1920000u, 46u}, // smq -> Latn {0xC9920000u, 46u}, // sms -> Latn {0x736E0000u, 46u}, // sn -> Latn @@ -1194,10 +1199,10 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xDDB20000u, 46u}, // snx -> Latn {0xE1B20000u, 46u}, // sny -> Latn {0x736F0000u, 46u}, // so -> Latn - {0x99D20000u, 77u}, // sog -> Sogd + {0x99D20000u, 78u}, // sog -> Sogd {0xA9D20000u, 46u}, // sok -> Latn {0xC1D20000u, 46u}, // soq -> Latn - {0xD1D20000u, 89u}, // sou -> Thai + {0xD1D20000u, 90u}, // sou -> Thai {0xE1D20000u, 46u}, // soy -> Latn {0x8DF20000u, 46u}, // spd -> Latn {0xADF20000u, 46u}, // spl -> Latn @@ -1208,7 +1213,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x7372524Fu, 46u}, // sr-RO -> Latn {0x73725255u, 46u}, // sr-RU -> Latn {0x73725452u, 46u}, // sr-TR -> Latn - {0x86320000u, 78u}, // srb -> Sora + {0x86320000u, 79u}, // srb -> Sora {0xB6320000u, 46u}, // srn -> Latn {0xC6320000u, 46u}, // srr -> Latn {0xDE320000u, 18u}, // srx -> Deva @@ -1235,9 +1240,9 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xB6F20000u, 46u}, // sxn -> Latn {0xDAF20000u, 46u}, // sxw -> Latn {0xAF120000u, 7u}, // syl -> Beng - {0xC7120000u, 80u}, // syr -> Syrc + {0xC7120000u, 81u}, // syr -> Syrc {0xAF320000u, 46u}, // szl -> Latn - {0x74610000u, 83u}, // ta -> Taml + {0x74610000u, 84u}, // ta -> Taml {0xA4130000u, 18u}, // taj -> Deva {0xAC130000u, 46u}, // tal -> Latn {0xB4130000u, 46u}, // tan -> Latn @@ -1251,11 +1256,11 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xE4330000u, 46u}, // tbz -> Latn {0xA0530000u, 46u}, // tci -> Latn {0xE0530000u, 42u}, // tcy -> Knda - {0x8C730000u, 81u}, // tdd -> Tale + {0x8C730000u, 82u}, // tdd -> Tale {0x98730000u, 18u}, // tdg -> Deva {0x9C730000u, 18u}, // tdh -> Deva {0xD0730000u, 46u}, // tdu -> Latn - {0x74650000u, 86u}, // te -> Telu + {0x74650000u, 87u}, // te -> Telu {0x8C930000u, 46u}, // ted -> Latn {0xB0930000u, 46u}, // tem -> Latn {0xB8930000u, 46u}, // teo -> Latn @@ -1266,7 +1271,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x88D30000u, 46u}, // tgc -> Latn {0xB8D30000u, 46u}, // tgo -> Latn {0xD0D30000u, 46u}, // tgu -> Latn - {0x74680000u, 89u}, // th -> Thai + {0x74680000u, 90u}, // th -> Thai {0xACF30000u, 18u}, // thl -> Deva {0xC0F30000u, 18u}, // thq -> Deva {0xC4F30000u, 18u}, // thr -> Deva @@ -1305,14 +1310,14 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x8E530000u, 25u}, // tsd -> Grek {0x96530000u, 18u}, // tsf -> Deva {0x9A530000u, 46u}, // tsg -> Latn - {0xA6530000u, 90u}, // tsj -> Tibt + {0xA6530000u, 91u}, // tsj -> Tibt {0xDA530000u, 46u}, // tsw -> Latn {0x74740000u, 17u}, // tt -> Cyrl {0x8E730000u, 46u}, // ttd -> Latn {0x92730000u, 46u}, // tte -> Latn {0xA6730000u, 46u}, // ttj -> Latn {0xC6730000u, 46u}, // ttr -> Latn - {0xCA730000u, 89u}, // tts -> Thai + {0xCA730000u, 90u}, // tts -> Thai {0xCE730000u, 46u}, // ttt -> Latn {0x9E930000u, 46u}, // tuh -> Latn {0xAE930000u, 46u}, // tul -> Latn @@ -1323,7 +1328,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xD2B30000u, 46u}, // tvu -> Latn {0x9ED30000u, 46u}, // twh -> Latn {0xC2D30000u, 46u}, // twq -> Latn - {0x9AF30000u, 84u}, // txg -> Tang + {0x9AF30000u, 85u}, // txg -> Tang {0x74790000u, 46u}, // ty -> Latn {0x83130000u, 46u}, // tya -> Latn {0xD7130000u, 17u}, // tyv -> Cyrl @@ -1333,7 +1338,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x75670000u, 1u}, // ug -> Arab {0x75674B5Au, 17u}, // ug-KZ -> Cyrl {0x75674D4Eu, 17u}, // ug-MN -> Cyrl - {0x80D40000u, 91u}, // uga -> Ugar + {0x80D40000u, 92u}, // uga -> Ugar {0x756B0000u, 17u}, // uk -> Cyrl {0xA1740000u, 46u}, // uli -> Latn {0x85940000u, 46u}, // umb -> Latn @@ -1346,6 +1351,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xCE340000u, 46u}, // urt -> Latn {0xDA340000u, 46u}, // urw -> Latn {0x82540000u, 46u}, // usa -> Latn + {0x9E740000u, 46u}, // uth -> Latn {0xC6740000u, 46u}, // utr -> Latn {0x9EB40000u, 46u}, // uvh -> Latn {0xAEB40000u, 46u}, // uvl -> Latn @@ -1353,7 +1359,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x757A4146u, 1u}, // uz-AF -> Arab {0x757A434Eu, 17u}, // uz-CN -> Cyrl {0x98150000u, 46u}, // vag -> Latn - {0xA0150000u, 92u}, // vai -> Vaii + {0xA0150000u, 93u}, // vai -> Vaii {0xB4150000u, 46u}, // van -> Latn {0x76650000u, 46u}, // ve -> Latn {0x88950000u, 46u}, // vec -> Latn @@ -1376,7 +1382,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xB4160000u, 46u}, // wan -> Latn {0xC4160000u, 46u}, // war -> Latn {0xBC360000u, 46u}, // wbp -> Latn - {0xC0360000u, 86u}, // wbq -> Telu + {0xC0360000u, 87u}, // wbq -> Telu {0xC4360000u, 18u}, // wbr -> Deva {0xA0560000u, 46u}, // wci -> Latn {0xC4960000u, 46u}, // wer -> Latn @@ -1418,9 +1424,9 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xC5B70000u, 18u}, // xnr -> Deva {0x99D70000u, 46u}, // xog -> Latn {0xB5D70000u, 46u}, // xon -> Latn - {0xC5F70000u, 70u}, // xpr -> Prti + {0xC5F70000u, 71u}, // xpr -> Prti {0x86370000u, 46u}, // xrb -> Latn - {0x82570000u, 73u}, // xsa -> Sarb + {0x82570000u, 74u}, // xsa -> Sarb {0xA2570000u, 46u}, // xsi -> Latn {0xB2570000u, 46u}, // xsm -> Latn {0xC6570000u, 18u}, // xsr -> Deva @@ -1461,7 +1467,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x98190000u, 46u}, // zag -> Latn {0xA4790000u, 1u}, // zdj -> Arab {0x80990000u, 46u}, // zea -> Latn - {0x9CD90000u, 87u}, // zgh -> Tfng + {0x9CD90000u, 88u}, // zgh -> Tfng {0x7A680000u, 28u}, // zh -> Hans {0x7A684155u, 29u}, // zh-AU -> Hant {0x7A68424Eu, 29u}, // zh-BN -> Hant @@ -1470,7 +1476,6 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x7A68484Bu, 29u}, // zh-HK -> Hant {0x7A684944u, 29u}, // zh-ID -> Hant {0x7A684D4Fu, 29u}, // zh-MO -> Hant - {0x7A684D59u, 29u}, // zh-MY -> Hant {0x7A685041u, 29u}, // zh-PA -> Hant {0x7A685046u, 29u}, // zh-PF -> Hant {0x7A685048u, 29u}, // zh-PH -> Hant @@ -1592,6 +1597,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0xD701434D4C61746ELLU, // byv_Latn_CM 0x93214D4C4C61746ELLU, // bze_Latn_ML 0x636145534C61746ELLU, // ca_Latn_ES + 0x8C0255534C61746ELLU, // cad_Latn_US 0x9C424E474C61746ELLU, // cch_Latn_NG 0xBC42424443616B6DLLU, // ccp_Cakm_BD 0x636552554379726CLLU, // ce_Cyrl_RU @@ -1627,6 +1633,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0x637652554379726CLLU, // cv_Cyrl_RU 0x637947424C61746ELLU, // cy_Latn_GB 0x6461444B4C61746ELLU, // da_Latn_DK + 0x940343494C61746ELLU, // daf_Latn_CI 0xA80355534C61746ELLU, // dak_Latn_US 0xC40352554379726CLLU, // dar_Cyrl_RU 0xD4034B454C61746ELLU, // dav_Latn_KE @@ -1636,7 +1643,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0xC4C343414C61746ELLU, // dgr_Latn_CA 0x91234E454C61746ELLU, // dje_Latn_NE 0xA5A343494C61746ELLU, // dnj_Latn_CI - 0xA1C3494E41726162LLU, // doi_Arab_IN + 0xA1C3494E44657661LLU, // doi_Deva_IN 0x9E23434E4D6F6E67LLU, // drh_Mong_CN 0x864344454C61746ELLU, // dsb_Latn_DE 0xB2634D4C4C61746ELLU, // dtm_Latn_ML @@ -1839,6 +1846,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0xC6AA49444C61746ELLU, // kvr_Latn_ID 0xDEAA504B41726162LLU, // kvx_Arab_PK 0x6B7747424C61746ELLU, // kw_Latn_GB + 0xAEEA494E44657661LLU, // kxl_Deva_IN 0xB2EA544854686169LLU, // kxm_Thai_TH 0xBEEA504B41726162LLU, // kxp_Arab_PK 0x6B79434E41726162LLU, // ky_Arab_CN @@ -2047,7 +2055,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0x9C1252554379726CLLU, // sah_Cyrl_RU 0xC0124B454C61746ELLU, // saq_Latn_KE 0xC81249444C61746ELLU, // sas_Latn_ID - 0xCC12494E4C61746ELLU, // sat_Latn_IN + 0xCC12494E4F6C636BLLU, // sat_Olck_IN 0xD412534E4C61746ELLU, // sav_Latn_SN 0xE412494E53617572LLU, // saz_Saur_IN 0xBC32545A4C61746ELLU, // sbp_Latn_TZ @@ -2149,6 +2157,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0x747254524C61746ELLU, // tr_Latn_TR 0xD23354524C61746ELLU, // tru_Latn_TR 0xD63354574C61746ELLU, // trv_Latn_TW + 0xDA33504B41726162LLU, // trw_Arab_PK 0x74735A414C61746ELLU, // ts_Latn_ZA 0x8E5347524772656BLLU, // tsd_Grek_GR 0x96534E5044657661LLU, // tsf_Deva_NP diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS index 8cffd6a3e548..bc056df23a36 100644 --- a/libs/androidfw/OWNERS +++ b/libs/androidfw/OWNERS @@ -3,4 +3,4 @@ toddke@google.com rtmitchell@google.com per-file CursorWindow.cpp=omakoto@google.com -per-file LocaleDataTables.cpp=vichang@google.com,tobiast@google.com,nikitai@google.com +per-file LocaleDataTables.cpp=vichang@google.com,ngeoffray@google.com,nikitai@google.com diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 4d7e5dfea4f7..223382731bc0 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -30,6 +30,7 @@ #include <memory> #include <set> #include <type_traits> +#include <vector> #include <android-base/macros.h> #include <androidfw/ByteBucketArray.h> @@ -37,7 +38,6 @@ #include <androidfw/TypeWrappers.h> #include <cutils/atomic.h> #include <utils/ByteOrder.h> -#include <utils/Debug.h> #include <utils/Log.h> #include <utils/String16.h> #include <utils/String8.h> @@ -105,22 +105,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<ResChunk_header>& chunk, size_t minSize, - const uint8_t* dataEnd, + const incfs::map_ptr<uint8_t> 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<uint8_t>())) { 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<uint8_t>())); return BAD_TYPE; } ALOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.", @@ -451,7 +455,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<void> data, size_t size, bool copyData) { if (!data || !size) { return (mError=BAD_TYPE); @@ -468,8 +472,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<const ResChunk_header*>(data), sizeof(ResStringPool_header), - reinterpret_cast<const uint8_t*>(data) + size, + const auto chunk_header = data.convert<ResChunk_header>(); + if (validate_chunk(chunk_header, sizeof(ResStringPool_header), data.convert<uint8_t>() + size, "ResStringPool_header") != NO_ERROR) { ALOGW("Bad string block: malformed block dimensions"); return (mError=BAD_TYPE); @@ -482,16 +486,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<uint8_t>().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<ResStringPool_header>(); + if (!header) { + return (mError=BAD_TYPE); + } + mHeader = header.verified(); if (notDeviceEndian) { - ResStringPool_header* h = const_cast<ResStringPool_header*>(mHeader); + ResStringPool_header* h = const_cast<ResStringPool_header*>(mHeader.unsafe_ptr()); h->header.headerSize = dtohs(mHeader->header.headerSize); h->header.type = dtohs(mHeader->header.type); h->header.size = dtohl(mHeader->header.size); @@ -509,8 +522,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<uint32_t>(); if (mHeader->stringCount > 0) { if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow? @@ -537,9 +549,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<void>(); if (mHeader->styleCount == 0) { mStringPoolSize = (mSize - mHeader->stringsStart) / charSize; } else { @@ -561,31 +571,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<uint32_t*>(mEntries); + auto e = const_cast<uint32_t*>(mEntries.unsafe_ptr()); for (i=0; i<mHeader->stringCount; 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<uint16_t*>(strings); + uint16_t* s = const_cast<uint16_t*>(mStrings.convert<uint16_t>().unsafe_ptr()); for (i=0; i<mStringPoolSize; i++) { - s[i] = dtohs(strings[i]); + s[i] = dtohs(s[i]); } } } - if ((mHeader->flags&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<uint8_t>() + (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<uint16_t>() + (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; @@ -600,14 +616,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<uint8_t>() - mHeader.convert<uint8_t>()) > (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<uint8_t>()-mHeader.convert<uint8_t>()), (int)size); return (mError=BAD_TYPE); } - mStyles = (const uint32_t*) - (((const uint8_t*)data)+mHeader->stylesStart); + mStyles = data.offset(mHeader->stylesStart).convert<uint32_t>(); 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); @@ -618,13 +633,13 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) if (notDeviceEndian) { size_t i; - uint32_t* e = const_cast<uint32_t*>(mEntryStyles); + uint32_t* e = const_cast<uint32_t*>(mEntryStyles.unsafe_ptr()); for (i=0; i<mHeader->styleCount; i++) { - e[i] = dtohl(mEntryStyles[i]); + e[i] = dtohl(e[i]); } - uint32_t* s = const_cast<uint32_t*>(mStyles); + uint32_t* s = const_cast<uint32_t*>(mStyles.unsafe_ptr()); for (i=0; i<mStylePoolSize; i++) { - s[i] = dtohl(mStyles[i]); + s[i] = dtohl(s[i]); } } @@ -632,8 +647,9 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) { htodl(ResStringPool_span::END) }, htodl(ResStringPool_span::END), htodl(ResStringPool_span::END) }; - if (memcmp(&mStyles[mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t))], - &endSpan, sizeof(endSpan)) != 0) { + + auto stylesEnd = mStyles + (mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t))); + if (!stylesEnd || memcmp(stylesEnd.unsafe_ptr(), &endSpan, sizeof(endSpan)) != 0) { ALOGW("Bad string block: last style is not 0xFFFFFFFF-terminated\n"); return (mError=BAD_TYPE); } @@ -654,7 +670,7 @@ status_t ResStringPool::getError() const void ResStringPool::uninit() { mError = NO_INIT; - if (mHeader != NULL && mCache != NULL) { + if (mHeader && mCache != NULL) { for (size_t x = 0; x < mHeader->stringCount; x++) { if (mCache[x] != NULL) { free(mCache[x]); @@ -680,15 +696,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<size_t, IOError> decodeLength(incfs::map_ptr<uint16_t>* 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; } @@ -702,82 +724,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<size_t, IOError> decodeLength(incfs::map_ptr<uint8_t>* 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<StringPiece16, NullOrIOError> 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<uint16_t>(); + auto str = strings+off; + + const base::expected<size_t, IOError> 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<const char16_t*>(str); + + return StringPiece16(reinterpret_cast<const char16_t*>(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<uint8_t>(); + auto u8str = strings+off; - *u16len = decodeLength(&u8str); - size_t u8len = decodeLength(&u8str); + base::expected<size_t, IOError> u16len = decodeLength(&u8str); + if (UNLIKELY(!u16len.has_value())) { + return base::unexpected(u16len.error()); + } + + const base::expected<size_t, IOError> 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<const uint8_t*>(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<const uint8_t*>(decodedString->data()), + decodedString->size(), u16str, *u16len + 1); if (mCache == NULL) { #ifndef __ANDROID__ @@ -794,19 +853,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); } } @@ -816,33 +875,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<StringPiece, NullOrIOError> 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<uint8_t>(); + 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<size_t, IOError> u16len = decodeLength(&str); + if (UNLIKELY(!u16len.has_value())) { + return base::unexpected(u16len.error()); + } - if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { - return stringDecodeAt(idx, str, encLen, outLen); + const base::expected<size_t, IOError> u8len = decodeLength(&str); + if (UNLIKELY(!u8len.has_value())) { + 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", @@ -850,7 +919,7 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const (int)(mStringPoolSize*sizeof(uint16_t))); } } - return NULL; + return base::unexpected(std::nullopt); } /** @@ -860,74 +929,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<StringPiece, NullOrIOError> ResStringPool::stringDecodeAt( + size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const +{ + const auto strings = mStrings.convert<uint8_t>(); 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<String8, IOError> 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<StringPiece, NullOrIOError> 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<StringPiece16, NullOrIOError> 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<incfs::map_ptr<ResStringPool_span>, NullOrIOError> ResStringPool::styleAt( + const ResStringPool_ref& ref) const { return styleAt(ref.index); } -const ResStringPool_span* ResStringPool::styleAt(size_t idx) const +base::expected<incfs::map_ptr<ResStringPool_span>, 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<ResStringPool_span>(); } 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<size_t, NullOrIOError> 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()); @@ -942,30 +1030,31 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const // But we don't want to hit the cache, so instead we will have a // local temporary allocation for the conversions. size_t convBufferLen = strLen + 4; - char16_t* convBuffer = (char16_t*)calloc(convBufferLen, sizeof(char16_t)); + std::vector<char16_t> convBuffer(convBufferLen); ssize_t l = 0; ssize_t h = mHeader->stringCount-1; 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); - c = strzcmp16(convBuffer, end-convBuffer, str, strLen); - } else { - c = -1; + int c = -1; + const base::expected<StringPiece, NullOrIOError> s = string8At(mid); + if (UNLIKELY(IsIOError(s))) { + return base::unexpected(s.error()); + } + if (s.has_value()) { + char16_t* end = utf8_to_utf16(reinterpret_cast<const uint8_t*>(s->data()), + s->size(), convBuffer.data(), convBufferLen); + c = strzcmp16(convBuffer.data(), end-convBuffer.data(), str, strLen); } 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) { ALOGI("MATCH!"); } - free(convBuffer); return mid; } else if (c < 0) { l = mid + 1; @@ -973,7 +1062,6 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const h = mid - 1; } } - free(convBuffer); } else { // It is unusual to get the ID from an unsorted string block... // most often this happens because we want to get IDs for style @@ -982,15 +1070,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<StringPiece, NullOrIOError> 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; } } } @@ -1008,11 +1102,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<StringPiece16, NullOrIOError> 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) { @@ -1031,11 +1128,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<StringPiece16, NullOrIOError> 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!"); } @@ -1044,8 +1145,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 @@ -1063,9 +1163,10 @@ size_t ResStringPool::bytes() const return (mError == NO_ERROR) ? mHeader->header.size : 0; } -const void* ResStringPool::data() const +incfs::map_ptr<void> ResStringPool::data() const { - return mHeader; + + return mHeader.unsafe_ptr(); } bool ResStringPool::isSorted() const @@ -1122,7 +1223,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 @@ -1141,7 +1242,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 @@ -1165,7 +1266,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 @@ -1180,7 +1281,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 @@ -1197,7 +1298,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 @@ -1214,7 +1315,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 @@ -1247,7 +1348,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 @@ -1257,7 +1358,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 @@ -1282,7 +1383,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 @@ -1292,7 +1393,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 @@ -1329,7 +1430,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 @@ -3597,9 +3698,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<size_t, NullOrIOError> index = + packages[i]->typeStrings.indexOfString(type, len); + if (index.has_value()) { + return *index + packages[i]->typeIdOffset; } } return -1; @@ -4305,21 +4407,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; @@ -4407,7 +4509,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); } @@ -4463,7 +4566,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; @@ -4979,15 +5083,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) { @@ -5010,8 +5112,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<size_t, NullOrIOError> ei = + t->package->keyStrings.indexOfString(name, nameLen); + if (!ei.has_value()) { continue; } @@ -5026,7 +5129,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; @@ -6188,8 +6291,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<String8, NullOrIOError> typeStr = typeStrings.string8ObjectAt( + type->typeSpec->id - 1); + if (ignoreMipmap && typeStr.has_value() && *typeStr == "mipmap") { continue; } @@ -6245,24 +6349,18 @@ void ResTable::getLocales(Vector<String8>* 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<StringPiece, NullOrIOError> 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<StringPiece16, NullOrIOError> 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 { @@ -7381,13 +7479,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..87fb2c038c9f 100644 --- a/libs/androidfw/ResourceUtils.cpp +++ b/libs/androidfw/ResourceUtils.cpp @@ -48,61 +48,80 @@ 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<AssetManager2::ResourceName, NullOrIOError> 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<StringPiece, NullOrIOError> type_str = type_string_ref.string8(); + type_str.ok()) { + 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<StringPiece16, NullOrIOError> type16_str = type_string_ref.string16(); + type16_str.ok()) { + 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<StringPiece, NullOrIOError> entry_str = entry_string_ref.string8(); + entry_str.ok()) { + 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<StringPiece16, NullOrIOError> entry16_str = entry_string_ref.string16(); + entry16_str.ok()) { + 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/TEST_MAPPING b/libs/androidfw/TEST_MAPPING index 777aa0b429e5..766714cd7694 100644 --- a/libs/androidfw/TEST_MAPPING +++ b/libs/androidfw/TEST_MAPPING @@ -1,10 +1,6 @@ { "presubmit": [ { - "name": "libandroidfw_tests", - "host": true - }, - { "name": "CtsResourcesLoaderTests" } ] 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 @@ -233,6 +233,29 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const } /* + * Create a new incfs::IncFsFileMap object that spans the data in "entry". + */ +std::optional<incfs::IncFsFileMap> 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. * * This doesn't verify the data's CRC, which might be useful for diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp index 5be2105fe404..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, uint32_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. @@ -63,7 +63,7 @@ class FileReader : public zip_archive::Reader { private: FILE* mFp; - mutable uint32_t mCurrentOffset; + mutable off64_t mCurrentOffset; }; class FdReader : public zip_archive::Reader { @@ -71,8 +71,8 @@ class FdReader : public zip_archive::Reader { explicit FdReader(int fd) : mFd(fd) { } - bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const { - return android::base::ReadFullyAtOffset(mFd, buf, len, static_cast<off_t>(offset)); + bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override { + return android::base::ReadFullyAtOffset(mFd, buf, len, offset); } private: @@ -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<const uint8_t*>(input)), + BufferReader(incfs::map_ptr<void> input, size_t inputSize) : Reader(), + mInput(input.convert<uint8_t>()), mInputSize(inputSize) { } - bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const { - if (offset + len > mInputSize) { + 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<uint8_t> 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<uint8_t> 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<void> in, void* buf, long uncompressedLen, long compressedLen) { BufferReader reader(in, compressedLen); diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp new file mode 100644 index 000000000000..b511244c4a30 --- /dev/null +++ b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp @@ -0,0 +1,55 @@ +// 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. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_libs_androidfw_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_libs_androidfw_license"], +} + +cc_fuzz { + name: "resourcefile_fuzzer", + srcs: [ + "resourcefile_fuzzer.cpp", + ], + host_supported: true, + corpus: ["corpus/*"], + static_libs: ["libgmock"], + target: { + android: { + shared_libs:[ + "libandroidfw", + "libbase", + "libcutils", + "libutils", + "libziparchive", + "libui", + ], + }, + host: { + static_libs: [ + "libandroidfw", + "libbase", + "libcutils", + "libutils", + "libziparchive", + "liblog", + "libz", + ], + }, + }, +} diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/corpus/resources.arsc b/libs/androidfw/fuzz/resourcefile_fuzzer/corpus/resources.arsc Binary files differnew file mode 100644 index 000000000000..3cf2ea733d28 --- /dev/null +++ b/libs/androidfw/fuzz/resourcefile_fuzzer/corpus/resources.arsc diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp new file mode 100644 index 000000000000..5309ab2b6e20 --- /dev/null +++ b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <string> +#include <memory> + +#include <androidfw/ApkAssets.h> +#include <androidfw/LoadedArsc.h> +#include <androidfw/StringPiece.h> + +#include <fuzzer/FuzzedDataProvider.h> + +using android::ApkAssets; +using android::LoadedArsc; +using android::StringPiece; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + std::unique_ptr<const LoadedArsc> 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 <stdio.h> #include <sys/types.h> - #include <memory> +#include <optional> #include <android-base/unique_fd.h> +#include <util/map_ptr.h> + #include <utils/Compat.h> #include <utils/Errors.h> #include <utils/String8.h> 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<void> 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<void> 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<Asset> createFromUncompressedMap(std::unique_ptr<FileMap> dataMap, - base::unique_fd fd, AccessMode mode); + static std::unique_ptr<Asset> 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<Asset> createFromCompressedMap(std::unique_ptr<FileMap> dataMap, - size_t uncompressedLen, AccessMode mode); - + static std::unique_ptr<Asset> 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<void> 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<void> 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<incfs::IncFsFileMap> 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<void> 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<incfs::IncFsFileMap> 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..a92694c94b9f 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<std::string, std::string>* - GetOverlayableMapForPackage(uint32_t package_id) const; + const std::unordered_map<std::string, std::string>* 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<ResTable_config> GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false) const; + base::expected<std::set<ResTable_config>, 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,119 @@ class AssetManager2 { std::unique_ptr<Asset> 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<ResourceName, NullOrIOError> 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<uint32_t, NullOrIOError> 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<uint32_t> 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<SelectedValue, NullOrIOError> 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`. 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<std::monostate, NullOrIOError> ResolveReference(SelectedValue& value, + bool cache_value = false) 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<const ResolvedBag*, NullOrIOError> 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<const ResolvedBag*, NullOrIOError> 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<const ResolvedBag*, NullOrIOError> ResolveBag(SelectedValue& value) const; + + const std::vector<uint32_t> 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<Theme> NewTheme(); @@ -286,11 +330,15 @@ class AssetManager2 { private: DISALLOW_COPY_AND_ASSIGN(AssetManager2); + struct TypeConfig { + incfs::verified_map_ptr<ResTable_type> type; + ResTable_config config; + }; + // A collection of configurations and their associated ResTable_type that match the current // AssetManager configuration. struct FilteredConfigGroup { - std::vector<ResTable_config> configurations; - std::vector<const ResTable_type*> types; + std::vector<TypeConfig> type_configs; }; // Represents an single package. @@ -331,9 +379,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 +393,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<FindEntryResult, NullOrIOError> 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<FindEntryResult, NullOrIOError> 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 +420,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<uint32_t>& child_resids); + base::expected<const ResolvedBag*, NullOrIOError> GetBag( + uint32_t resid, std::vector<uint32_t>& child_resids) const; // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. @@ -394,19 +443,20 @@ class AssetManager2 { // Cached set of bags. These are cached because they can inherit keys from parent bags, // which involves some calculation. - std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_; + mutable std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> 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<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_; + mutable std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_; + + // Cached set of resolved resource values. + mutable std::unordered_map<uint32_t, SelectedValue> 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, @@ -455,55 +505,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<std::monostate, NullOrIOError> 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<std::monostate, IOError> 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<AssetManager2::SelectedValue> 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<std::monostate, NullOrIOError> 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<std::monostate, IOError> 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<std::monostate, IOError> 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<std::monostate, IOError> 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<ResChunk_header> 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 <typename T, size_t MinSize = sizeof(T)> - inline const T* header() const { - if (header_size() >= MinSize) { - return reinterpret_cast<const T*>(device_chunk_); - } - return nullptr; + inline incfs::map_ptr<T> header() const { + return (header_size() >= MinSize) ? device_chunk_.convert<T>() : nullptr; } - inline const void* data_ptr() const { - return reinterpret_cast<const uint8_t*>(device_chunk_) + header_size(); + inline incfs::map_ptr<void> 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<ResChunk_header> 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<const ResChunk_header*>(data)), + ChunkIterator(incfs::map_ptr<void> data, size_t len) + : next_chunk_(data.convert<ResChunk_header>()), 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<ResChunk_header> next_chunk_; size_t len_; const char* last_error_; bool last_error_was_fatal_ = true; diff --git a/libs/androidfw/include/androidfw/ConfigDescription.h b/libs/androidfw/include/androidfw/ConfigDescription.h index 6fa089aeb12c..61d10cd4e55b 100644 --- a/libs/androidfw/include/androidfw/ConfigDescription.h +++ b/libs/androidfw/include/androidfw/ConfigDescription.h @@ -151,8 +151,8 @@ inline ConfigDescription::ConfigDescription(const android::ResTable_config& o) { size = sizeof(android::ResTable_config); } -inline ConfigDescription::ConfigDescription(const ConfigDescription& o) { - *static_cast<android::ResTable_config*>(this) = o; +inline ConfigDescription::ConfigDescription(const ConfigDescription& o) + : android::ResTable_config(o) { } inline ConfigDescription::ConfigDescription(ConfigDescription&& o) noexcept { @@ -177,9 +177,8 @@ inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) no return *this; } -inline bool ConfigDescription::MatchWithDensity( - const ConfigDescription& o) const { - return match(o) && (density == 0 || density == o.density); +inline bool ConfigDescription::MatchWithDensity(const ConfigDescription& o) const { + return match(o) && (density == 0 || o.density != 0); } inline bool ConfigDescription::operator<(const ConfigDescription& o) const { 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 <optional> +#include <variant> + +#include <android-base/result.h> + +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<std::nullopt_t, IOError>; + +// Checks whether the result holds an unexpected I/O error. +template <typename T> +static inline bool IsIOError(const base::expected<T, NullOrIOError> result) { + return !result.has_value() && std::holds_alternative<IOError>(result.error()); +} + +static inline IOError GetIOError(const NullOrIOError& error) { + return std::get<IOError>(error); +} + +} // namespace android + +#endif //ANDROIDFW_ERRORS_H_ diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index ecc1ce65d124..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<StringPiece16, NullOrIOError> stringAt(size_t idx) const override; + base::expected<StringPiece, NullOrIOError> string8At(size_t idx) const override; size_t size() const override; explicit OverlayStringPool(const LoadedIdmap* loaded_idmap); @@ -77,40 +77,40 @@ class OverlayDynamicRefTable : public DynamicRefTable { // A mapping of target resource ids to a values or resource ids that should overlay the target. class IdmapResMap { public: - // Represents the result of a idmap lookup. The result can be one of three possibillities: + // Represents the result of a idmap lookup. The result can be one of three possibilities: // 1) The result is a resource id which represents the overlay resource that should act as an // alias of the target resource. // 2) The result is a table entry which overlays the type and value of the target resource. // 3) The result is neither and the target resource is not overlaid. class Result { public: - Result() : data_(nullptr) {}; + Result() = default; explicit Result(uint32_t value) : data_(value) {}; - explicit Result(ResTable_entry_handle&& value) : data_(value) { }; + explicit Result(const Res_value& value) : data_(value) { }; // Returns `true` if the resource is overlaid. - inline explicit operator bool() const { - return !std::get_if<nullptr_t>(&data_); + explicit operator bool() const { + return std::get_if<std::monostate>(&data_) == nullptr; } - inline bool IsResourceId() const { - return std::get_if<uint32_t>(&data_); + bool IsResourceId() const { + return std::get_if<uint32_t>(&data_) != nullptr; } - inline uint32_t GetResourceId() const { - return *std::get_if<uint32_t>(&data_); + uint32_t GetResourceId() const { + return std::get<uint32_t>(data_); } - inline bool IsTableEntry() const { - return std::get_if<ResTable_entry_handle>(&data_); + bool IsInlineValue() const { + return std::get_if<Res_value>(&data_) != nullptr; } - inline const ResTable_entry_handle& GetTableEntry() const { - return *std::get_if<ResTable_entry_handle>(&data_); + const Res_value& GetInlineValue() const { + return std::get<Res_value>(data_); } private: - std::variant<uint32_t, nullptr_t, ResTable_entry_handle> data_; + std::variant<std::monostate, uint32_t, Res_value> data_; }; // Looks up the value that overlays the target resource id. @@ -123,11 +123,13 @@ class IdmapResMap { private: explicit IdmapResMap(const Idmap_data_header* data_header, const Idmap_target_entry* entries, + const Idmap_target_entry_inline* inline_entries, uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table); const Idmap_data_header* data_header_; const Idmap_target_entry* entries_; + const Idmap_target_entry_inline* inline_entries_; const uint8_t target_assigned_package_id_; const OverlayDynamicRefTable* overlay_ref_table_; @@ -163,8 +165,8 @@ class LoadedIdmap { // Returns a mapping from target resource ids to overlay values. inline const IdmapResMap GetTargetResourcesMap( uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const { - return IdmapResMap(data_header_, target_entries_, target_assigned_package_id, - overlay_ref_table); + return IdmapResMap(data_header_, target_entries_, target_inline_entries_, + target_assigned_package_id, overlay_ref_table); } // Returns a dynamic reference table for a loaded overlay package. @@ -184,6 +186,7 @@ class LoadedIdmap { const Idmap_header* header_; const Idmap_data_header* data_header_; const Idmap_target_entry* target_entries_; + const Idmap_target_entry_inline* target_inline_entries_; const Idmap_overlay_entry* overlay_entries_; const std::unique_ptr<ResStringPool> string_pool_; @@ -200,6 +203,7 @@ class LoadedIdmap { const Idmap_header* header, const Idmap_data_header* data_header, const Idmap_target_entry* target_entries, + const Idmap_target_entry_inline* target_inline_entries, const Idmap_overlay_entry* overlay_entries, ResStringPool* string_pool); 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 <unordered_map> #include <unordered_set> -#include "android-base/macros.h" +#include <android-base/macros.h> +#include <android-base/result.h> #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<ResTable_typeSpec> 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<ResTable_type> types[0]; - inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { + base::expected<uint32_t, NullOrIOError> GetFlagsForEntryIndex(uint16_t entry_index) const { if (entry_index >= dtohl(type_spec->entryCount)) { - return 0u; + return 0U; } - - const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec + 1); - return flags[entry_index]; + const auto entry_flags_ptr = ((type_spec + 1).convert<uint32_t>() + 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<uint32_t, NullOrIOError> 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<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntry( + incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index); - static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); + static base::expected<uint32_t, NullOrIOError> GetEntryOffset( + incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index); - static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); + static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntryFromOffset( + incfs::verified_map_ptr<ResTable_type> 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<ResTable_config>* out_configs) const; + base::expected<std::monostate, IOError> CollectConfigurations( + bool exclude_mipmap, std::set<ResTable_config>* 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<const LoadedArsc> Load(const StringPiece& data, + static std::unique_ptr<const LoadedArsc> Load(incfs::map_ptr<void> 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 e351a46d633a..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 <android-base/expected.h> + #include <androidfw/Asset.h> +#include <androidfw/Errors.h> #include <androidfw/LocaleData.h> #include <androidfw/StringPiece.h> #include <utils/Errors.h> @@ -41,7 +44,7 @@ namespace android { constexpr const static uint32_t kIdmapMagic = 0x504D4449u; -constexpr const static uint32_t kIdmapCurrentVersion = 0x00000004u; +constexpr const static uint32_t kIdmapCurrentVersion = 0x00000005u; /** * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of @@ -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<void> 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<StringPiece16, NullOrIOError> 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<StringPiece16, NullOrIOError> 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<StringPiece, NullOrIOError> 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<String8, IOError> string8ObjectAt(size_t idx) const; - const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const; - const ResStringPool_span* styleAt(size_t idx) const; + base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> styleAt( + const ResStringPool_ref& ref) const; + base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> styleAt(size_t idx) const; - ssize_t indexOfString(const char16_t* str, size_t strLen) const; + base::expected<size_t, NullOrIOError> 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<void> 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<ResStringPool_header> mHeader; + size_t mSize; + mutable Mutex mDecodeLock; + incfs::map_ptr<uint32_t> mEntries; + incfs::map_ptr<uint32_t> mEntryStyles; + incfs::map_ptr<void> mStrings; + char16_t mutable** mCache; + uint32_t mStringPoolSize; // number of uint16_t + incfs::map_ptr<uint32_t> mStyles; + uint32_t mStylePoolSize; // number of uint32_t + + base::expected<StringPiece, NullOrIOError> stringDecodeAt( + size_t idx, incfs::map_ptr<uint8_t> 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<StringPiece, NullOrIOError> string8() const; + base::expected<StringPiece16, NullOrIOError> string16() const; private: const ResStringPool* mPool = nullptr; @@ -1476,7 +1480,7 @@ struct ResTable_entry // If set, this is a weak resource and may be overriden by strong // resources of the same name/type. This is only useful during // linking with other resource tables. - FLAG_WEAK = 0x0004 + FLAG_WEAK = 0x0004, }; uint16_t flags; @@ -1586,50 +1590,6 @@ struct ResTable_map Res_value value; }; - -// A ResTable_entry variant that either holds an unmanaged pointer to a constant ResTable_entry or -// holds a ResTable_entry which is tied to the lifetime of the handle. -class ResTable_entry_handle { - public: - ResTable_entry_handle() = default; - - ResTable_entry_handle(const ResTable_entry_handle& handle) { - entry_ = handle.entry_; - } - - ResTable_entry_handle(ResTable_entry_handle&& handle) noexcept { - entry_ = handle.entry_; - } - - inline static ResTable_entry_handle managed(ResTable_entry* entry, void (*deleter)(void *)) { - return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, deleter)); - } - - inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry) { - return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, [](auto /*p */){})); - } - - inline ResTable_entry_handle& operator=(const ResTable_entry_handle& handle) noexcept { - entry_ = handle.entry_; - return *this; - } - - inline ResTable_entry_handle& operator=(ResTable_entry_handle&& handle) noexcept { - entry_ = handle.entry_; - return *this; - } - - inline const ResTable_entry* operator*() & { - return entry_.get(); - } - - private: - explicit ResTable_entry_handle(std::shared_ptr<const ResTable_entry> entry) - : entry_(std::move(entry)) { } - - std::shared_ptr<const ResTable_entry> entry_; -}; - /** * A package-id to package name mapping for any shared libraries used * in this resource table. The package-id's encoded in this resource @@ -1717,6 +1677,10 @@ struct ResTable_overlayable_policy_header // The overlay must be signed with the same signature as the actor declared for the target // resource ACTOR_SIGNATURE = 0x00000080, + + // The overlay must be signed with the same signature as the reference package declared + // in the SystemConfig + CONFIG_SIGNATURE = 0x00000100, }; using PolicyBitmask = uint32_t; @@ -1736,7 +1700,6 @@ inline ResTable_overlayable_policy_header::PolicyFlags& operator |=( return first; } -#pragma pack(push, 1) struct Idmap_header { // Always 0x504D4449 ('IDMP') uint32_t magic; @@ -1747,7 +1710,7 @@ struct Idmap_header { uint32_t overlay_crc32; uint32_t fulfilled_policies; - uint8_t enforce_overlayable; + uint32_t enforce_overlayable; uint8_t target_path[256]; uint8_t overlay_path[256]; @@ -1761,23 +1724,31 @@ struct Idmap_header { struct Idmap_data_header { uint8_t target_package_id; uint8_t overlay_package_id; + + // Padding to ensure 4 byte alignment for target_entry_count + uint16_t p0; + uint32_t target_entry_count; + uint32_t target_inline_entry_count; uint32_t overlay_entry_count; + uint32_t string_pool_index_offset; - uint32_t string_pool_length; }; struct Idmap_target_entry { uint32_t target_id; - uint8_t type; - uint32_t value; + uint32_t overlay_id; +}; + +struct Idmap_target_entry_inline { + uint32_t target_id; + Res_value value; }; struct Idmap_overlay_entry { uint32_t overlay_id; uint32_t target_id; }; -#pragma pack(pop) class AssetManager2; @@ -1830,6 +1801,16 @@ private: bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue); +template<typename TChar, typename E> +static const TChar* UnpackOptionalString(base::expected<BasicStringPiece<TChar>, 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<AssetManager2::ResourceName, NullOrIOError> 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<uint32_t>(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 <unistd.h> #include <inttypes.h> + +#include <util/map_ptr.h> #include <zlib.h> #include <utils/Compat.h> @@ -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 <sstream> #include <vector> -#include "android-base/macros.h" +#include <android-base/macros.h> +#include <util/map_ptr.h> #include "androidfw/StringPiece.h" @@ -126,6 +127,11 @@ std::string Utf16ToUtf8(const StringPiece16& utf16); std::vector<std::string> SplitAndLowercase(const android::StringPiece& str, char sep); +template <typename T> +bool IsFourByteAligned(const incfs::map_ptr<T>& 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 <utils/Compat.h> -#include <utils/Errors.h> -#include <utils/FileMap.h> -#include <utils/threads.h> - +#include <optional> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <time.h> +#include <util/map_ptr.h> + +#include <utils/Compat.h> +#include <utils/Errors.h> +#include <utils/FileMap.h> +#include <utils/threads.h> + struct ZipArchive; typedef ZipArchive* ZipArchiveHandle; @@ -136,14 +139,26 @@ 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<incfs::IncFsFileMap> 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 <stdio.h> #include <time.h> +#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<void> in, void* buf, + long uncompressedLen, long compressedLen); /* * Someday we might want to make this generic and handle bzip2 ".bz2" diff --git a/libs/androidfw/libandroidfw_blacklist.txt b/libs/androidfw/libandroidfw_blocklist.txt index dd17e4d5b1e8..dd17e4d5b1e8 100644 --- a/libs/androidfw/libandroidfw_blacklist.txt +++ b/libs/androidfw/libandroidfw_blocklist.txt 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..471b0ee1e7e9 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()); - EXPECT_EQ(static_cast<uint8_t>(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); + ASSERT_EQ(3u, (*bag)->entry_count); - EXPECT_EQ(static_cast<uint8_t>(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<uint8_t>(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<uint8_t>(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<uint8_t>(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<uint8_t>(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); + 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); - 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 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); + 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); - 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 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,50 +421,107 @@ 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) { 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; + value.type = Res_value::TYPE_STRING; + value.resid = basic::R::string::test1; - uint32_t flags = 0u; + auto result = assetmanager.ResolveReference(value); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(1, value.cookie); + EXPECT_EQ(basic::R::string::test1, value.resid); +} - // Create some kind of Res_value that is NOT a reference. - Res_value value; - value.dataType = Res_value::TYPE_STRING; - value.data = 0; +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()}); - 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); + 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<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value.flags); } static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations, @@ -516,43 +533,45 @@ TEST_F(AssetManager2Test, GetResourceConfigurations) { AssetManager2 assetmanager; assetmanager.SetApkAssets({system_assets_.get(), basic_de_fr_assets_.get()}); - std::set<ResTable_config> 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 +597,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 +682,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 +711,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 +730,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 +749,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> asset = assetmanager.OpenNonAsset( - StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER); + std::unique_ptr<Asset> 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<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; std::array<uint32_t, attrs.size() + 1> 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> theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo).has_value()); std::array<uint32_t, 5> 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<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> 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> theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo).has_value()); std::array<uint32_t, 6> 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<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; std::array<uint32_t, attrs.size() + 1> 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<std::string>& 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/CommonHelpers.h b/libs/androidfw/tests/CommonHelpers.h index 8af13f20fb0d..316a57aa1ae9 100644 --- a/libs/androidfw/tests/CommonHelpers.h +++ b/libs/androidfw/tests/CommonHelpers.h @@ -21,8 +21,6 @@ #include <string> #include "androidfw/ResourceTypes.h" -#include "utils/String16.h" -#include "utils/String8.h" namespace android { @@ -40,10 +38,6 @@ static inline bool operator==(const ResTable_config& a, const ResTable_config& b return a.compare(b) == 0; } -static inline ::std::ostream& operator<<(::std::ostream& out, const String8& str) { - return out << str.string(); -} - static inline ::std::ostream& operator<<(::std::ostream& out, const ResTable_config& c) { return out << c.toString(); } 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<const ApkAssets> 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<ResXMLTree>(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<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + auto loaded_arsc = LoadedArsc::Load(reinterpret_cast<const void*>(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<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr<const LoadedArsc> 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<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr<const LoadedArsc> 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<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr<const LoadedArsc> 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<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, PROPERTY_DYNAMIC); + std::unique_ptr<const LoadedArsc> 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<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr<const LoadedArsc> 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<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr<const LoadedArsc> 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<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr<const LoadedArsc> 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<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_NE(nullptr, loaded_arsc); const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages(); @@ -320,7 +323,8 @@ TEST(LoadedArscTest, GetOverlayableMap) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); ASSERT_NE(nullptr, loaded_arsc); const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages(); @@ -345,7 +349,7 @@ TEST(LoadedArscTest, LoadCustomLoader) { asset->getLength()); std::unique_ptr<const LoadedArsc> 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> 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> 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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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> 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<uint32_t>(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<uint32_t>(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<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); + GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value->data)); + EXPECT_EQ(static_cast<uint32_t>(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<uint32_t>(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<uint32_t>(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> 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> 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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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> 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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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> 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<uint32_t>(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<uint32_t>(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> 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<uint32_t>(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<uint32_t>(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> 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<uint32_t>(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<uint32_t>(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 diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk Binary files differindex f1ed59279fdb..c9bf2527c82a 100644 --- a/libs/androidfw/tests/data/overlay/overlay.apk +++ b/libs/androidfw/tests/data/overlay/overlay.apk diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap Binary files differindex 29c5eb6a9ccf..3ab244eb084a 100644 --- a/libs/androidfw/tests/data/overlay/overlay.idmap +++ b/libs/androidfw/tests/data/overlay/overlay.idmap diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp index e713b98b867e..0388e92e1d1b 100644 --- a/libs/hostgraphics/Android.bp +++ b/libs/hostgraphics/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + cc_library_host_static { name: "libhostgraphics", @@ -28,4 +37,4 @@ cc_library_host_static { enabled: true, } }, -}
\ No newline at end of file +} diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index aa842ff6a7b7..6220abed05eb 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -1,3 +1,33 @@ +package { + default_applicable_licenses: ["frameworks_base_libs_hwui_license"], +} + +// Added automatically by a large-scale-change that took the approach of +// 'apply every license found to every target'. While this makes sure we respect +// every license restriction, it may not be entirely correct. +// +// e.g. GPL in an MIT project might only apply to the contrib/ directory. +// +// Please consider splitting the single license below into multiple licenses, +// taking care not to lose any license_kind information, and overriding the +// default license using the 'licenses: [...]' property on targets as needed. +// +// For unused files, consider creating a 'fileGroup' with "//visibility:private" +// to attach the license to, and including a comment whether the files may be +// used in the current project. +// See: http://go/android-license-faq +license { + name: "frameworks_base_libs_hwui_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + "SPDX-license-identifier-BSD", + ], + license_text: [ + "NOTICE", + ], +} + cc_defaults { name: "hwui_defaults", defaults: [ @@ -313,12 +343,13 @@ cc_defaults { "libharfbuzz_ng", "liblog", "libminikin", - "libnativehelper", "libz", "libziparchive", "libjpeg", ], + static_libs: ["libnativehelper_lazy"], + target: { android: { srcs: [ // sources that depend on android only libraries diff --git a/libs/hwui/OWNERS b/libs/hwui/OWNERS index 936ba5cc8311..bb93e66968be 100644 --- a/libs/hwui/OWNERS +++ b/libs/hwui/OWNERS @@ -1,6 +1,10 @@ +alecmouri@google.com +djsollen@google.com jreck@google.com njawad@google.com -djsollen@google.com -stani@google.com -scroggo@google.com reed@google.com +scroggo@google.com +stani@google.com + +# For text, e.g. Typeface, Font, Minikin, etc. +nona@google.com diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp index 808921d344da..61d06c2697aa 100644 --- a/libs/hwui/PathParser.cpp +++ b/libs/hwui/PathParser.cpp @@ -16,8 +16,6 @@ #include "PathParser.h" -#include "jni.h" - #include <errno.h> #include <stdlib.h> #include <utils/Log.h> diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h index f5bebce605fb..878bb7c0f137 100644 --- a/libs/hwui/PathParser.h +++ b/libs/hwui/PathParser.h @@ -22,7 +22,6 @@ #include <android/log.h> #include <cutils/compiler.h> -#include <jni.h> #include <string> diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index dc467c41baed..41ecd5e49acd 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -401,10 +401,11 @@ struct DrawImageLattice final : Op { struct DrawTextBlob final : Op { static const auto kType = Type::DrawTextBlob; DrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) - : blob(sk_ref_sp(blob)), x(x), y(y), paint(paint) {} + : blob(sk_ref_sp(blob)), x(x), y(y), paint(paint), drawTextBlobMode(gDrawTextBlobMode) {} sk_sp<const SkTextBlob> blob; SkScalar x, y; SkPaint paint; + DrawTextBlobMode drawTextBlobMode; void draw(SkCanvas* c, const SkMatrix&) const { c->drawTextBlob(blob.get(), x, y, paint); } }; @@ -791,6 +792,24 @@ constexpr color_transform_fn colorTransformForOp() { } } +template<> +constexpr color_transform_fn colorTransformForOp<DrawTextBlob>() { + return [](const void *opRaw, ColorTransform transform) { + const DrawTextBlob *op = reinterpret_cast<const DrawTextBlob*>(opRaw); + switch (op->drawTextBlobMode) { + case DrawTextBlobMode::HctOutline: + const_cast<SkPaint&>(op->paint).setColor(SK_ColorBLACK); + break; + case DrawTextBlobMode::HctInner: + const_cast<SkPaint&>(op->paint).setColor(SK_ColorWHITE); + break; + default: + transformPaint(transform, const_cast<SkPaint*>(&(op->paint))); + break; + } + }; +} + #define X(T) colorTransformForOp<T>(), static const color_transform_fn color_transform_fns[] = { #include "DisplayListOps.in" diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 56d951cdb338..60ef4371d38d 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -217,11 +217,8 @@ static SkImageInfo validateAlpha(const SkImageInfo& info) { void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes) { mInfo = validateAlpha(newInfo); - // Dirty hack is dirty - // TODO: Figure something out here, Skia's current design makes this - // really hard to work with. Skia really, really wants immutable objects, - // but with the nested-ref-count hackery going on that's just not - // feasible without going insane trying to figure it out + // TODO: Skia intends for SkPixelRef to be immutable, but this method + // modifies it. Find another way to support reusing the same pixel memory. this->android_only_reset(mInfo.width(), mInfo.height(), rowBytes); } diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index c138a32eacc2..14906d5c1166 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -111,6 +111,7 @@ public: bool darken = channelSum < (128 * 3); // outline + gDrawTextBlobMode = DrawTextBlobMode::HctOutline; Paint outlinePaint(paint); simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); @@ -118,11 +119,14 @@ public: bounds.mRight, bounds.mBottom, totalAdvance); // inner + gDrawTextBlobMode = DrawTextBlobMode::HctInner; Paint innerPaint(paint); simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint); innerPaint.setStyle(SkPaint::kFill_Style); canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance); + + gDrawTextBlobMode = DrawTextBlobMode::Normal; } else { // standard draw path canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, bounds.mLeft, bounds.mTop, diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 27dfed305a94..d9928059ed30 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -87,6 +87,14 @@ class Bitmap; class Paint; struct Typeface; +enum class DrawTextBlobMode { + Normal, + HctOutline, + HctInner, +}; + +inline DrawTextBlobMode gDrawTextBlobMode = DrawTextBlobMode::Normal; + class ANDROID_API Canvas { public: virtual ~Canvas(){}; diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp index d4e27d812500..e8e89d81bdb7 100644 --- a/libs/hwui/jni/BitmapFactory.cpp +++ b/libs/hwui/jni/BitmapFactory.cpp @@ -16,7 +16,7 @@ #include "Utils.h" #include <HardwareBitmapUploader.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <androidfw/Asset.h> #include <androidfw/ResourceTypes.h> #include <cutils/compiler.h> diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp index 41d939bd6373..c8c3d3d5b078 100644 --- a/libs/hwui/jni/ImageDecoder.cpp +++ b/libs/hwui/jni/ImageDecoder.cpp @@ -152,7 +152,7 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream, } static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, - jobject fileDescriptor, jboolean preferAnimation, jobject source) { + jobject fileDescriptor, jlong length, jboolean preferAnimation, jobject source) { #ifndef __ANDROID__ // LayoutLib for Windows does not support F_DUPFD_CLOEXEC return throw_exception(env, kSourceException, "Only supported on Android", nullptr, source); #else @@ -172,7 +172,14 @@ static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, nullptr, source); } - std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file)); + std::unique_ptr<SkFILEStream> fileStream; + if (length == -1) { + // -1 corresponds to AssetFileDescriptor.UNKNOWN_LENGTH. Pass no length + // so SkFILEStream will figure out the size of the file on its own. + fileStream.reset(new SkFILEStream(file)); + } else { + fileStream.reset(new SkFILEStream(file, length)); + } return native_create(env, std::move(fileStream), source, preferAnimation); #endif } @@ -494,7 +501,7 @@ static const JNINativeMethod gImageDecoderMethods[] = { { "nCreate", "(Ljava/nio/ByteBuffer;IIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer }, { "nCreate", "([BIIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray }, { "nCreate", "(Ljava/io/InputStream;[BZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream }, - { "nCreate", "(Ljava/io/FileDescriptor;ZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd }, + { "nCreate", "(Ljava/io/FileDescriptor;JZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd }, { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZJZ)Landroid/graphics/Bitmap;", (void*) ImageDecoder_nDecodeBitmap }, { "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize }, diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 49c7fcd468e1..9815e85db880 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -27,7 +27,7 @@ #include <inttypes.h> #include <media/NdkImage.h> #include <media/NdkImageReader.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <pipeline/skia/ShaderCache.h> #include <private/EGL/cache.h> #include <renderthread/CanvasContext.h> diff --git a/libs/hwui/jni/android_nio_utils.cpp b/libs/hwui/jni/android_nio_utils.cpp index c2b09c1d15d7..0663821a5d89 100644 --- a/libs/hwui/jni/android_nio_utils.cpp +++ b/libs/hwui/jni/android_nio_utils.cpp @@ -16,7 +16,7 @@ #include "android_nio_utils.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> namespace android { diff --git a/libs/hwui/jni/android_nio_utils.h b/libs/hwui/jni/android_nio_utils.h index 4aaa0a78c276..4760d9ca8107 100644 --- a/libs/hwui/jni/android_nio_utils.h +++ b/libs/hwui/jni/android_nio_utils.h @@ -17,7 +17,9 @@ #ifndef _ANDROID_NIO_UTILS_H_ #define _ANDROID_NIO_UTILS_H_ -#include <nativehelper/JNIHelp.h> +#include <jni.h> + +#include <cstddef> namespace android { diff --git a/libs/hwui/jni/graphics_jni_helpers.h b/libs/hwui/jni/graphics_jni_helpers.h index b97cc6a10179..78db54acc9e5 100644 --- a/libs/hwui/jni/graphics_jni_helpers.h +++ b/libs/hwui/jni/graphics_jni_helpers.h @@ -18,7 +18,7 @@ #define GRAPHICS_JNI_HELPERS #include <log/log.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <nativehelper/scoped_local_ref.h> #include <nativehelper/scoped_utf_chars.h> #include <string> diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index 158c3493a90c..2edd48c5a41b 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -102,12 +102,12 @@ bool SkiaDisplayList::prepareListAndChildren( bool hasBackwardProjectedNodesSubtree = false; for (auto& child : mChildNodes) { - hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards(); RenderNode* childNode = child.getRenderNode(); Matrix4 mat4(child.getRecordedMatrix()); info.damageAccumulator->pushTransform(&mat4); info.hasBackwardProjectedNodes = false; childFn(childNode, observer, info, functorsNeedLayer); + hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards(); hasBackwardProjectedNodesSubtree |= info.hasBackwardProjectedNodes; info.damageAccumulator->popTransform(); } diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 5ff8fbc05e95..621f2863fa03 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -301,8 +301,8 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch } sk_sp<SkImage> image = bitmap.makeImage(); - applyLooper(get_looper(paint), *filterBitmap(*filteredPaint), [&](SkScalar x, SkScalar y, - const SkPaint& p) { + applyLooper(get_looper(paint), *filterBitmap(std::move(filteredPaint)), + [&](SkScalar x, SkScalar y, const SkPaint& p) { mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), &p, bitmap.palette()); }); diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp index d291ec001daf..697cbb9d41c0 100644 --- a/libs/incident/Android.bp +++ b/libs/incident/Android.bp @@ -13,6 +13,15 @@ // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + cc_defaults { name: "libincidentpriv_defaults", @@ -125,6 +134,3 @@ cc_test { "libgmock", ], } - - - diff --git a/libs/incident/OWNERS b/libs/incident/OWNERS new file mode 100644 index 000000000000..f76611555dbb --- /dev/null +++ b/libs/incident/OWNERS @@ -0,0 +1 @@ +include /cmds/incidentd/OWNERS diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 88d6033ed9fb..55f932dffff1 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -12,11 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + cc_library_shared { name: "libinputservice", srcs: [ "PointerController.cpp", + "PointerControllerContext.cpp", + "MouseCursorController.cpp", + "TouchSpotController.cpp", "SpriteController.cpp", + "SpriteIcon.cpp", ], shared_libs: [ diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp new file mode 100644 index 000000000000..45da008c3e8e --- /dev/null +++ b/libs/input/MouseCursorController.cpp @@ -0,0 +1,489 @@ +/* + * 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. + */ + +#define LOG_TAG "MouseCursorController" +//#define LOG_NDEBUG 0 + +// Log debug messages about pointer updates +#define DEBUG_MOUSE_CURSOR_UPDATES 0 + +#include "MouseCursorController.h" + +#include <log/log.h> + +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkColor.h> +#include <SkPaint.h> + +namespace { +// Time to spend fading out the pointer completely. +const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms +} // namespace + +namespace android { + +// --- MouseCursorController --- + +MouseCursorController::MouseCursorController(PointerControllerContext& context) + : mContext(context) { + std::scoped_lock lock(mLock); + + mLocked.animationFrameIndex = 0; + mLocked.lastFrameUpdatedTime = 0; + + mLocked.pointerFadeDirection = 0; + mLocked.pointerX = 0; + mLocked.pointerY = 0; + mLocked.pointerAlpha = 0.0f; // pointer is initially faded + mLocked.pointerSprite = mContext.getSpriteController()->createSprite(); + mLocked.updatePointerIcon = false; + mLocked.requestedPointerType = mContext.getPolicy()->getDefaultPointerIconId(); + + mLocked.resourcesLoaded = false; + + mLocked.buttonState = 0; +} + +MouseCursorController::~MouseCursorController() { + std::scoped_lock lock(mLock); + + mLocked.pointerSprite.clear(); +} + +bool MouseCursorController::getBounds(float* outMinX, float* outMinY, float* outMaxX, + float* outMaxY) const { + std::scoped_lock lock(mLock); + + return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY); +} + +bool MouseCursorController::getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, + float* outMaxY) const REQUIRES(mLock) { + if (!mLocked.viewport.isValid()) { + return false; + } + + *outMinX = mLocked.viewport.logicalLeft; + *outMinY = mLocked.viewport.logicalTop; + *outMaxX = mLocked.viewport.logicalRight - 1; + *outMaxY = mLocked.viewport.logicalBottom - 1; + return true; +} + +void MouseCursorController::move(float deltaX, float deltaY) { +#if DEBUG_MOUSE_CURSOR_UPDATES + ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY); +#endif + if (deltaX == 0.0f && deltaY == 0.0f) { + return; + } + + std::scoped_lock lock(mLock); + + setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY); +} + +void MouseCursorController::setButtonState(int32_t buttonState) { +#if DEBUG_MOUSE_CURSOR_UPDATES + ALOGD("Set button state 0x%08x", buttonState); +#endif + std::scoped_lock lock(mLock); + + if (mLocked.buttonState != buttonState) { + mLocked.buttonState = buttonState; + } +} + +int32_t MouseCursorController::getButtonState() const { + std::scoped_lock lock(mLock); + return mLocked.buttonState; +} + +void MouseCursorController::setPosition(float x, float y) { +#if DEBUG_MOUSE_CURSOR_UPDATES + ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y); +#endif + std::scoped_lock lock(mLock); + setPositionLocked(x, y); +} + +void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) { + float minX, minY, maxX, maxY; + if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { + if (x <= minX) { + mLocked.pointerX = minX; + } else if (x >= maxX) { + mLocked.pointerX = maxX; + } else { + mLocked.pointerX = x; + } + if (y <= minY) { + mLocked.pointerY = minY; + } else if (y >= maxY) { + mLocked.pointerY = maxY; + } else { + mLocked.pointerY = y; + } + updatePointerLocked(); + } +} + +void MouseCursorController::getPosition(float* outX, float* outY) const { + std::scoped_lock lock(mLock); + + *outX = mLocked.pointerX; + *outY = mLocked.pointerY; +} + +int32_t MouseCursorController::getDisplayId() const { + std::scoped_lock lock(mLock); + return mLocked.viewport.displayId; +} + +void MouseCursorController::fade(PointerControllerInterface::Transition transition) { + std::scoped_lock lock(mLock); + + // Remove the inactivity timeout, since we are fading now. + mContext.removeInactivityTimeout(); + + // Start fading. + if (transition == PointerControllerInterface::Transition::IMMEDIATE) { + mLocked.pointerFadeDirection = 0; + mLocked.pointerAlpha = 0.0f; + updatePointerLocked(); + } else { + mLocked.pointerFadeDirection = -1; + startAnimationLocked(); + } +} + +void MouseCursorController::unfade(PointerControllerInterface::Transition transition) { + std::scoped_lock lock(mLock); + + // Always reset the inactivity timer. + mContext.resetInactivityTimeout(); + + // Start unfading. + if (transition == PointerControllerInterface::Transition::IMMEDIATE) { + mLocked.pointerFadeDirection = 0; + mLocked.pointerAlpha = 1.0f; + updatePointerLocked(); + } else { + mLocked.pointerFadeDirection = 1; + startAnimationLocked(); + } +} + +void MouseCursorController::reloadPointerResources(bool getAdditionalMouseResources) { + std::scoped_lock lock(mLock); + + loadResourcesLocked(getAdditionalMouseResources); + updatePointerLocked(); +} + +/** + * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation, + * so here we are getting the dimensions in the original, unrotated orientation (orientation 0). + */ +static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) { + width = viewport.deviceWidth; + height = viewport.deviceHeight; + + if (viewport.orientation == DISPLAY_ORIENTATION_90 || + viewport.orientation == DISPLAY_ORIENTATION_270) { + std::swap(width, height); + } +} + +void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport, + bool getAdditionalMouseResources) { + std::scoped_lock lock(mLock); + + if (viewport == mLocked.viewport) { + return; + } + + const DisplayViewport oldViewport = mLocked.viewport; + mLocked.viewport = viewport; + + int32_t oldDisplayWidth, oldDisplayHeight; + getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight); + int32_t newDisplayWidth, newDisplayHeight; + getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight); + + // Reset cursor position to center if size or display changed. + if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth || + oldDisplayHeight != newDisplayHeight) { + float minX, minY, maxX, maxY; + if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { + mLocked.pointerX = (minX + maxX) * 0.5f; + mLocked.pointerY = (minY + maxY) * 0.5f; + // Reload icon resources for density may be changed. + loadResourcesLocked(getAdditionalMouseResources); + } else { + mLocked.pointerX = 0; + mLocked.pointerY = 0; + } + } else if (oldViewport.orientation != viewport.orientation) { + // Apply offsets to convert from the pixel top-left corner position to the pixel center. + // This creates an invariant frame of reference that we can easily rotate when + // taking into account that the pointer may be located at fractional pixel offsets. + float x = mLocked.pointerX + 0.5f; + float y = mLocked.pointerY + 0.5f; + float temp; + + // Undo the previous rotation. + switch (oldViewport.orientation) { + case DISPLAY_ORIENTATION_90: + temp = x; + x = oldViewport.deviceHeight - y; + y = temp; + break; + case DISPLAY_ORIENTATION_180: + x = oldViewport.deviceWidth - x; + y = oldViewport.deviceHeight - y; + break; + case DISPLAY_ORIENTATION_270: + temp = x; + x = y; + y = oldViewport.deviceWidth - temp; + break; + } + + // Perform the new rotation. + switch (viewport.orientation) { + case DISPLAY_ORIENTATION_90: + temp = x; + x = y; + y = viewport.deviceHeight - temp; + break; + case DISPLAY_ORIENTATION_180: + x = viewport.deviceWidth - x; + y = viewport.deviceHeight - y; + break; + case DISPLAY_ORIENTATION_270: + temp = x; + x = viewport.deviceWidth - y; + y = temp; + break; + } + + // Apply offsets to convert from the pixel center to the pixel top-left corner position + // and save the results. + mLocked.pointerX = x - 0.5f; + mLocked.pointerY = y - 0.5f; + } + + updatePointerLocked(); +} + +void MouseCursorController::updatePointerIcon(int32_t iconId) { + std::scoped_lock lock(mLock); + + if (mLocked.requestedPointerType != iconId) { + mLocked.requestedPointerType = iconId; + mLocked.updatePointerIcon = true; + updatePointerLocked(); + } +} + +void MouseCursorController::setCustomPointerIcon(const SpriteIcon& icon) { + std::scoped_lock lock(mLock); + + const int32_t iconId = mContext.getPolicy()->getCustomPointerIconId(); + mLocked.additionalMouseResources[iconId] = icon; + mLocked.requestedPointerType = iconId; + mLocked.updatePointerIcon = true; + updatePointerLocked(); +} + +bool MouseCursorController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) { + nsecs_t frameDelay = timestamp - mContext.getAnimationTime(); + bool keepAnimating = false; + + // Animate pointer fade. + if (mLocked.pointerFadeDirection < 0) { + mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION; + if (mLocked.pointerAlpha <= 0.0f) { + mLocked.pointerAlpha = 0.0f; + mLocked.pointerFadeDirection = 0; + } else { + keepAnimating = true; + } + updatePointerLocked(); + } else if (mLocked.pointerFadeDirection > 0) { + mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION; + if (mLocked.pointerAlpha >= 1.0f) { + mLocked.pointerAlpha = 1.0f; + mLocked.pointerFadeDirection = 0; + } else { + keepAnimating = true; + } + updatePointerLocked(); + } + return keepAnimating; +} + +bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) { + std::map<int32_t, PointerAnimation>::const_iterator iter = + mLocked.animationResources.find(mLocked.requestedPointerType); + if (iter == mLocked.animationResources.end()) { + return false; + } + + if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) { + sp<SpriteController> spriteController = mContext.getSpriteController(); + spriteController->openTransaction(); + + int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame; + mLocked.animationFrameIndex += incr; + mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr; + while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) { + mLocked.animationFrameIndex -= iter->second.animationFrames.size(); + } + mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]); + + spriteController->closeTransaction(); + } + // Keep animating. + return true; +} + +void MouseCursorController::updatePointerLocked() REQUIRES(mLock) { + if (!mLocked.viewport.isValid()) { + return; + } + sp<SpriteController> spriteController = mContext.getSpriteController(); + spriteController->openTransaction(); + + mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER); + mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY); + mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId); + + if (mLocked.pointerAlpha > 0) { + mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha); + mLocked.pointerSprite->setVisible(true); + } else { + mLocked.pointerSprite->setVisible(false); + } + + if (mLocked.updatePointerIcon) { + if (mLocked.requestedPointerType == mContext.getPolicy()->getDefaultPointerIconId()) { + mLocked.pointerSprite->setIcon(mLocked.pointerIcon); + } else { + std::map<int32_t, SpriteIcon>::const_iterator iter = + mLocked.additionalMouseResources.find(mLocked.requestedPointerType); + if (iter != mLocked.additionalMouseResources.end()) { + std::map<int32_t, PointerAnimation>::const_iterator anim_iter = + mLocked.animationResources.find(mLocked.requestedPointerType); + if (anim_iter != mLocked.animationResources.end()) { + mLocked.animationFrameIndex = 0; + mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC); + startAnimationLocked(); + } + mLocked.pointerSprite->setIcon(iter->second); + } else { + ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerType); + mLocked.pointerSprite->setIcon(mLocked.pointerIcon); + } + } + mLocked.updatePointerIcon = false; + } + + spriteController->closeTransaction(); +} + +void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) { + if (!mLocked.viewport.isValid()) { + return; + } + + if (!mLocked.resourcesLoaded) mLocked.resourcesLoaded = true; + + sp<PointerControllerPolicyInterface> policy = mContext.getPolicy(); + policy->loadPointerResources(&mResources, mLocked.viewport.displayId); + policy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId); + + mLocked.additionalMouseResources.clear(); + mLocked.animationResources.clear(); + if (getAdditionalMouseResources) { + policy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, + &mLocked.animationResources, + mLocked.viewport.displayId); + } + + mLocked.updatePointerIcon = true; +} + +bool MouseCursorController::isViewportValid() { + std::scoped_lock lock(mLock); + return mLocked.viewport.isValid(); +} + +void MouseCursorController::getAdditionalMouseResources() { + std::scoped_lock lock(mLock); + + if (mLocked.additionalMouseResources.empty()) { + mContext.getPolicy()->loadAdditionalMouseResources(&mLocked.additionalMouseResources, + &mLocked.animationResources, + mLocked.viewport.displayId); + } + mLocked.updatePointerIcon = true; + updatePointerLocked(); +} + +bool MouseCursorController::resourcesLoaded() { + std::scoped_lock lock(mLock); + return mLocked.resourcesLoaded; +} + +bool MouseCursorController::doAnimations(nsecs_t timestamp) { + std::scoped_lock lock(mLock); + bool keepFading = doFadingAnimationLocked(timestamp); + bool keepBitmap = doBitmapAnimationLocked(timestamp); + bool keepAnimating = keepFading || keepBitmap; + if (!keepAnimating) { + /* + * We know that this callback will be removed before another + * is added. mLock in PointerAnimator will not be released + * until after this is removed, and adding another callback + * requires that lock. Thus it's safe to set mLocked.animating + * here. + */ + mLocked.animating = false; + } + return keepAnimating; +} + +void MouseCursorController::startAnimationLocked() REQUIRES(mLock) { + using namespace std::placeholders; + + if (mLocked.animating) { + return; + } + mLocked.animating = true; + + std::function<bool(nsecs_t)> func = std::bind(&MouseCursorController::doAnimations, this, _1); + /* + * Using -1 for displayId here to avoid removing the callback + * if a TouchSpotController with the same display is removed. + */ + mContext.addAnimationCallback(-1, func); +} + +} // namespace android diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h new file mode 100644 index 000000000000..e6dfc4c6f99a --- /dev/null +++ b/libs/input/MouseCursorController.h @@ -0,0 +1,118 @@ +/* + * 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 _UI_MOUSE_CURSOR_CONTROLLER_H +#define _UI_MOUSE_CURSOR_CONTROLLER_H + +#include <gui/DisplayEventReceiver.h> +#include <input/DisplayViewport.h> +#include <input/Input.h> +#include <ui/DisplayInfo.h> +#include <utils/BitSet.h> +#include <utils/Looper.h> +#include <utils/RefBase.h> + +#include <functional> +#include <map> +#include <memory> +#include <vector> + +#include "PointerControllerContext.h" +#include "SpriteController.h" + +namespace android { + +/* + * Helper class for PointerController that specifically handles + * mouse cursor resources and actions. + */ +class MouseCursorController { +public: + MouseCursorController(PointerControllerContext& context); + ~MouseCursorController(); + + bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; + void move(float deltaX, float deltaY); + void setButtonState(int32_t buttonState); + int32_t getButtonState() const; + void setPosition(float x, float y); + void getPosition(float* outX, float* outY) const; + int32_t getDisplayId() const; + void fade(PointerControllerInterface::Transition transition); + void unfade(PointerControllerInterface::Transition transition); + void setDisplayViewport(const DisplayViewport& viewport, bool getAdditionalMouseResources); + + void updatePointerIcon(int32_t iconId); + void setCustomPointerIcon(const SpriteIcon& icon); + void reloadPointerResources(bool getAdditionalMouseResources); + + void getAdditionalMouseResources(); + bool isViewportValid(); + + bool doAnimations(nsecs_t timestamp); + + bool resourcesLoaded(); + +private: + mutable std::mutex mLock; + + PointerResources mResources; + + PointerControllerContext& mContext; + + struct Locked { + DisplayViewport viewport; + + size_t animationFrameIndex; + nsecs_t lastFrameUpdatedTime; + + int32_t pointerFadeDirection; + float pointerX; + float pointerY; + float pointerAlpha; + sp<Sprite> pointerSprite; + SpriteIcon pointerIcon; + bool updatePointerIcon; + + bool resourcesLoaded; + + std::map<int32_t, SpriteIcon> additionalMouseResources; + std::map<int32_t, PointerAnimation> animationResources; + + int32_t requestedPointerType; + + int32_t buttonState; + + bool animating{false}; + + } mLocked GUARDED_BY(mLock); + + bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; + void setPositionLocked(float x, float y); + + void updatePointerLocked(); + + void loadResourcesLocked(bool getAdditionalMouseResources); + + bool doBitmapAnimationLocked(nsecs_t timestamp); + bool doFadingAnimationLocked(nsecs_t timestamp); + + void startAnimationLocked(); +}; + +} // namespace android + +#endif // _UI_MOUSE_CURSOR_CONTROLLER_H diff --git a/libs/input/OWNERS b/libs/input/OWNERS new file mode 100644 index 000000000000..d701f23cb9b8 --- /dev/null +++ b/libs/input/OWNERS @@ -0,0 +1 @@ +include /core/java/android/hardware/input/OWNERS diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 3b494e9129db..8f04cfb70469 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -21,785 +21,195 @@ #define DEBUG_POINTER_UPDATES 0 #include "PointerController.h" +#include "MouseCursorController.h" +#include "PointerControllerContext.h" +#include "TouchSpotController.h" #include <log/log.h> -namespace android { - -// --- WeakLooperCallback --- - -class WeakLooperCallback: public LooperCallback { -protected: - virtual ~WeakLooperCallback() { } - -public: - explicit WeakLooperCallback(const wp<LooperCallback>& callback) : - mCallback(callback) { - } - - virtual int handleEvent(int fd, int events, void* data) { - sp<LooperCallback> callback = mCallback.promote(); - if (callback != NULL) { - return callback->handleEvent(fd, events, data); - } - return 0; // the client is gone, remove the callback - } - -private: - wp<LooperCallback> mCallback; -}; - -// --- PointerController --- - -// Time to wait before starting the fade when the pointer is inactive. -static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds -static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds - -// Time to spend fading out the spot completely. -static const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkColor.h> +#include <SkPaint.h> -// Time to spend fading out the pointer completely. -static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms - -// The number of events to be read at once for DisplayEventReceiver. -static const int EVENT_BUFFER_SIZE = 100; +namespace android { // --- PointerController --- -PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy, - const sp<Looper>& looper, const sp<SpriteController>& spriteController) : - mPolicy(policy), mLooper(looper), mSpriteController(spriteController) { - mHandler = new WeakMessageHandler(this); - mCallback = new WeakLooperCallback(this); - - if (mDisplayEventReceiver.initCheck() == NO_ERROR) { - mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, - Looper::EVENT_INPUT, mCallback, nullptr); - } else { - ALOGE("Failed to initialize DisplayEventReceiver."); - } - - AutoMutex _l(mLock); - - mLocked.animationPending = false; - - mLocked.presentation = PRESENTATION_POINTER; - mLocked.presentationChanged = false; - - mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL; - - mLocked.pointerFadeDirection = 0; - mLocked.pointerX = 0; - mLocked.pointerY = 0; - mLocked.pointerAlpha = 0.0f; // pointer is initially faded - mLocked.pointerSprite = mSpriteController->createSprite(); - mLocked.pointerIconChanged = false; - mLocked.requestedPointerType = mPolicy->getDefaultPointerIconId(); - - mLocked.animationFrameIndex = 0; - mLocked.lastFrameUpdatedTime = 0; - - mLocked.buttonState = 0; +std::shared_ptr<PointerController> PointerController::create( + const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, + const sp<SpriteController>& spriteController) { + // using 'new' to access non-public constructor + std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>( + new PointerController(policy, looper, spriteController)); + + /* + * Now we need to hook up the constructed PointerController object to its callbacks. + * + * This must be executed after the constructor but before any other methods on PointerController + * in order to ensure that the fully constructed object is visible on the Looper thread, since + * that may be a different thread than where the PointerController is initially constructed. + * + * Unfortunately, this cannot be done as part of the constructor since we need to hand out + * weak_ptr's which themselves cannot be constructed until there's at least one shared_ptr. + */ + + controller->mContext.setHandlerController(controller); + controller->mContext.setCallbackController(controller); + return controller; } -PointerController::~PointerController() { - mLooper->removeMessages(mHandler); - - AutoMutex _l(mLock); - - mLocked.pointerSprite.clear(); - - for (auto& it : mLocked.spotsByDisplay) { - const std::vector<Spot*>& spots = it.second; - size_t numSpots = spots.size(); - for (size_t i = 0; i < numSpots; i++) { - delete spots[i]; - } - } - mLocked.spotsByDisplay.clear(); - mLocked.recycledSprites.clear(); -} - -bool PointerController::getBounds(float* outMinX, float* outMinY, - float* outMaxX, float* outMaxY) const { - AutoMutex _l(mLock); - - return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY); +PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy, + const sp<Looper>& looper, + const sp<SpriteController>& spriteController) + : mContext(policy, looper, spriteController, *this), mCursorController(mContext) { + std::scoped_lock lock(mLock); + mLocked.presentation = Presentation::SPOT; } -bool PointerController::getBoundsLocked(float* outMinX, float* outMinY, - float* outMaxX, float* outMaxY) const { - - if (!mLocked.viewport.isValid()) { - return false; - } - - *outMinX = mLocked.viewport.logicalLeft; - *outMinY = mLocked.viewport.logicalTop; - *outMaxX = mLocked.viewport.logicalRight - 1; - *outMaxY = mLocked.viewport.logicalBottom - 1; - return true; +bool PointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX, + float* outMaxY) const { + return mCursorController.getBounds(outMinX, outMinY, outMaxX, outMaxY); } void PointerController::move(float deltaX, float deltaY) { -#if DEBUG_POINTER_UPDATES - ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY); -#endif - if (deltaX == 0.0f && deltaY == 0.0f) { - return; - } - - AutoMutex _l(mLock); - - setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY); + mCursorController.move(deltaX, deltaY); } void PointerController::setButtonState(int32_t buttonState) { -#if DEBUG_POINTER_UPDATES - ALOGD("Set button state 0x%08x", buttonState); -#endif - AutoMutex _l(mLock); - - if (mLocked.buttonState != buttonState) { - mLocked.buttonState = buttonState; - } + mCursorController.setButtonState(buttonState); } int32_t PointerController::getButtonState() const { - AutoMutex _l(mLock); - - return mLocked.buttonState; + return mCursorController.getButtonState(); } void PointerController::setPosition(float x, float y) { -#if DEBUG_POINTER_UPDATES - ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y); -#endif - AutoMutex _l(mLock); - - setPositionLocked(x, y); -} - -void PointerController::setPositionLocked(float x, float y) { - float minX, minY, maxX, maxY; - if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { - if (x <= minX) { - mLocked.pointerX = minX; - } else if (x >= maxX) { - mLocked.pointerX = maxX; - } else { - mLocked.pointerX = x; - } - if (y <= minY) { - mLocked.pointerY = minY; - } else if (y >= maxY) { - mLocked.pointerY = maxY; - } else { - mLocked.pointerY = y; - } - updatePointerLocked(); - } + std::scoped_lock lock(mLock); + mCursorController.setPosition(x, y); } void PointerController::getPosition(float* outX, float* outY) const { - AutoMutex _l(mLock); - - *outX = mLocked.pointerX; - *outY = mLocked.pointerY; + mCursorController.getPosition(outX, outY); } int32_t PointerController::getDisplayId() const { - AutoMutex _l(mLock); - - return mLocked.viewport.displayId; + return mCursorController.getDisplayId(); } void PointerController::fade(Transition transition) { - AutoMutex _l(mLock); - - // Remove the inactivity timeout, since we are fading now. - removeInactivityTimeoutLocked(); - - // Start fading. - if (transition == TRANSITION_IMMEDIATE) { - mLocked.pointerFadeDirection = 0; - mLocked.pointerAlpha = 0.0f; - updatePointerLocked(); - } else { - mLocked.pointerFadeDirection = -1; - startAnimationLocked(); - } + std::scoped_lock lock(mLock); + mCursorController.fade(transition); } void PointerController::unfade(Transition transition) { - AutoMutex _l(mLock); - - // Always reset the inactivity timer. - resetInactivityTimeoutLocked(); - - // Start unfading. - if (transition == TRANSITION_IMMEDIATE) { - mLocked.pointerFadeDirection = 0; - mLocked.pointerAlpha = 1.0f; - updatePointerLocked(); - } else { - mLocked.pointerFadeDirection = 1; - startAnimationLocked(); - } + std::scoped_lock lock(mLock); + mCursorController.unfade(transition); } void PointerController::setPresentation(Presentation presentation) { - AutoMutex _l(mLock); + std::scoped_lock lock(mLock); if (mLocked.presentation == presentation) { return; } mLocked.presentation = presentation; - mLocked.presentationChanged = true; - if (!mLocked.viewport.isValid()) { + if (!mCursorController.isViewportValid()) { return; } - if (presentation == PRESENTATION_POINTER) { - if (mLocked.additionalMouseResources.empty()) { - mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, - &mLocked.animationResources, - mLocked.viewport.displayId); - } - fadeOutAndReleaseAllSpotsLocked(); - updatePointerLocked(); + if (presentation == Presentation::POINTER) { + mCursorController.getAdditionalMouseResources(); + clearSpotsLocked(); } } -void PointerController::setSpots(const PointerCoords* spotCoords, - const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId) { -#if DEBUG_POINTER_UPDATES - ALOGD("setSpots: idBits=%08x", spotIdBits.value); - for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.firstMarkedBit(); - idBits.clearBit(id); - const PointerCoords& c = spotCoords[spotIdToIndex[id]]; - ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id, - c.getAxisValue(AMOTION_EVENT_AXIS_X), - c.getAxisValue(AMOTION_EVENT_AXIS_Y), - c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - displayId); +void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits, int32_t displayId) { + std::scoped_lock lock(mLock); + auto it = mLocked.spotControllers.find(displayId); + if (it == mLocked.spotControllers.end()) { + mLocked.spotControllers.try_emplace(displayId, displayId, mContext); } -#endif - - AutoMutex _l(mLock); - if (!mLocked.viewport.isValid()) { - return; - } - - std::vector<Spot*> newSpots; - std::map<int32_t, std::vector<Spot*>>::const_iterator iter = - mLocked.spotsByDisplay.find(displayId); - if (iter != mLocked.spotsByDisplay.end()) { - newSpots = iter->second; - } - - mSpriteController->openTransaction(); - - // Add or move spots for fingers that are down. - for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - const PointerCoords& c = spotCoords[spotIdToIndex[id]]; - const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0 - ? mResources.spotTouch : mResources.spotHover; - float x = c.getAxisValue(AMOTION_EVENT_AXIS_X); - float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y); - - Spot* spot = getSpot(id, newSpots); - if (!spot) { - spot = createAndAddSpotLocked(id, newSpots); - } - - spot->updateSprite(&icon, x, y, displayId); - } - - // Remove spots for fingers that went up. - for (size_t i = 0; i < newSpots.size(); i++) { - Spot* spot = newSpots[i]; - if (spot->id != Spot::INVALID_ID - && !spotIdBits.hasBit(spot->id)) { - fadeOutAndReleaseSpotLocked(spot); - } - } - - mSpriteController->closeTransaction(); - mLocked.spotsByDisplay[displayId] = newSpots; + mLocked.spotControllers.at(displayId).setSpots(spotCoords, spotIdToIndex, spotIdBits); } void PointerController::clearSpots() { -#if DEBUG_POINTER_UPDATES - ALOGD("clearSpots"); -#endif + std::scoped_lock lock(mLock); + clearSpotsLocked(); +} - AutoMutex _l(mLock); - if (!mLocked.viewport.isValid()) { - return; +void PointerController::clearSpotsLocked() REQUIRES(mLock) { + for (auto& [displayID, spotController] : mLocked.spotControllers) { + spotController.clearSpots(); } - - fadeOutAndReleaseAllSpotsLocked(); } void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout) { - AutoMutex _l(mLock); - - if (mLocked.inactivityTimeout != inactivityTimeout) { - mLocked.inactivityTimeout = inactivityTimeout; - resetInactivityTimeoutLocked(); - } + mContext.setInactivityTimeout(inactivityTimeout); } void PointerController::reloadPointerResources() { - AutoMutex _l(mLock); - - loadResourcesLocked(); - updatePointerLocked(); -} + std::scoped_lock lock(mLock); -/** - * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation, - * so here we are getting the dimensions in the original, unrotated orientation (orientation 0). - */ -static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) { - width = viewport.deviceWidth; - height = viewport.deviceHeight; + for (auto& [displayID, spotController] : mLocked.spotControllers) { + spotController.reloadSpotResources(); + } - if (viewport.orientation == DISPLAY_ORIENTATION_90 - || viewport.orientation == DISPLAY_ORIENTATION_270) { - std::swap(width, height); + if (mCursorController.resourcesLoaded()) { + bool getAdditionalMouseResources = false; + if (mLocked.presentation == PointerController::Presentation::POINTER) { + getAdditionalMouseResources = true; + } + mCursorController.reloadPointerResources(getAdditionalMouseResources); } } void PointerController::setDisplayViewport(const DisplayViewport& viewport) { - AutoMutex _l(mLock); - if (viewport == mLocked.viewport) { - return; - } - - const DisplayViewport oldViewport = mLocked.viewport; - mLocked.viewport = viewport; - - int32_t oldDisplayWidth, oldDisplayHeight; - getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight); - int32_t newDisplayWidth, newDisplayHeight; - getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight); - - // Reset cursor position to center if size or display changed. - if (oldViewport.displayId != viewport.displayId - || oldDisplayWidth != newDisplayWidth - || oldDisplayHeight != newDisplayHeight) { - - float minX, minY, maxX, maxY; - if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { - mLocked.pointerX = (minX + maxX) * 0.5f; - mLocked.pointerY = (minY + maxY) * 0.5f; - // Reload icon resources for density may be changed. - loadResourcesLocked(); - } else { - mLocked.pointerX = 0; - mLocked.pointerY = 0; - } + std::scoped_lock lock(mLock); - fadeOutAndReleaseAllSpotsLocked(); - } else if (oldViewport.orientation != viewport.orientation) { - // Apply offsets to convert from the pixel top-left corner position to the pixel center. - // This creates an invariant frame of reference that we can easily rotate when - // taking into account that the pointer may be located at fractional pixel offsets. - float x = mLocked.pointerX + 0.5f; - float y = mLocked.pointerY + 0.5f; - float temp; - - // Undo the previous rotation. - switch (oldViewport.orientation) { - case DISPLAY_ORIENTATION_90: - temp = x; - x = oldViewport.deviceHeight - y; - y = temp; - break; - case DISPLAY_ORIENTATION_180: - x = oldViewport.deviceWidth - x; - y = oldViewport.deviceHeight - y; - break; - case DISPLAY_ORIENTATION_270: - temp = x; - x = y; - y = oldViewport.deviceWidth - temp; - break; - } - - // Perform the new rotation. - switch (viewport.orientation) { - case DISPLAY_ORIENTATION_90: - temp = x; - x = y; - y = viewport.deviceHeight - temp; - break; - case DISPLAY_ORIENTATION_180: - x = viewport.deviceWidth - x; - y = viewport.deviceHeight - y; - break; - case DISPLAY_ORIENTATION_270: - temp = x; - x = viewport.deviceWidth - y; - y = temp; - break; - } - - // Apply offsets to convert from the pixel center to the pixel top-left corner position - // and save the results. - mLocked.pointerX = x - 0.5f; - mLocked.pointerY = y - 0.5f; + bool getAdditionalMouseResources = false; + if (mLocked.presentation == PointerController::Presentation::POINTER) { + getAdditionalMouseResources = true; } - - updatePointerLocked(); + mCursorController.setDisplayViewport(viewport, getAdditionalMouseResources); } void PointerController::updatePointerIcon(int32_t iconId) { - AutoMutex _l(mLock); - if (mLocked.requestedPointerType != iconId) { - mLocked.requestedPointerType = iconId; - mLocked.presentationChanged = true; - updatePointerLocked(); - } + std::scoped_lock lock(mLock); + mCursorController.updatePointerIcon(iconId); } void PointerController::setCustomPointerIcon(const SpriteIcon& icon) { - AutoMutex _l(mLock); - - const int32_t iconId = mPolicy->getCustomPointerIconId(); - mLocked.additionalMouseResources[iconId] = icon; - mLocked.requestedPointerType = iconId; - mLocked.presentationChanged = true; - - updatePointerLocked(); -} - -void PointerController::handleMessage(const Message& message) { - switch (message.what) { - case MSG_INACTIVITY_TIMEOUT: - doInactivityTimeout(); - break; - } -} - -int PointerController::handleEvent(int /* fd */, int events, void* /* data */) { - if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { - ALOGE("Display event receiver pipe was closed or an error occurred. " - "events=0x%x", events); - return 0; // remove the callback - } - - if (!(events & Looper::EVENT_INPUT)) { - ALOGW("Received spurious callback for unhandled poll event. " - "events=0x%x", events); - return 1; // keep the callback - } - - bool gotVsync = false; - ssize_t n; - nsecs_t timestamp; - DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; - while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { - for (size_t i = 0; i < static_cast<size_t>(n); ++i) { - if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { - timestamp = buf[i].header.timestamp; - gotVsync = true; - } - } - } - if (gotVsync) { - doAnimate(timestamp); - } - return 1; // keep the callback -} - -void PointerController::doAnimate(nsecs_t timestamp) { - AutoMutex _l(mLock); - - mLocked.animationPending = false; - - bool keepFading = doFadingAnimationLocked(timestamp); - bool keepBitmapFlipping = doBitmapAnimationLocked(timestamp); - if (keepFading || keepBitmapFlipping) { - startAnimationLocked(); - } -} - -bool PointerController::doFadingAnimationLocked(nsecs_t timestamp) { - bool keepAnimating = false; - nsecs_t frameDelay = timestamp - mLocked.animationTime; - - // Animate pointer fade. - if (mLocked.pointerFadeDirection < 0) { - mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION; - if (mLocked.pointerAlpha <= 0.0f) { - mLocked.pointerAlpha = 0.0f; - mLocked.pointerFadeDirection = 0; - } else { - keepAnimating = true; - } - updatePointerLocked(); - } else if (mLocked.pointerFadeDirection > 0) { - mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION; - if (mLocked.pointerAlpha >= 1.0f) { - mLocked.pointerAlpha = 1.0f; - mLocked.pointerFadeDirection = 0; - } else { - keepAnimating = true; - } - updatePointerLocked(); - } - - // Animate spots that are fading out and being removed. - for(auto it = mLocked.spotsByDisplay.begin(); it != mLocked.spotsByDisplay.end();) { - std::vector<Spot*>& spots = it->second; - size_t numSpots = spots.size(); - for (size_t i = 0; i < numSpots;) { - Spot* spot = spots[i]; - if (spot->id == Spot::INVALID_ID) { - spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION; - if (spot->alpha <= 0) { - spots.erase(spots.begin() + i); - releaseSpotLocked(spot); - numSpots--; - continue; - } else { - spot->sprite->setAlpha(spot->alpha); - keepAnimating = true; - } - } - ++i; - } - - if (spots.size() == 0) { - it = mLocked.spotsByDisplay.erase(it); - } else { - ++it; - } - } - - return keepAnimating; -} - -bool PointerController::doBitmapAnimationLocked(nsecs_t timestamp) { - std::map<int32_t, PointerAnimation>::const_iterator iter = mLocked.animationResources.find( - mLocked.requestedPointerType); - if (iter == mLocked.animationResources.end()) { - return false; - } - - if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) { - mSpriteController->openTransaction(); - - int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame; - mLocked.animationFrameIndex += incr; - mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr; - while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) { - mLocked.animationFrameIndex -= iter->second.animationFrames.size(); - } - mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]); - - mSpriteController->closeTransaction(); - } - - // Keep animating. - return true; + std::scoped_lock lock(mLock); + mCursorController.setCustomPointerIcon(icon); } void PointerController::doInactivityTimeout() { - fade(TRANSITION_GRADUAL); -} - -void PointerController::startAnimationLocked() { - if (!mLocked.animationPending) { - mLocked.animationPending = true; - mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC); - mDisplayEventReceiver.requestNextVsync(); - } -} - -void PointerController::resetInactivityTimeoutLocked() { - mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); - - nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT - ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; - mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT); -} - -void PointerController::removeInactivityTimeoutLocked() { - mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); + fade(Transition::GRADUAL); } -void PointerController::updatePointerLocked() REQUIRES(mLock) { - if (!mLocked.viewport.isValid()) { - return; +void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports) { + std::unordered_set<int32_t> displayIdSet; + for (DisplayViewport viewport : viewports) { + displayIdSet.insert(viewport.displayId); } - mSpriteController->openTransaction(); - - mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER); - mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY); - mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId); - - if (mLocked.pointerAlpha > 0) { - mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha); - mLocked.pointerSprite->setVisible(true); - } else { - mLocked.pointerSprite->setVisible(false); - } - - if (mLocked.pointerIconChanged || mLocked.presentationChanged) { - if (mLocked.presentation == PRESENTATION_POINTER) { - if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) { - mLocked.pointerSprite->setIcon(mLocked.pointerIcon); - } else { - std::map<int32_t, SpriteIcon>::const_iterator iter = - mLocked.additionalMouseResources.find(mLocked.requestedPointerType); - if (iter != mLocked.additionalMouseResources.end()) { - std::map<int32_t, PointerAnimation>::const_iterator anim_iter = - mLocked.animationResources.find(mLocked.requestedPointerType); - if (anim_iter != mLocked.animationResources.end()) { - mLocked.animationFrameIndex = 0; - mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC); - startAnimationLocked(); - } - mLocked.pointerSprite->setIcon(iter->second); - } else { - ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerType); - mLocked.pointerSprite->setIcon(mLocked.pointerIcon); - } - } - } else { - mLocked.pointerSprite->setIcon(mResources.spotAnchor); - } - mLocked.pointerIconChanged = false; - mLocked.presentationChanged = false; - } - - mSpriteController->closeTransaction(); -} - -PointerController::Spot* PointerController::getSpot(uint32_t id, const std::vector<Spot*>& spots) { - for (size_t i = 0; i < spots.size(); i++) { - Spot* spot = spots[i]; - if (spot->id == id) { - return spot; - } - } - - return nullptr; -} - -PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id, - std::vector<Spot*>& spots) { - // Remove spots until we have fewer than MAX_SPOTS remaining. - while (spots.size() >= MAX_SPOTS) { - Spot* spot = removeFirstFadingSpotLocked(spots); - if (!spot) { - spot = spots[0]; - spots.erase(spots.begin()); - } - releaseSpotLocked(spot); - } - - // Obtain a sprite from the recycled pool. - sp<Sprite> sprite; - if (! mLocked.recycledSprites.empty()) { - sprite = mLocked.recycledSprites.back(); - mLocked.recycledSprites.pop_back(); - } else { - sprite = mSpriteController->createSprite(); - } - - // Return the new spot. - Spot* spot = new Spot(id, sprite); - spots.push_back(spot); - return spot; -} - -PointerController::Spot* PointerController::removeFirstFadingSpotLocked(std::vector<Spot*>& spots) { - for (size_t i = 0; i < spots.size(); i++) { - Spot* spot = spots[i]; - if (spot->id == Spot::INVALID_ID) { - spots.erase(spots.begin() + i); - return spot; - } - } - return NULL; -} - -void PointerController::releaseSpotLocked(Spot* spot) { - spot->sprite->clearIcon(); - - if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) { - mLocked.recycledSprites.push_back(spot->sprite); - } - - delete spot; -} - -void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) { - if (spot->id != Spot::INVALID_ID) { - spot->id = Spot::INVALID_ID; - startAnimationLocked(); - } -} - -void PointerController::fadeOutAndReleaseAllSpotsLocked() { - for (auto& it : mLocked.spotsByDisplay) { - const std::vector<Spot*>& spots = it.second; - size_t numSpots = spots.size(); - for (size_t i = 0; i < numSpots; i++) { - Spot* spot = spots[i]; - fadeOutAndReleaseSpotLocked(spot); - } - } -} - -void PointerController::loadResourcesLocked() REQUIRES(mLock) { - if (!mLocked.viewport.isValid()) { - return; - } - - mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId); - mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId); - - mLocked.additionalMouseResources.clear(); - mLocked.animationResources.clear(); - if (mLocked.presentation == PRESENTATION_POINTER) { - mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, - &mLocked.animationResources, mLocked.viewport.displayId); - } - - mLocked.pointerIconChanged = true; -} - - -// --- PointerController::Spot --- - -void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y, - int32_t displayId) { - sprite->setLayer(Sprite::BASE_LAYER_SPOT + id); - sprite->setAlpha(alpha); - sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale)); - sprite->setPosition(x, y); - sprite->setDisplayId(displayId); - this->x = x; - this->y = y; - - if (icon != lastIcon) { - lastIcon = icon; - if (icon) { - sprite->setIcon(*icon); - sprite->setVisible(true); + std::scoped_lock lock(mLock); + for (auto it = mLocked.spotControllers.begin(); it != mLocked.spotControllers.end();) { + int32_t displayID = it->first; + if (!displayIdSet.count(displayID)) { + /* + * Ensures that an in-progress animation won't dereference + * a null pointer to TouchSpotController. + */ + mContext.removeAnimationCallback(displayID); + it = mLocked.spotControllers.erase(it); } else { - sprite->setVisible(false); + ++it; } } } diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index ebc622bae302..827fcf1e1bc1 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -17,81 +17,40 @@ #ifndef _UI_POINTER_CONTROLLER_H #define _UI_POINTER_CONTROLLER_H -#include "SpriteController.h" - -#include <map> -#include <vector> - -#include <ui/DisplayInfo.h> +#include <PointerControllerInterface.h> +#include <gui/DisplayEventReceiver.h> #include <input/DisplayViewport.h> #include <input/Input.h> -#include <PointerControllerInterface.h> +#include <ui/DisplayInfo.h> #include <utils/BitSet.h> -#include <utils/RefBase.h> #include <utils/Looper.h> -#include <gui/DisplayEventReceiver.h> - -namespace android { - -/* - * Pointer resources. - */ -struct PointerResources { - SpriteIcon spotHover; - SpriteIcon spotTouch; - SpriteIcon spotAnchor; -}; - -struct PointerAnimation { - std::vector<SpriteIcon> animationFrames; - nsecs_t durationPerFrame; -}; +#include <utils/RefBase.h> -/* - * Pointer controller policy interface. - * - * The pointer controller policy is used by the pointer controller to interact with - * the Window Manager and other system components. - * - * The actual implementation is partially supported by callbacks into the DVM - * via JNI. This interface is also mocked in the unit tests. - */ -class PointerControllerPolicyInterface : public virtual RefBase { -protected: - PointerControllerPolicyInterface() { } - virtual ~PointerControllerPolicyInterface() { } +#include <map> +#include <memory> +#include <vector> -public: - virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) = 0; - virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) = 0; - virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources, - std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0; - virtual int32_t getDefaultPointerIconId() = 0; - virtual int32_t getCustomPointerIconId() = 0; -}; +#include "MouseCursorController.h" +#include "PointerControllerContext.h" +#include "SpriteController.h" +#include "TouchSpotController.h" +namespace android { /* * Tracks pointer movements and draws the pointer sprite to a surface. * * Handles pointer acceleration and animation. */ -class PointerController : public PointerControllerInterface, public MessageHandler, - public LooperCallback { -protected: - virtual ~PointerController(); - +class PointerController : public PointerControllerInterface { public: - enum InactivityTimeout { - INACTIVITY_TIMEOUT_NORMAL = 0, - INACTIVITY_TIMEOUT_SHORT = 1, - }; + static std::shared_ptr<PointerController> create( + const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, + const sp<SpriteController>& spriteController); - PointerController(const sp<PointerControllerPolicyInterface>& policy, - const sp<Looper>& looper, const sp<SpriteController>& spriteController); + virtual ~PointerController() = default; - virtual bool getBounds(float* outMinX, float* outMinY, - float* outMaxX, float* outMaxY) const; + virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; virtual void move(float deltaX, float deltaY); virtual void setButtonState(int32_t buttonState); virtual int32_t getButtonState() const; @@ -103,111 +62,36 @@ public: virtual void setDisplayViewport(const DisplayViewport& viewport); virtual void setPresentation(Presentation presentation); - virtual void setSpots(const PointerCoords* spotCoords, - const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId); + virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits, int32_t displayId); virtual void clearSpots(); void updatePointerIcon(int32_t iconId); void setCustomPointerIcon(const SpriteIcon& icon); void setInactivityTimeout(InactivityTimeout inactivityTimeout); + void doInactivityTimeout(); void reloadPointerResources(); + void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports); private: - static const size_t MAX_RECYCLED_SPRITES = 12; - static const size_t MAX_SPOTS = 12; - - enum { - MSG_INACTIVITY_TIMEOUT, - }; + friend PointerControllerContext::LooperCallback; + friend PointerControllerContext::MessageHandler; - struct Spot { - static const uint32_t INVALID_ID = 0xffffffff; + mutable std::mutex mLock; - uint32_t id; - sp<Sprite> sprite; - float alpha; - float scale; - float x, y; + PointerControllerContext mContext; - inline Spot(uint32_t id, const sp<Sprite>& sprite) - : id(id), sprite(sprite), alpha(1.0f), scale(1.0f), - x(0.0f), y(0.0f), lastIcon(NULL) { } - - void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId); - - private: - const SpriteIcon* lastIcon; - }; - - mutable Mutex mLock; - - sp<PointerControllerPolicyInterface> mPolicy; - sp<Looper> mLooper; - sp<SpriteController> mSpriteController; - sp<WeakMessageHandler> mHandler; - sp<LooperCallback> mCallback; - - DisplayEventReceiver mDisplayEventReceiver; - - PointerResources mResources; + MouseCursorController mCursorController; struct Locked { - bool animationPending; - nsecs_t animationTime; - - size_t animationFrameIndex; - nsecs_t lastFrameUpdatedTime; - - DisplayViewport viewport; - - InactivityTimeout inactivityTimeout; - Presentation presentation; - bool presentationChanged; - - int32_t pointerFadeDirection; - float pointerX; - float pointerY; - float pointerAlpha; - sp<Sprite> pointerSprite; - SpriteIcon pointerIcon; - bool pointerIconChanged; - - std::map<int32_t, SpriteIcon> additionalMouseResources; - std::map<int32_t, PointerAnimation> animationResources; - - int32_t requestedPointerType; - - int32_t buttonState; - std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay; - std::vector<sp<Sprite> > recycledSprites; + std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers; } mLocked GUARDED_BY(mLock); - bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; - void setPositionLocked(float x, float y); - - void handleMessage(const Message& message); - int handleEvent(int fd, int events, void* data); - void doAnimate(nsecs_t timestamp); - bool doFadingAnimationLocked(nsecs_t timestamp); - bool doBitmapAnimationLocked(nsecs_t timestamp); - void doInactivityTimeout(); - - void startAnimationLocked(); - - void resetInactivityTimeoutLocked(); - void removeInactivityTimeoutLocked(); - void updatePointerLocked(); - - Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots); - Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots); - Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots); - void releaseSpotLocked(Spot* spot); - void fadeOutAndReleaseSpotLocked(Spot* spot); - void fadeOutAndReleaseAllSpotsLocked(); - - void loadResourcesLocked(); + PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, + const sp<SpriteController>& spriteController); + void clearSpotsLocked(); }; } // namespace android diff --git a/libs/input/PointerControllerContext.cpp b/libs/input/PointerControllerContext.cpp new file mode 100644 index 000000000000..f30e8d8e33a5 --- /dev/null +++ b/libs/input/PointerControllerContext.cpp @@ -0,0 +1,228 @@ +/* + * 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. + */ + +#include "PointerControllerContext.h" +#include "PointerController.h" + +namespace { +// Time to wait before starting the fade when the pointer is inactive. +const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds +const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds + +// The number of events to be read at once for DisplayEventReceiver. +const int EVENT_BUFFER_SIZE = 100; +} // namespace + +namespace android { + +// --- PointerControllerContext --- + +PointerControllerContext::PointerControllerContext( + const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, + const sp<SpriteController>& spriteController, PointerController& controller) + : mPolicy(policy), + mLooper(looper), + mSpriteController(spriteController), + mHandler(new MessageHandler()), + mCallback(new LooperCallback()), + mController(controller), + mAnimator(*this) { + std::scoped_lock lock(mLock); + mLocked.inactivityTimeout = InactivityTimeout::NORMAL; +} + +PointerControllerContext::~PointerControllerContext() { + mLooper->removeMessages(mHandler); +} + +void PointerControllerContext::setInactivityTimeout(InactivityTimeout inactivityTimeout) { + std::scoped_lock lock(mLock); + + if (mLocked.inactivityTimeout != inactivityTimeout) { + mLocked.inactivityTimeout = inactivityTimeout; + resetInactivityTimeoutLocked(); + } +} + +void PointerControllerContext::resetInactivityTimeout() { + std::scoped_lock lock(mLock); + resetInactivityTimeoutLocked(); +} + +void PointerControllerContext::resetInactivityTimeoutLocked() REQUIRES(mLock) { + mLooper->removeMessages(mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT); + + nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT + ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT + : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; + mLooper->sendMessageDelayed(timeout, mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT); +} + +void PointerControllerContext::removeInactivityTimeout() { + std::scoped_lock lock(mLock); + mLooper->removeMessages(mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT); +} + +nsecs_t PointerControllerContext::getAnimationTime() REQUIRES(mAnimator.mLock) { + return mAnimator.getAnimationTimeLocked(); +} + +void PointerControllerContext::setHandlerController(std::shared_ptr<PointerController> controller) { + mHandler->pointerController = controller; +} + +void PointerControllerContext::setCallbackController( + std::shared_ptr<PointerController> controller) { + mCallback->pointerController = controller; +} + +sp<PointerControllerPolicyInterface> PointerControllerContext::getPolicy() { + return mPolicy; +} + +sp<SpriteController> PointerControllerContext::getSpriteController() { + return mSpriteController; +} + +void PointerControllerContext::handleDisplayEvents() { + mAnimator.handleVsyncEvents(); +} + +void PointerControllerContext::MessageHandler::handleMessage(const Message& message) { + std::shared_ptr<PointerController> controller = pointerController.lock(); + + if (controller == nullptr) { + ALOGE("PointerController instance was released before processing message: what=%d", + message.what); + return; + } + switch (message.what) { + case MSG_INACTIVITY_TIMEOUT: + controller->doInactivityTimeout(); + break; + } +} + +int PointerControllerContext::LooperCallback::handleEvent(int /* fd */, int events, + void* /* data */) { + std::shared_ptr<PointerController> controller = pointerController.lock(); + if (controller == nullptr) { + ALOGW("PointerController instance was released with pending callbacks. events=0x%x", + events); + return 0; // Remove the callback, the PointerController is gone anyways + } + if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { + ALOGE("Display event receiver pipe was closed or an error occurred. events=0x%x", events); + return 0; // remove the callback + } + + if (!(events & Looper::EVENT_INPUT)) { + ALOGW("Received spurious callback for unhandled poll event. events=0x%x", events); + return 1; // keep the callback + } + + controller->mContext.handleDisplayEvents(); + return 1; // keep the callback +} + +void PointerControllerContext::addAnimationCallback(int32_t displayId, + std::function<bool(nsecs_t)> callback) { + mAnimator.addCallback(displayId, callback); +} + +void PointerControllerContext::removeAnimationCallback(int32_t displayId) { + mAnimator.removeCallback(displayId); +} + +PointerControllerContext::PointerAnimator::PointerAnimator(PointerControllerContext& context) + : mContext(context) { + initializeDisplayEventReceiver(); +} + +void PointerControllerContext::PointerAnimator::initializeDisplayEventReceiver() { + if (mDisplayEventReceiver.initCheck() == NO_ERROR) { + mContext.mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, + Looper::EVENT_INPUT, mContext.mCallback, nullptr); + } else { + ALOGE("Failed to initialize DisplayEventReceiver."); + } +} + +void PointerControllerContext::PointerAnimator::addCallback(int32_t displayId, + std::function<bool(nsecs_t)> callback) { + std::scoped_lock lock(mLock); + mLocked.callbacks[displayId] = callback; + startAnimationLocked(); +} + +void PointerControllerContext::PointerAnimator::removeCallback(int32_t displayId) { + std::scoped_lock lock(mLock); + auto it = mLocked.callbacks.find(displayId); + if (it == mLocked.callbacks.end()) { + return; + } + mLocked.callbacks.erase(it); +} + +void PointerControllerContext::PointerAnimator::handleVsyncEvents() { + bool gotVsync = false; + ssize_t n; + nsecs_t timestamp; + DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; + while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { + for (size_t i = 0; i < static_cast<size_t>(n); ++i) { + if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { + timestamp = buf[i].header.timestamp; + gotVsync = true; + } + } + } + if (gotVsync) { + std::scoped_lock lock(mLock); + mLocked.animationPending = false; + handleCallbacksLocked(timestamp); + } +} + +nsecs_t PointerControllerContext::PointerAnimator::getAnimationTimeLocked() REQUIRES(mLock) { + return mLocked.animationTime; +} + +void PointerControllerContext::PointerAnimator::startAnimationLocked() REQUIRES(mLock) { + if (!mLocked.animationPending) { + mLocked.animationPending = true; + mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC); + mDisplayEventReceiver.requestNextVsync(); + } +} + +void PointerControllerContext::PointerAnimator::handleCallbacksLocked(nsecs_t timestamp) + REQUIRES(mLock) { + for (auto it = mLocked.callbacks.begin(); it != mLocked.callbacks.end();) { + bool keepCallback = it->second(timestamp); + if (!keepCallback) { + it = mLocked.callbacks.erase(it); + } else { + ++it; + } + } + + if (!mLocked.callbacks.empty()) { + startAnimationLocked(); + } +} + +} // namespace android diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h new file mode 100644 index 000000000000..98073fea323e --- /dev/null +++ b/libs/input/PointerControllerContext.h @@ -0,0 +1,183 @@ +/* + * 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 _UI_POINTER_CONTROLLER_CONTEXT_H +#define _UI_POINTER_CONTROLLER_CONTEXT_H + +#include <PointerControllerInterface.h> +#include <gui/DisplayEventReceiver.h> +#include <input/DisplayViewport.h> +#include <input/Input.h> +#include <ui/DisplayInfo.h> +#include <utils/BitSet.h> +#include <utils/Looper.h> +#include <utils/RefBase.h> + +#include <functional> +#include <map> +#include <memory> +#include <vector> + +#include "SpriteController.h" + +namespace android { + +class PointerController; +class MouseCursorController; +class TouchSpotController; + +/* + * Pointer resources. + */ +struct PointerResources { + SpriteIcon spotHover; + SpriteIcon spotTouch; + SpriteIcon spotAnchor; +}; + +struct PointerAnimation { + std::vector<SpriteIcon> animationFrames; + nsecs_t durationPerFrame; +}; + +enum class InactivityTimeout { + NORMAL = 0, + SHORT = 1, +}; + +/* + * Pointer controller policy interface. + * + * The pointer controller policy is used by the pointer controller to interact with + * the Window Manager and other system components. + * + * The actual implementation is partially supported by callbacks into the DVM + * via JNI. This interface is also mocked in the unit tests. + */ +class PointerControllerPolicyInterface : public virtual RefBase { +protected: + PointerControllerPolicyInterface() {} + virtual ~PointerControllerPolicyInterface() {} + +public: + virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) = 0; + virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) = 0; + virtual void loadAdditionalMouseResources( + std::map<int32_t, SpriteIcon>* outResources, + std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0; + virtual int32_t getDefaultPointerIconId() = 0; + virtual int32_t getCustomPointerIconId() = 0; +}; + +/* + * Contains logic and resources shared among PointerController, + * MouseCursorController, and TouchSpotController. + */ + +class PointerControllerContext { +public: + PointerControllerContext(const sp<PointerControllerPolicyInterface>& policy, + const sp<Looper>& looper, const sp<SpriteController>& spriteController, + PointerController& controller); + ~PointerControllerContext(); + + void removeInactivityTimeout(); + void resetInactivityTimeout(); + void startAnimation(); + void setInactivityTimeout(InactivityTimeout inactivityTimeout); + + nsecs_t getAnimationTime(); + + void clearSpotsByDisplay(int32_t displayId); + + void setHandlerController(std::shared_ptr<PointerController> controller); + void setCallbackController(std::shared_ptr<PointerController> controller); + + sp<PointerControllerPolicyInterface> getPolicy(); + sp<SpriteController> getSpriteController(); + + void handleDisplayEvents(); + + void addAnimationCallback(int32_t displayId, std::function<bool(nsecs_t)> callback); + void removeAnimationCallback(int32_t displayId); + + class MessageHandler : public virtual android::MessageHandler { + public: + enum { + MSG_INACTIVITY_TIMEOUT, + }; + + void handleMessage(const Message& message) override; + std::weak_ptr<PointerController> pointerController; + }; + + class LooperCallback : public virtual android::LooperCallback { + public: + int handleEvent(int fd, int events, void* data) override; + std::weak_ptr<PointerController> pointerController; + }; + +private: + class PointerAnimator { + public: + PointerAnimator(PointerControllerContext& context); + + void addCallback(int32_t displayId, std::function<bool(nsecs_t)> callback); + void removeCallback(int32_t displayId); + void handleVsyncEvents(); + nsecs_t getAnimationTimeLocked(); + + mutable std::mutex mLock; + + private: + struct Locked { + bool animationPending{false}; + nsecs_t animationTime{systemTime(SYSTEM_TIME_MONOTONIC)}; + + std::unordered_map<int32_t, std::function<bool(nsecs_t)>> callbacks; + } mLocked GUARDED_BY(mLock); + + DisplayEventReceiver mDisplayEventReceiver; + + PointerControllerContext& mContext; + + void initializeDisplayEventReceiver(); + void startAnimationLocked(); + void handleCallbacksLocked(nsecs_t timestamp); + }; + + sp<PointerControllerPolicyInterface> mPolicy; + sp<Looper> mLooper; + sp<SpriteController> mSpriteController; + sp<MessageHandler> mHandler; + sp<LooperCallback> mCallback; + + PointerController& mController; + + PointerAnimator mAnimator; + + mutable std::mutex mLock; + + struct Locked { + InactivityTimeout inactivityTimeout; + } mLocked GUARDED_BY(mLock); + + void resetInactivityTimeoutLocked(); +}; + +} // namespace android + +#endif // _UI_POINTER_CONTROLLER_CONTEXT_H diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index 804644c230b9..acd8bced0612 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -23,11 +23,6 @@ #include <utils/String8.h> #include <gui/Surface.h> -#include <android/graphics/bitmap.h> -#include <android/graphics/canvas.h> -#include <android/graphics/paint.h> -#include <android/native_window.h> - namespace android { // --- SpriteController --- @@ -130,8 +125,8 @@ void SpriteController::doUpdateSprites() { SpriteUpdate& update = updates.editItemAt(i); if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) { - update.state.surfaceWidth = update.state.icon.bitmap.getInfo().width; - update.state.surfaceHeight = update.state.icon.bitmap.getInfo().height; + update.state.surfaceWidth = update.state.icon.width(); + update.state.surfaceHeight = update.state.icon.height(); update.state.surfaceDrawn = false; update.state.surfaceVisible = false; update.state.surfaceControl = obtainSurface( @@ -152,8 +147,8 @@ void SpriteController::doUpdateSprites() { } if (update.state.wantSurfaceVisible()) { - int32_t desiredWidth = update.state.icon.bitmap.getInfo().width; - int32_t desiredHeight = update.state.icon.bitmap.getInfo().height; + int32_t desiredWidth = update.state.icon.width(); + int32_t desiredHeight = update.state.icon.height(); if (update.state.surfaceWidth < desiredWidth || update.state.surfaceHeight < desiredHeight) { needApplyTransaction = true; @@ -194,36 +189,9 @@ void SpriteController::doUpdateSprites() { if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn && update.state.wantSurfaceVisible()) { sp<Surface> surface = update.state.surfaceControl->getSurface(); - ANativeWindow_Buffer outBuffer; - status_t status = surface->lock(&outBuffer, NULL); - if (status) { - ALOGE("Error %d locking sprite surface before drawing.", status); - } else { - graphics::Paint paint; - paint.setBlendMode(ABLEND_MODE_SRC); - - graphics::Canvas canvas(outBuffer, (int32_t) surface->getBuffersDataSpace()); - canvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint); - - const int iconWidth = update.state.icon.bitmap.getInfo().width; - const int iconHeight = update.state.icon.bitmap.getInfo().height; - - if (outBuffer.width > iconWidth) { - paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent - canvas.drawRect({iconWidth, 0, outBuffer.width, iconHeight}, paint); - } - if (outBuffer.height > iconHeight) { - paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent - canvas.drawRect({0, iconHeight, outBuffer.width, outBuffer.height}, paint); - } - - status = surface->unlockAndPost(); - if (status) { - ALOGE("Error %d unlocking and posting sprite surface after drawing.", status); - } else { - update.state.surfaceDrawn = true; - update.surfaceChanged = surfaceChanged = true; - } + if (update.state.icon.draw(surface)) { + update.state.surfaceDrawn = true; + update.surfaceChanged = surfaceChanged = true; } } } diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h index 2513544d4bdf..137b5646feae 100644 --- a/libs/input/SpriteController.h +++ b/libs/input/SpriteController.h @@ -20,9 +20,10 @@ #include <utils/RefBase.h> #include <utils/Looper.h> -#include <android/graphics/bitmap.h> #include <gui/SurfaceComposerClient.h> +#include "SpriteIcon.h" + namespace android { /* @@ -51,35 +52,6 @@ struct SpriteTransformationMatrix { }; /* - * Icon that a sprite displays, including its hotspot. - */ -struct SpriteIcon { - inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { } - inline SpriteIcon(const graphics::Bitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) : - bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { } - - graphics::Bitmap bitmap; - int32_t style; - float hotSpotX; - float hotSpotY; - - inline SpriteIcon copy() const { - return SpriteIcon(bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888), style, hotSpotX, hotSpotY); - } - - inline void reset() { - bitmap.reset(); - style = 0; - hotSpotX = 0; - hotSpotY = 0; - } - - inline bool isValid() const { - return bitmap.isValid() && !bitmap.isEmpty(); - } -}; - -/* * A sprite is a simple graphical object that is displayed on-screen above other layers. * The basic sprite class is an interface. * The implementation is provided by the sprite controller. diff --git a/libs/input/SpriteIcon.cpp b/libs/input/SpriteIcon.cpp new file mode 100644 index 000000000000..b7e51e22a214 --- /dev/null +++ b/libs/input/SpriteIcon.cpp @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#include "SpriteIcon.h" + +#include <android/graphics/bitmap.h> +#include <android/graphics/canvas.h> +#include <android/graphics/paint.h> +#include <android/native_window.h> +#include <log/log.h> + +namespace android { + +bool SpriteIcon::draw(sp<Surface> surface) const { + ANativeWindow_Buffer outBuffer; + status_t status = surface->lock(&outBuffer, NULL); + if (status) { + ALOGE("Error %d locking sprite surface before drawing.", status); + return false; + } + + graphics::Paint paint; + paint.setBlendMode(ABLEND_MODE_SRC); + + graphics::Canvas canvas(outBuffer, (int32_t)surface->getBuffersDataSpace()); + canvas.drawBitmap(bitmap, 0, 0, &paint); + + const int iconWidth = width(); + const int iconHeight = height(); + + if (outBuffer.width > iconWidth) { + paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent + canvas.drawRect({iconWidth, 0, outBuffer.width, iconHeight}, paint); + } + if (outBuffer.height > iconHeight) { + paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent + canvas.drawRect({0, iconHeight, outBuffer.width, outBuffer.height}, paint); + } + + status = surface->unlockAndPost(); + if (status) { + ALOGE("Error %d unlocking and posting sprite surface after drawing.", status); + } + return !status; +} + +} // namespace android diff --git a/libs/input/SpriteIcon.h b/libs/input/SpriteIcon.h new file mode 100644 index 000000000000..a257d7e89ebc --- /dev/null +++ b/libs/input/SpriteIcon.h @@ -0,0 +1,61 @@ +/* + * 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 _UI_SPRITE_ICON_H +#define _UI_SPRITE_ICON_H + +#include <android/graphics/bitmap.h> +#include <gui/Surface.h> + +namespace android { + +/* + * Icon that a sprite displays, including its hotspot. + */ +struct SpriteIcon { + inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) {} + inline SpriteIcon(const graphics::Bitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) + : bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) {} + + graphics::Bitmap bitmap; + int32_t style; + float hotSpotX; + float hotSpotY; + + inline SpriteIcon copy() const { + return SpriteIcon(bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888), style, hotSpotX, hotSpotY); + } + + inline void reset() { + bitmap.reset(); + style = 0; + hotSpotX = 0; + hotSpotY = 0; + } + + inline bool isValid() const { return bitmap.isValid() && !bitmap.isEmpty(); } + + inline int32_t width() const { return bitmap.getInfo().width; } + inline int32_t height() const { return bitmap.getInfo().height; } + + // Draw the bitmap onto the given surface. Returns true if it's successful, or false otherwise. + // Note it doesn't set any metadata to the surface. + bool draw(const sp<Surface> surface) const; +}; + +} // namespace android + +#endif // _UI_SPRITE_ICON_H diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp new file mode 100644 index 000000000000..f7c685ff8ba6 --- /dev/null +++ b/libs/input/TouchSpotController.cpp @@ -0,0 +1,264 @@ +/* + * 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. + */ + +#define LOG_TAG "TouchSpotController" + +// Log debug messages about pointer updates +#define DEBUG_SPOT_UPDATES 0 + +#include "TouchSpotController.h" + +#include <log/log.h> + +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkColor.h> +#include <SkPaint.h> + +namespace { +// Time to spend fading out the spot completely. +const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms +} // namespace + +namespace android { + +// --- Spot --- + +void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, float y, + int32_t displayId) { + sprite->setLayer(Sprite::BASE_LAYER_SPOT + id); + sprite->setAlpha(alpha); + sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale)); + sprite->setPosition(x, y); + sprite->setDisplayId(displayId); + this->x = x; + this->y = y; + + if (icon != mLastIcon) { + mLastIcon = icon; + if (icon) { + sprite->setIcon(*icon); + sprite->setVisible(true); + } else { + sprite->setVisible(false); + } + } +} + +// --- TouchSpotController --- + +TouchSpotController::TouchSpotController(int32_t displayId, PointerControllerContext& context) + : mDisplayId(displayId), mContext(context) { + mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId); +} + +TouchSpotController::~TouchSpotController() { + std::scoped_lock lock(mLock); + + size_t numSpots = mLocked.displaySpots.size(); + for (size_t i = 0; i < numSpots; i++) { + delete mLocked.displaySpots[i]; + } + mLocked.displaySpots.clear(); +} + +void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits) { +#if DEBUG_SPOT_UPDATES + ALOGD("setSpots: idBits=%08x", spotIdBits.value); + for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + const PointerCoords& c = spotCoords[spotIdToIndex[id]]; + ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id, + c.getAxisValue(AMOTION_EVENT_AXIS_X), c.getAxisValue(AMOTION_EVENT_AXIS_Y), + c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), displayId); + } +#endif + + std::scoped_lock lock(mLock); + sp<SpriteController> spriteController = mContext.getSpriteController(); + spriteController->openTransaction(); + + // Add or move spots for fingers that are down. + for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const PointerCoords& c = spotCoords[spotIdToIndex[id]]; + const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0 + ? mResources.spotTouch + : mResources.spotHover; + float x = c.getAxisValue(AMOTION_EVENT_AXIS_X); + float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y); + + Spot* spot = getSpot(id, mLocked.displaySpots); + if (!spot) { + spot = createAndAddSpotLocked(id, mLocked.displaySpots); + } + + spot->updateSprite(&icon, x, y, mDisplayId); + } + + for (Spot* spot : mLocked.displaySpots) { + if (spot->id != Spot::INVALID_ID && !spotIdBits.hasBit(spot->id)) { + fadeOutAndReleaseSpotLocked(spot); + } + } + + spriteController->closeTransaction(); +} + +void TouchSpotController::clearSpots() { +#if DEBUG_SPOT_UPDATES + ALOGD("clearSpots"); +#endif + + std::scoped_lock lock(mLock); + fadeOutAndReleaseAllSpotsLocked(); +} + +TouchSpotController::Spot* TouchSpotController::getSpot(uint32_t id, + const std::vector<Spot*>& spots) { + for (size_t i = 0; i < spots.size(); i++) { + Spot* spot = spots[i]; + if (spot->id == id) { + return spot; + } + } + return nullptr; +} + +TouchSpotController::Spot* TouchSpotController::createAndAddSpotLocked(uint32_t id, + std::vector<Spot*>& spots) + REQUIRES(mLock) { + // Remove spots until we have fewer than MAX_SPOTS remaining. + while (spots.size() >= MAX_SPOTS) { + Spot* spot = removeFirstFadingSpotLocked(spots); + if (!spot) { + spot = spots[0]; + spots.erase(spots.begin()); + } + releaseSpotLocked(spot); + } + + // Obtain a sprite from the recycled pool. + sp<Sprite> sprite; + if (!mLocked.recycledSprites.empty()) { + sprite = mLocked.recycledSprites.back(); + mLocked.recycledSprites.pop_back(); + } else { + sprite = mContext.getSpriteController()->createSprite(); + } + + // Return the new spot. + Spot* spot = new Spot(id, sprite); + spots.push_back(spot); + return spot; +} + +TouchSpotController::Spot* TouchSpotController::removeFirstFadingSpotLocked( + std::vector<Spot*>& spots) REQUIRES(mLock) { + for (size_t i = 0; i < spots.size(); i++) { + Spot* spot = spots[i]; + if (spot->id == Spot::INVALID_ID) { + spots.erase(spots.begin() + i); + return spot; + } + } + return NULL; +} + +void TouchSpotController::releaseSpotLocked(Spot* spot) REQUIRES(mLock) { + spot->sprite->clearIcon(); + + if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) { + mLocked.recycledSprites.push_back(spot->sprite); + } + delete spot; +} + +void TouchSpotController::fadeOutAndReleaseSpotLocked(Spot* spot) REQUIRES(mLock) { + if (spot->id != Spot::INVALID_ID) { + spot->id = Spot::INVALID_ID; + startAnimationLocked(); + } +} + +void TouchSpotController::fadeOutAndReleaseAllSpotsLocked() REQUIRES(mLock) { + size_t numSpots = mLocked.displaySpots.size(); + for (size_t i = 0; i < numSpots; i++) { + Spot* spot = mLocked.displaySpots[i]; + fadeOutAndReleaseSpotLocked(spot); + } +} + +void TouchSpotController::reloadSpotResources() { + mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId); +} + +bool TouchSpotController::doAnimations(nsecs_t timestamp) { + std::scoped_lock lock(mLock); + bool keepAnimating = doFadingAnimationLocked(timestamp); + if (!keepAnimating) { + /* + * We know that this callback will be removed before another + * is added. mLock in PointerAnimator will not be released + * until after this is removed, and adding another callback + * requires that lock. Thus it's safe to set mLocked.animating + * here. + */ + mLocked.animating = false; + } + return keepAnimating; +} + +bool TouchSpotController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) { + bool keepAnimating = false; + nsecs_t animationTime = mContext.getAnimationTime(); + nsecs_t frameDelay = timestamp - animationTime; + size_t numSpots = mLocked.displaySpots.size(); + for (size_t i = 0; i < numSpots;) { + Spot* spot = mLocked.displaySpots[i]; + if (spot->id == Spot::INVALID_ID) { + spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION; + if (spot->alpha <= 0) { + mLocked.displaySpots.erase(mLocked.displaySpots.begin() + i); + releaseSpotLocked(spot); + numSpots--; + continue; + } else { + spot->sprite->setAlpha(spot->alpha); + keepAnimating = true; + } + } + ++i; + } + return keepAnimating; +} + +void TouchSpotController::startAnimationLocked() REQUIRES(mLock) { + using namespace std::placeholders; + + if (mLocked.animating) { + return; + } + mLocked.animating = true; + + std::function<bool(nsecs_t)> func = std::bind(&TouchSpotController::doAnimations, this, _1); + mContext.addAnimationCallback(mDisplayId, func); +} + +} // namespace android diff --git a/libs/input/TouchSpotController.h b/libs/input/TouchSpotController.h new file mode 100644 index 000000000000..703de3603f48 --- /dev/null +++ b/libs/input/TouchSpotController.h @@ -0,0 +1,97 @@ +/* + * 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 _UI_TOUCH_SPOT_CONTROLLER_H +#define _UI_TOUCH_SPOT_CONTROLLER_H + +#include <functional> + +#include "PointerControllerContext.h" + +namespace android { + +/* + * Helper class for PointerController that specifically handles + * touch spot resources and actions for a single display. + */ +class TouchSpotController { +public: + TouchSpotController(int32_t displayId, PointerControllerContext& context); + ~TouchSpotController(); + void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits); + void clearSpots(); + + void reloadSpotResources(); + bool doAnimations(nsecs_t timestamp); + +private: + struct Spot { + static const uint32_t INVALID_ID = 0xffffffff; + + uint32_t id; + sp<Sprite> sprite; + float alpha; + float scale; + float x, y; + + inline Spot(uint32_t id, const sp<Sprite>& sprite) + : id(id), + sprite(sprite), + alpha(1.0f), + scale(1.0f), + x(0.0f), + y(0.0f), + mLastIcon(nullptr) {} + + void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId); + + private: + const SpriteIcon* mLastIcon; + }; + + int32_t mDisplayId; + + mutable std::mutex mLock; + + PointerResources mResources; + + PointerControllerContext& mContext; + + static constexpr size_t MAX_RECYCLED_SPRITES = 12; + static constexpr size_t MAX_SPOTS = 12; + + struct Locked { + std::vector<Spot*> displaySpots; + std::vector<sp<Sprite>> recycledSprites; + + bool animating{false}; + + } mLocked GUARDED_BY(mLock); + + Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots); + Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots); + Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots); + void releaseSpotLocked(Spot* spot); + void fadeOutAndReleaseSpotLocked(Spot* spot); + void fadeOutAndReleaseAllSpotsLocked(); + bool doFadingAnimationLocked(nsecs_t timestamp); + void startAnimationLocked(); +}; + +} // namespace android + +#endif // _UI_TOUCH_SPOT_CONTROLLER_H diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 213b3adfb2a8..4eabfb238f4a 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + cc_test { name: "libinputservice_test", srcs: [ diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp index a15742671dc7..b67088a389b6 100644 --- a/libs/input/tests/PointerController_test.cpp +++ b/libs/input/tests/PointerController_test.cpp @@ -136,7 +136,7 @@ protected: sp<MockSprite> mPointerSprite; sp<MockPointerControllerPolicyInterface> mPolicy; sp<MockSpriteController> mSpriteController; - sp<PointerController> mPointerController; + std::shared_ptr<PointerController> mPointerController; private: void loopThread(); @@ -160,7 +160,7 @@ PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<Moc EXPECT_CALL(*mSpriteController, createSprite()) .WillOnce(Return(mPointerSprite)); - mPointerController = new PointerController(mPolicy, mLooper, mSpriteController); + mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController); } PointerControllerTest::~PointerControllerTest() { @@ -178,9 +178,6 @@ void PointerControllerTest::ensureDisplayViewportIsSet() { viewport.deviceWidth = 400; viewport.deviceHeight = 300; mPointerController->setDisplayViewport(viewport); - - // The first call to setDisplayViewport should trigger the loading of the necessary resources. - EXPECT_TRUE(mPolicy->allResourcesAreLoaded()); } void PointerControllerTest::loopThread() { @@ -193,7 +190,7 @@ void PointerControllerTest::loopThread() { TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) { ensureDisplayViewportIsSet(); - mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerController::Transition::IMMEDIATE); std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT); EXPECT_CALL(*mPointerSprite, setVisible(true)); @@ -208,7 +205,8 @@ TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) { TEST_F(PointerControllerTest, updatePointerIcon) { ensureDisplayViewportIsSet(); - mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + mPointerController->setPresentation(PointerController::Presentation::POINTER); + mPointerController->unfade(PointerController::Transition::IMMEDIATE); int32_t type = CURSOR_TYPE_ADDITIONAL; std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type); @@ -224,7 +222,7 @@ TEST_F(PointerControllerTest, updatePointerIcon) { TEST_F(PointerControllerTest, setCustomPointerIcon) { ensureDisplayViewportIsSet(); - mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerController::Transition::IMMEDIATE); int32_t style = CURSOR_TYPE_CUSTOM; float hotSpotX = 15; @@ -246,13 +244,11 @@ TEST_F(PointerControllerTest, setCustomPointerIcon) { } TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) { - mPointerController->setPresentation(PointerController::PRESENTATION_POINTER); - mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1); - mPointerController->clearSpots(); + mPointerController->setPresentation(PointerController::Presentation::POINTER); mPointerController->setPosition(1.0f, 1.0f); mPointerController->move(1.0f, 1.0f); - mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); - mPointerController->fade(PointerController::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerController::Transition::IMMEDIATE); + mPointerController->fade(PointerController::Transition::IMMEDIATE); EXPECT_TRUE(mPolicy->noResourcesAreLoaded()); diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp index d2b7d5c75d58..132d71eb6db8 100644 --- a/libs/protoutil/Android.bp +++ b/libs/protoutil/Android.bp @@ -12,6 +12,15 @@ // 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. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + cc_defaults { name: "libprotoutil_defaults", diff --git a/libs/services/Android.bp b/libs/services/Android.bp index 1e621079b532..bf2e764aae6a 100644 --- a/libs/services/Android.bp +++ b/libs/services/Android.bp @@ -14,6 +14,15 @@ // Provides C++ wrappers for system services. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + cc_library_shared { name: "libservices", srcs: [ diff --git a/libs/storage/Android.bp b/libs/storage/Android.bp index c19933e39c96..e8c6c071bc06 100644 --- a/libs/storage/Android.bp +++ b/libs/storage/Android.bp @@ -1,3 +1,20 @@ +package { + default_applicable_licenses: ["frameworks_base_libs_storage_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_libs_storage_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_library_static { name: "libstorage", diff --git a/libs/storage/OWNERS b/libs/storage/OWNERS new file mode 100644 index 000000000000..6f9dbea36b06 --- /dev/null +++ b/libs/storage/OWNERS @@ -0,0 +1 @@ +include /core/java/android/os/storage/OWNERS diff --git a/libs/tracingproxy/Android.bp b/libs/tracingproxy/Android.bp new file mode 100644 index 000000000000..7126bfac773d --- /dev/null +++ b/libs/tracingproxy/Android.bp @@ -0,0 +1,53 @@ +// 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. + +// Provides C++ wrappers for system services. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_base_license"], +} + +cc_library_shared { + name: "libtracingproxy", + + aidl: { + export_aidl_headers: true, + include_dirs: [ + "frameworks/base/core/java", + ], + }, + + srcs: [ + ":ITracingServiceProxy.aidl", + ], + + shared_libs: [ + "libbinder", + "libutils", + ], + + cflags: [ + "-Wall", + "-Werror", + "-Wunused", + "-Wunreachable-code", + ], +} diff --git a/libs/usb/Android.bp b/libs/usb/Android.bp index e752b55f5ef7..edc847487dcb 100644 --- a/libs/usb/Android.bp +++ b/libs/usb/Android.bp @@ -14,6 +14,16 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-GPL-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + java_sdk_library { name: "com.android.future.usb.accessory", srcs: ["src/**/*.java"], diff --git a/libs/usb/OWNERS b/libs/usb/OWNERS new file mode 100644 index 000000000000..f7b2a37a297a --- /dev/null +++ b/libs/usb/OWNERS @@ -0,0 +1 @@ +include /services/usb/OWNERS diff --git a/libs/usb/tests/AccessoryChat/Android.bp b/libs/usb/tests/AccessoryChat/Android.bp index 19ed3d3ef52e..72090fb692e0 100644 --- a/libs/usb/tests/AccessoryChat/Android.bp +++ b/libs/usb/tests/AccessoryChat/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "AccessoryChat", diff --git a/libs/usb/tests/AccessoryChat/accessorychat/Android.bp b/libs/usb/tests/AccessoryChat/accessorychat/Android.bp index 5613745e966b..108649aa92ae 100644 --- a/libs/usb/tests/AccessoryChat/accessorychat/Android.bp +++ b/libs/usb/tests/AccessoryChat/accessorychat/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + cc_binary { name: "accessorychat", host_supported: true, diff --git a/libs/usb/tests/accessorytest/Android.bp b/libs/usb/tests/accessorytest/Android.bp index c6340e32e60c..69761aeb46fe 100644 --- a/libs/usb/tests/accessorytest/Android.bp +++ b/libs/usb/tests/accessorytest/Android.bp @@ -1,3 +1,13 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-GPL-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + cc_binary_host { name: "accessorytest", |