diff options
Diffstat (limited to 'libs/androidfw')
57 files changed, 2436 insertions, 1061 deletions
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index eeaefc5b157c..f87f98a59a12 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -44,6 +44,7 @@ cc_library { "AttributeResolution.cpp", "ChunkIterator.cpp", "ConfigDescription.cpp", + "DynamicLibManager.cpp", "Idmap.cpp", "LoadedArsc.cpp", "Locale.cpp", @@ -67,7 +68,6 @@ cc_library { "BackupData.cpp", "BackupHelpers.cpp", "CursorWindow.cpp", - "DisplayEventDispatcher.cpp", ], shared_libs: [ "libziparchive", @@ -75,7 +75,6 @@ cc_library { "libbinder", "liblog", "libcutils", - "libgui", "libutils", "libz", ], @@ -166,7 +165,11 @@ cc_test { static_libs: common_test_libs + ["liblog", "libz"], }, }, - data: ["tests/data/**/*.apk"], + data: [ + "tests/data/**/*.apk", + "tests/data/**/*.arsc", + "tests/data/**/*.idmap", + ], test_suites: ["device-tests"], } @@ -188,4 +191,3 @@ cc_benchmark { shared_libs: common_test_libs, data: ["tests/data/**/*.apk"], } - diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 09705e1aae67..918e7af12d31 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -21,6 +21,7 @@ #include "android-base/errors.h" #include "android-base/file.h" #include "android-base/logging.h" +#include "android-base/stringprintf.h" #include "android-base/unique_fd.h" #include "android-base/utf8.h" #include "utils/Compat.h" @@ -40,23 +41,342 @@ using base::unique_fd; static const std::string kResourcesArsc("resources.arsc"); -ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle, - const std::string& path, - time_t last_mod_time) - : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time) { +ApkAssets::ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider, + std::string path, + time_t last_mod_time, + package_property_t property_flags) + : assets_provider_(std::move(assets_provider)), + path_(std::move(path)), + last_mod_time_(last_mod_time), + property_flags_(property_flags) { } -std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) { - return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/); +// Provides asset files from a zip file. +class ZipAssetsProvider : public AssetsProvider { + public: + ~ZipAssetsProvider() override = default; + + static std::unique_ptr<const AssetsProvider> Create(const std::string& path) { + ::ZipArchiveHandle unmanaged_handle; + const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle); + if (result != 0) { + LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); + ::CloseArchive(unmanaged_handle); + return {}; + } + + return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider(path, path, unmanaged_handle)); + } + + static std::unique_ptr<const AssetsProvider> Create( + unique_fd fd, const std::string& friendly_name, const off64_t offset = 0, + const off64_t length = ApkAssets::kUnknownLength) { + + ::ZipArchiveHandle unmanaged_handle; + const int32_t result = (length == ApkAssets::kUnknownLength) + ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle) + : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length, + offset); + + if (result != 0) { + LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset + << " and length " << length << ": " << ::ErrorCodeString(result); + ::CloseArchive(unmanaged_handle); + return {}; + } + + return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider({}, friendly_name, + unmanaged_handle)); + } + + // Iterate over all files and directories within the zip. The order of iteration is not + // guaranteed to be the same as the order of elements in the central directory but is stable for a + // given zip file. + bool ForEachFile(const std::string& root_path, + const std::function<void(const StringPiece&, FileType)>& f) const override { + // If this is a resource loader from an .arsc, there will be no zip handle + if (zip_handle_ == nullptr) { + return false; + } + + std::string root_path_full = root_path; + if (root_path_full.back() != '/') { + root_path_full += '/'; + } + + void* cookie; + if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) { + return false; + } + + std::string name; + ::ZipEntry entry{}; + + // We need to hold back directories because many paths will contain them and we want to only + // surface one. + std::set<std::string> dirs{}; + + int32_t result; + while ((result = ::Next(cookie, &entry, &name)) == 0) { + StringPiece full_file_path(name); + StringPiece leaf_file_path = full_file_path.substr(root_path_full.size()); + + if (!leaf_file_path.empty()) { + auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); + if (iter != leaf_file_path.end()) { + std::string dir = + leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string(); + dirs.insert(std::move(dir)); + } else { + f(leaf_file_path, kFileTypeRegular); + } + } + } + ::EndIteration(cookie); + + // Now present the unique directories. + for (const std::string& dir : dirs) { + f(dir, kFileTypeDirectory); + } + + // -1 is end of iteration, anything else is an error. + return result == -1; + } + + protected: + std::unique_ptr<Asset> OpenInternal( + const std::string& path, Asset::AccessMode mode, bool* file_exists) const override { + if (file_exists) { + *file_exists = false; + } + + ::ZipEntry entry; + int32_t result = ::FindEntry(zip_handle_.get(), path, &entry); + if (result != 0) { + return {}; + } + + if (file_exists) { + *file_exists = true; + } + + const int fd = ::GetFileDescriptor(zip_handle_.get()); + const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get()); + 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*/)) { + 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); + 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 {}; + } + } + + 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_ << "'"; + return {}; + } + return asset; + } + } + + private: + DISALLOW_COPY_AND_ASSIGN(ZipAssetsProvider); + + explicit ZipAssetsProvider(std::string path, + std::string friendly_name, + ZipArchiveHandle unmanaged_handle) + : zip_handle_(unmanaged_handle, ::CloseArchive), + path_(std::move(path)), + friendly_name_(std::move(friendly_name)) { } + + const char* GetPath() const { + return path_.empty() ? nullptr : path_.c_str(); + } + + using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>; + ZipArchivePtr zip_handle_; + std::string path_; + std::string friendly_name_; +}; + +class DirectoryAssetsProvider : AssetsProvider { + public: + ~DirectoryAssetsProvider() override = default; + + static std::unique_ptr<const AssetsProvider> Create(const std::string& path) { + struct stat sb{}; + const int result = stat(path.c_str(), &sb); + if (result == -1) { + LOG(ERROR) << "Failed to find directory '" << path << "'."; + return nullptr; + } + + if (!S_ISDIR(sb.st_mode)) { + LOG(ERROR) << "Path '" << path << "' is not a directory."; + return nullptr; + } + + return std::unique_ptr<AssetsProvider>(new DirectoryAssetsProvider(path)); + } + + protected: + std::unique_ptr<Asset> OpenInternal( + const std::string& path, Asset::AccessMode /* mode */, bool* file_exists) const override { + const std::string resolved_path = ResolvePath(path); + if (file_exists) { + struct stat sb{}; + const int result = stat(resolved_path.c_str(), &sb); + *file_exists = result != -1 && S_ISREG(sb.st_mode); + } + + return ApkAssets::CreateAssetFromFile(resolved_path); + } + + private: + DISALLOW_COPY_AND_ASSIGN(DirectoryAssetsProvider); + + explicit DirectoryAssetsProvider(std::string path) : path_(std::move(path)) { } + + inline std::string ResolvePath(const std::string& path) const { + return base::StringPrintf("%s%c%s", path_.c_str(), OS_PATH_SEPARATOR, path.c_str()); + } + + const std::string path_; +}; + +// AssetProvider implementation that does not provide any assets. Used for ApkAssets::LoadEmpty. +class EmptyAssetsProvider : public AssetsProvider { + public: + EmptyAssetsProvider() = default; + ~EmptyAssetsProvider() override = default; + + protected: + std::unique_ptr<Asset> OpenInternal(const std::string& /*path */, + Asset::AccessMode /* mode */, + bool* file_exists) const override { + if (file_exists) { + *file_exists = false; + } + return nullptr; + } + + private: + DISALLOW_COPY_AND_ASSIGN(EmptyAssetsProvider); +}; + +// AssetProvider implementation +class MultiAssetsProvider : public AssetsProvider { + public: + ~MultiAssetsProvider() override = default; + + static std::unique_ptr<const AssetsProvider> Create( + std::unique_ptr<const AssetsProvider> child, std::unique_ptr<const AssetsProvider> parent) { + CHECK(parent != nullptr) << "parent provider must not be null"; + return (!child) ? std::move(parent) + : std::unique_ptr<const AssetsProvider>(new MultiAssetsProvider( + std::move(child), std::move(parent))); + } + + bool ForEachFile(const std::string& root_path, + const std::function<void(const StringPiece&, FileType)>& f) const override { + // TODO: Only call the function once for files defined in the parent and child + return child_->ForEachFile(root_path, f) && parent_->ForEachFile(root_path, f); + } + + protected: + std::unique_ptr<Asset> OpenInternal( + const std::string& path, Asset::AccessMode mode, bool* file_exists) const override { + auto asset = child_->Open(path, mode, file_exists); + return (asset) ? std::move(asset) : parent_->Open(path, mode, file_exists); + } + + private: + DISALLOW_COPY_AND_ASSIGN(MultiAssetsProvider); + + MultiAssetsProvider(std::unique_ptr<const AssetsProvider> child, + std::unique_ptr<const AssetsProvider> parent) + : child_(std::move(child)), parent_(std::move(parent)) { } + + std::unique_ptr<const AssetsProvider> child_; + std::unique_ptr<const AssetsProvider> parent_; +}; + +// Opens the archive using the file path. Calling CloseArchive on the zip handle will close the +// file. +std::unique_ptr<const ApkAssets> ApkAssets::Load( + const std::string& path, const package_property_t flags, + std::unique_ptr<const AssetsProvider> override_asset) { + auto assets = ZipAssetsProvider::Create(path); + return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset)) + : nullptr; +} + +// Opens the archive using the file file descriptor with the specified file offset and read length. +// If the `assume_ownership` parameter is 'true' calling CloseArchive will close the file. +std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd( + unique_fd fd, const std::string& friendly_name, const package_property_t flags, + std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset, + const off64_t length) { + CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength; + CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is " + << kUnknownLength; + + auto assets = ZipAssetsProvider::Create(std::move(fd), friendly_name, offset, length); + return (assets) ? LoadImpl(std::move(assets), friendly_name, flags, std::move(override_asset)) + : nullptr; } -std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path, - bool system) { - return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, true /*load_as_shared_library*/); +std::unique_ptr<const ApkAssets> ApkAssets::LoadTable( + const std::string& path, const package_property_t flags, + std::unique_ptr<const AssetsProvider> override_asset) { + + auto assets = CreateAssetFromFile(path); + return (assets) ? LoadTableImpl(std::move(assets), path, flags, std::move(override_asset)) + : nullptr; +} + +std::unique_ptr<const ApkAssets> ApkAssets::LoadTableFromFd( + unique_fd fd, const std::string& friendly_name, const package_property_t flags, + std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset, + const off64_t length) { + + auto assets = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length); + return (assets) ? LoadTableImpl(std::move(assets), friendly_name, flags, + std::move(override_asset)) + : nullptr; } std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path, - bool system) { + const package_property_t flags) { + CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders"; std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path); if (idmap_asset == nullptr) { return {}; @@ -70,92 +390,115 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap LOG(ERROR) << "failed to load IDMAP " << idmap_path; return {}; } - auto apkPath = loaded_idmap->OverlayApkPath(); - return LoadImpl({} /*fd*/, apkPath, - std::move(idmap_asset), - std::move(loaded_idmap), - system, false /*load_as_shared_library*/); + + auto overlay_path = loaded_idmap->OverlayApkPath(); + auto assets = ZipAssetsProvider::Create(overlay_path); + return (assets) ? LoadImpl(std::move(assets), overlay_path, flags | PROPERTY_OVERLAY, + nullptr /* override_asset */, std::move(idmap_asset), + std::move(loaded_idmap)) + : nullptr; } -std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd, - const std::string& friendly_name, - bool system, bool force_shared_lib) { - return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/, - system, force_shared_lib); +std::unique_ptr<const ApkAssets> ApkAssets::LoadFromDir( + const std::string& path, const package_property_t flags, + std::unique_ptr<const AssetsProvider> override_asset) { + + auto assets = DirectoryAssetsProvider::Create(path); + return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset)) + : nullptr; +} + +std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty( + const package_property_t flags, std::unique_ptr<const AssetsProvider> override_asset) { + + auto assets = (override_asset) ? std::move(override_asset) + : std::unique_ptr<const AssetsProvider>(new EmptyAssetsProvider()); + std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(std::move(assets), "empty" /* path */, + -1 /* last_mod-time */, flags)); + loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); + // Need to force a move for mingw32. + return std::move(loaded_apk); } std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) { unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC)); - if (fd == -1) { + if (!fd.ok()) { LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno); return {}; } - const off64_t file_len = lseek64(fd, 0, SEEK_END); - if (file_len < 0) { - LOG(ERROR) << "Failed to get size of file '" << path << "': " << SystemErrorCodeToString(errno); - return {}; + return CreateAssetFromFd(std::move(fd), path.c_str()); +} + +std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd, + const char* path, + off64_t offset, + off64_t length) { + CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength; + CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is " + << kUnknownLength; + if (length == kUnknownLength) { + length = lseek64(fd, 0, SEEK_END); + if (length < 0) { + LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': " + << SystemErrorCodeToString(errno); + return {}; + } } std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>(); - if (!file_map->create(path.c_str(), fd, 0, static_cast<size_t>(file_len), true /*readOnly*/)) { - LOG(ERROR) << "Failed to mmap file '" << path << "': " << SystemErrorCodeToString(errno); + if (!file_map->create(path, fd, offset, static_cast<size_t>(length), true /*readOnly*/)) { + LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': " + << SystemErrorCodeToString(errno); return {}; } - return Asset::createFromUncompressedMap(std::move(file_map), Asset::AccessMode::ACCESS_RANDOM); + + // 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); } std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl( - unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset, - std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library) { - ::ZipArchiveHandle unmanaged_handle; - int32_t result; - if (fd >= 0) { - result = - ::OpenArchiveFd(fd.release(), path.c_str(), &unmanaged_handle, true /*assume_ownership*/); - } else { - result = ::OpenArchive(path.c_str(), &unmanaged_handle); - } + std::unique_ptr<const AssetsProvider> assets, const std::string& path, + package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets, + std::unique_ptr<Asset> idmap_asset, std::unique_ptr<const LoadedIdmap> idmap) { - if (result != 0) { - LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); - return {}; - } + const time_t last_mod_time = getFileModDate(path.c_str()); + + // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open. + bool resources_asset_exists = false; + auto resources_asset_ = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER, + &resources_asset_exists); - time_t last_mod_time = getFileModDate(path.c_str()); + assets = MultiAssetsProvider::Create(std::move(override_assets), std::move(assets)); // Wrap the handle in a unique_ptr so it gets automatically closed. - std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time)); + std::unique_ptr<ApkAssets> + loaded_apk(new ApkAssets(std::move(assets), path, last_mod_time, property_flags)); - // Find the resource table. - ::ZipEntry entry; - result = ::FindEntry(loaded_apk->zip_handle_.get(), kResourcesArsc, &entry); - if (result != 0) { - // There is no resources.arsc, so create an empty LoadedArsc and return. + if (!resources_asset_exists) { loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); return std::move(loaded_apk); } - if (entry.method == kCompressDeflated) { - LOG(WARNING) << kResourcesArsc << " in APK '" << path << "' is compressed."; - } - - // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open. - loaded_apk->resources_asset_ = loaded_apk->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER); - if (loaded_apk->resources_asset_ == nullptr) { + loaded_apk->resources_asset_ = std::move(resources_asset_); + if (!loaded_apk->resources_asset_) { LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'."; return {}; } // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid. 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()); - loaded_apk->loaded_arsc_ = - LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library); - if (loaded_apk->loaded_arsc_ == nullptr) { + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), + property_flags); + if (!loaded_apk->loaded_arsc_) { LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'."; return {}; } @@ -164,96 +507,38 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl( return std::move(loaded_apk); } -std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const { - CHECK(zip_handle_ != nullptr); - - ::ZipEntry entry; - int32_t result = ::FindEntry(zip_handle_.get(), path, &entry); - if (result != 0) { - return {}; - } - - if (entry.method == kCompressDeflated) { - std::unique_ptr<FileMap> map = util::make_unique<FileMap>(); - if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset, - entry.compressed_length, true /*readOnly*/)) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; - return {}; - } - - std::unique_ptr<Asset> asset = - Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode); - if (asset == nullptr) { - LOG(ERROR) << "Failed to decompress '" << path << "'."; - return {}; - } - return asset; - } else { - std::unique_ptr<FileMap> map = util::make_unique<FileMap>(); - if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset, - entry.uncompressed_length, true /*readOnly*/)) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; - return {}; - } - - std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), mode); - if (asset == nullptr) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; - return {}; - } - return asset; - } -} - -bool ApkAssets::ForEachFile(const std::string& root_path, - const std::function<void(const StringPiece&, FileType)>& f) const { - CHECK(zip_handle_ != nullptr); +std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl( + std::unique_ptr<Asset> resources_asset, const std::string& path, + package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets) { - std::string root_path_full = root_path; - if (root_path_full.back() != '/') { - root_path_full += '/'; - } + const time_t last_mod_time = getFileModDate(path.c_str()); - void* cookie; - if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) { - return false; - } + auto assets = (override_assets) ? std::move(override_assets) + : std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider()); - std::string name; - ::ZipEntry entry; - - // We need to hold back directories because many paths will contain them and we want to only - // surface one. - std::set<std::string> dirs; - - int32_t result; - while ((result = ::Next(cookie, &entry, &name)) == 0) { - StringPiece full_file_path(name); - StringPiece leaf_file_path = full_file_path.substr(root_path_full.size()); - - if (!leaf_file_path.empty()) { - auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); - if (iter != leaf_file_path.end()) { - std::string dir = - leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string(); - dirs.insert(std::move(dir)); - } else { - f(leaf_file_path, kFileTypeRegular); - } - } - } - ::EndIteration(cookie); + std::unique_ptr<ApkAssets> loaded_apk( + new ApkAssets(std::move(assets), path, last_mod_time, property_flags)); + loaded_apk->resources_asset_ = std::move(resources_asset); - // Now present the unique directories. - for (const std::string& dir : dirs) { - f(dir, kFileTypeDirectory); + const StringPiece data( + reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), + loaded_apk->resources_asset_->getLength()); + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags); + if (loaded_apk->loaded_arsc_ == nullptr) { + LOG(ERROR) << "Failed to load '" << kResourcesArsc << path; + return {}; } - // -1 is end of iteration, anything else is an error. - return result == -1; + // Need to force a move for mingw32. + return std::move(loaded_apk); } bool ApkAssets::IsUpToDate() const { + if (IsLoader()) { + // Loaders are invalidated by the app, not the system, so assume they are up to date. + return true; + } + return last_mod_time_ == getFileModDate(path_.c_str()); } diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index 9a95fdf80cb5..cd30c184d5a4 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -133,14 +133,24 @@ Asset::Asset(void) */ /*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode) { + return createFromFd(open(fileName, O_RDONLY | O_BINARY), fileName, mode); +} + +/* + * Create a new Asset from a file on disk. There is a fair chance that + * the file doesn't actually exist. + * + * We can use "mode" to decide how we want to go about it. + */ +/*static*/ Asset* Asset::createFromFd(const int fd, const char* fileName, AccessMode mode) +{ + if (fd < 0) { + return NULL; + } + _FileAsset* pAsset; status_t result; off64_t length; - int fd; - - fd = open(fileName, O_RDONLY | O_BINARY); - if (fd < 0) - return NULL; /* * Under Linux, the lseek fails if we actually opened a directory. To @@ -253,8 +263,10 @@ Asset::Asset(void) pAsset = new _FileAsset; result = pAsset->openChunk(NULL, fd, offset, length); - if (result != NO_ERROR) + if (result != NO_ERROR) { + delete pAsset; return NULL; + } pAsset->mAccessMode = mode; return pAsset; @@ -273,8 +285,10 @@ Asset::Asset(void) pAsset = new _CompressedAsset; result = pAsset->openChunk(fd, offset, compressionMethod, uncompressedLen, compressedLen); - if (result != NO_ERROR) + if (result != NO_ERROR) { + delete pAsset; return NULL; + } pAsset->mAccessMode = mode; return pAsset; @@ -284,14 +298,13 @@ Asset::Asset(void) /* * Create a new Asset from a memory mapping. */ -/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, - AccessMode mode) +/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, AccessMode mode) { _FileAsset* pAsset; status_t result; pAsset = new _FileAsset; - result = pAsset->openChunk(dataMap); + result = pAsset->openChunk(dataMap, base::unique_fd(-1)); if (result != NO_ERROR) { delete pAsset; return NULL; @@ -302,11 +315,11 @@ Asset::Asset(void) } /*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(std::unique_ptr<FileMap> dataMap, - AccessMode mode) + base::unique_fd fd, AccessMode mode) { std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>(); - status_t result = pAsset->openChunk(dataMap.get()); + status_t result = pAsset->openChunk(dataMap.get(), std::move(fd)); if (result != NO_ERROR) { return NULL; } @@ -328,8 +341,10 @@ Asset::Asset(void) pAsset = new _CompressedAsset; result = pAsset->openChunk(dataMap, uncompressedLen); - if (result != NO_ERROR) + if (result != NO_ERROR) { + delete pAsset; return NULL; + } pAsset->mAccessMode = mode; return pAsset; @@ -399,7 +414,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), mMap(NULL), mBuf(NULL) + : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mMap(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 @@ -469,7 +484,7 @@ 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) +status_t _FileAsset::openChunk(FileMap* dataMap, base::unique_fd fd) { assert(mFp == NULL); // no reopen assert(mMap == NULL); @@ -478,6 +493,7 @@ status_t _FileAsset::openChunk(FileMap* dataMap) mMap = dataMap; mStart = -1; // not used mLength = dataMap->getDataLength(); + mFd = std::move(fd); assert(mOffset == 0); return NO_ERROR; @@ -676,6 +692,17 @@ const void* _FileAsset::getBuffer(bool wordAligned) int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const { if (mMap != NULL) { + 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; + } const char* fname = mMap->getFileName(); if (fname == NULL) { fname = mFileName; diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index d20aecaaf0f6..f20e18453f8b 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -22,10 +22,12 @@ #include <iterator> #include <map> #include <set> -#include <sstream> #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "androidfw/DynamicLibManager.h" +#include "androidfw/ResourceUtils.h" +#include "androidfw/Util.h" #include "utils/ByteOrder.h" #include "utils/Trace.h" @@ -35,21 +37,13 @@ #endif #endif -#ifdef __ANDROID__ -#define ANDROID_LOG(x) LOG(x) -#else -#define ANDROID_LOG(x) std::stringstream() -#endif - -#include "androidfw/ResourceUtils.h" - namespace android { 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. - const ResTable_entry* entry; + ResTable_entry_handle entry; // The configuration for which the resulting entry was defined. This is already swapped to host // endianness. @@ -61,6 +55,9 @@ struct FindEntryResult { // The dynamic package ID map for the package from which this resource came from. const DynamicRefTable* dynamic_ref_table; + // The package name of the resource. + const std::string* package_name; + // The string pool reference to the type's name. This uses a different string pool than // the global string pool, but this is hidden from the caller. StringPoolRef type_string_ref; @@ -70,7 +67,12 @@ struct FindEntryResult { StringPoolRef entry_string_ref; }; -AssetManager2::AssetManager2() { +AssetManager2::AssetManager2() : dynamic_lib_manager_(std::make_unique<DynamicLibManager>()) { + memset(&configuration_, 0, sizeof(configuration_)); +} + +AssetManager2::AssetManager2(DynamicLibManager* dynamic_lib_manager) + : dynamic_lib_manager_(dynamic_lib_manager) { memset(&configuration_, 0, sizeof(configuration_)); } @@ -89,29 +91,84 @@ void AssetManager2::BuildDynamicRefTable() { package_groups_.clear(); package_ids_.fill(0xff); - // 0x01 is reserved for the android package. - int next_package_id = 0x02; - const size_t apk_assets_count = apk_assets_.size(); - for (size_t i = 0; i < apk_assets_count; i++) { - const LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc(); - - for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) { - // Get the package ID or assign one if a shared library. - int package_id; - if (package->IsDynamic()) { - package_id = next_package_id++; - } else { - package_id = package->GetPackageId(); + // Overlay resources are not directly referenced by an application so their resource ids + // can change throughout the application's lifetime. Assign overlay package ids last. + std::vector<const ApkAssets*> sorted_apk_assets(apk_assets_); + std::stable_partition(sorted_apk_assets.begin(), sorted_apk_assets.end(), [](const ApkAssets* a) { + return !a->IsOverlay(); + }); + + std::unordered_map<std::string, uint8_t> apk_assets_package_ids; + std::unordered_map<std::string, uint8_t> package_name_package_ids; + + // Assign stable package ids to application packages. + uint8_t next_available_package_id = 0U; + for (const auto& apk_assets : sorted_apk_assets) { + for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) { + uint8_t package_id = package->GetPackageId(); + if (package->IsOverlay()) { + package_id = GetDynamicLibManager()->FindUnassignedId(next_available_package_id); + next_available_package_id = package_id + 1; + } else if (package->IsDynamic()) { + package_id = GetDynamicLibManager()->GetAssignedId(package->GetPackageName()); } + // Map the path of the apk assets to the package id of its first loaded package. + apk_assets_package_ids[apk_assets->GetPath()] = package_id; + + // Map the package name of the package to the first loaded package with that package id. + package_name_package_ids[package->GetPackageName()] = package_id; + } + } + + const int apk_assets_count = apk_assets_.size(); + for (int i = 0; i < apk_assets_count; i++) { + const auto& apk_assets = apk_assets_[i]; + for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) { + const auto package_id_entry = package_name_package_ids.find(package->GetPackageName()); + CHECK(package_id_entry != package_name_package_ids.end()) + << "no package id assgined to package " << package->GetPackageName(); + const uint8_t package_id = package_id_entry->second; + // Add the mapping for package ID to index if not present. uint8_t idx = package_ids_[package_id]; if (idx == 0xff) { package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size()); package_groups_.push_back({}); - DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table; - ref_table.mAssignedPackageId = package_id; - ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f; + + if (apk_assets->IsOverlay()) { + // The target package must precede the overlay package in the apk assets paths in order + // to take effect. + const auto& loaded_idmap = apk_assets->GetLoadedIdmap(); + auto target_package_iter = apk_assets_package_ids.find(loaded_idmap->TargetApkPath()); + if (target_package_iter == apk_assets_package_ids.end()) { + LOG(INFO) << "failed to find target package for overlay " + << loaded_idmap->OverlayApkPath(); + } else { + const uint8_t target_package_id = target_package_iter->second; + const uint8_t target_idx = package_ids_[target_package_id]; + CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not" + << " have an assigned package group"; + + PackageGroup& target_package_group = package_groups_[target_idx]; + + // Create a special dynamic reference table for the overlay to rewrite references to + // overlay resources as references to the target resources they overlay. + auto overlay_table = std::make_shared<OverlayDynamicRefTable>( + loaded_idmap->GetOverlayDynamicRefTable(target_package_id)); + package_groups_.back().dynamic_ref_table = overlay_table; + + // Add the overlay resource map to the target package's set of overlays. + target_package_group.overlays_.push_back( + ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id, + overlay_table.get()), + static_cast<ApkAssetsCookie>(i)}); + } + } + + DynamicRefTable* ref_table = package_groups_.back().dynamic_ref_table.get(); + ref_table->mAssignedPackageId = package_id; + ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f; } PackageGroup* package_group = &package_groups_[idx]; @@ -122,7 +179,7 @@ void AssetManager2::BuildDynamicRefTable() { // Add the package name -> build time ID mappings. for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) { String16 package_name(entry.package_name.c_str(), entry.package_name.size()); - package_group->dynamic_ref_table.mEntries.replaceValueFor( + package_group->dynamic_ref_table->mEntries.replaceValueFor( package_name, static_cast<uint8_t>(entry.package_id)); } } @@ -133,8 +190,8 @@ void AssetManager2::BuildDynamicRefTable() { for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { - iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), - iter->dynamic_ref_table.mAssignedPackageId); + iter2->dynamic_ref_table->addMapping(String16(package_name.c_str(), package_name.size()), + iter->dynamic_ref_table->mAssignedPackageId); } } } @@ -167,13 +224,13 @@ void AssetManager2::DumpToLog() const { (loaded_package->IsDynamic() ? " dynamic" : "")); } LOG(INFO) << base::StringPrintf("PG (%02x): ", - package_group.dynamic_ref_table.mAssignedPackageId) + package_group.dynamic_ref_table->mAssignedPackageId) << list; for (size_t i = 0; i < 256; i++) { - if (package_group.dynamic_ref_table.mLookupTable[i] != 0) { + if (package_group.dynamic_ref_table->mLookupTable[i] != 0) { LOG(INFO) << base::StringPrintf(" e[0x%02x] -> 0x%02x", (uint8_t) i, - package_group.dynamic_ref_table.mLookupTable[i]); + package_group.dynamic_ref_table->mLookupTable[i]); } } } @@ -195,14 +252,15 @@ const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t pack if (idx == 0xff) { return nullptr; } - return &package_groups_[idx].dynamic_ref_table; + return package_groups_[idx].dynamic_ref_table.get(); } -const DynamicRefTable* AssetManager2::GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const { +std::shared_ptr<const DynamicRefTable> AssetManager2::GetDynamicRefTableForCookie( + ApkAssetsCookie cookie) const { for (const PackageGroup& package_group : package_groups_) { for (const ApkAssetsCookie& package_cookie : package_group.cookies_) { if (package_cookie == cookie) { - return &package_group.dynamic_ref_table; + return package_group.dynamic_ref_table; } } } @@ -230,6 +288,62 @@ const std::unordered_map<std::string, std::string>* return &loaded_package->GetOverlayableMap(); } +bool AssetManager2::GetOverlayablesToString(const android::StringPiece& package_name, + std::string* out) const { + uint8_t package_id = 0U; + for (const auto& apk_assets : apk_assets_) { + const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc(); + if (loaded_arsc == nullptr) { + continue; + } + + const auto& loaded_packages = loaded_arsc->GetPackages(); + if (loaded_packages.empty()) { + continue; + } + + const auto& loaded_package = loaded_packages[0]; + if (loaded_package->GetPackageName() == package_name) { + package_id = GetAssignedPackageId(loaded_package.get()); + break; + } + } + + if (package_id == 0U) { + ANDROID_LOG(ERROR) << base::StringPrintf("No package with name '%s", package_name.data()); + return false; + } + + const size_t idx = package_ids_[package_id]; + if (idx == 0xff) { + return false; + } + + std::string output; + for (const ConfiguredPackage& package : package_groups_[idx].packages_) { + const LoadedPackage* loaded_package = package.loaded_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)) { + ANDROID_LOG(ERROR) << base::StringPrintf( + "Unable to retrieve name of overlayable resource 0x%08x", *it); + return false; + } + + 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)); + } + } + } + + *out = std::move(output); + return true; +} + void AssetManager2::SetConfiguration(const ResTable_config& configuration) { const int diff = configuration_.diff(configuration); configuration_ = configuration; @@ -240,21 +354,45 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { } } +std::set<std::string> AssetManager2::GetNonSystemOverlayPaths() const { + std::set<std::string> non_system_overlays; + for (const PackageGroup& package_group : package_groups_) { + bool found_system_package = false; + for (const ConfiguredPackage& package : package_group.packages_) { + if (package.loaded_package_->IsSystem()) { + found_system_package = true; + break; + } + } + + if (!found_system_package) { + for (const ConfiguredOverlay& overlay : package_group.overlays_) { + non_system_overlays.insert(apk_assets_[overlay.cookie]->GetPath()); + } + } + } + + return non_system_overlays; +} + std::set<ResTable_config> 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>(); + std::set<ResTable_config> configurations; for (const PackageGroup& package_group : package_groups_) { - bool found_system_package = false; - for (const ConfiguredPackage& package : package_group.packages_) { + for (size_t i = 0; i < package_group.packages_.size(); i++) { + const ConfiguredPackage& package = package_group.packages_[i]; if (exclude_system && package.loaded_package_->IsSystem()) { - found_system_package = true; continue; } - if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) { - // Overlays must appear after the target package to take effect. Any overlay found in the - // same package as a system package is able to overlay system resources. + auto apk_assets = apk_assets_[package_group.cookies_[i]]; + if (exclude_system && apk_assets->IsOverlay() + && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) { + // Exclude overlays that target system resources. continue; } @@ -268,17 +406,20 @@ std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system, bool merge_equivalent_languages) const { ATRACE_NAME("AssetManager::GetResourceLocales"); std::set<std::string> locales; + const auto non_system_overlays = + (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>(); + for (const PackageGroup& package_group : package_groups_) { - bool found_system_package = false; - for (const ConfiguredPackage& package : package_group.packages_) { + for (size_t i = 0; i < package_group.packages_.size(); i++) { + const ConfiguredPackage& package = package_group.packages_[i]; if (exclude_system && package.loaded_package_->IsSystem()) { - found_system_package = true; continue; } - if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) { - // Overlays must appear after the target package to take effect. Any overlay found in the - // same package as a system package is able to overlay system resources. + auto apk_assets = apk_assets_[package_group.cookies_[i]]; + if (exclude_system && apk_assets->IsOverlay() + && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) { + // Exclude overlays that target system resources. continue; } @@ -322,7 +463,7 @@ std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) con files->add(info); }; - if (!apk_assets->ForEachFile(full_path, func)) { + if (!apk_assets->GetAssetsProvider()->ForEachFile(full_path, func)) { return {}; } } @@ -346,7 +487,7 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, continue; } - std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode); + std::unique_ptr<Asset> asset = apk_assets_[i]->GetAssetsProvider()->Open(filename, mode); if (asset) { if (out_cookie != nullptr) { *out_cookie = i; @@ -367,13 +508,19 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) { return {}; } - return apk_assets_[cookie]->Open(filename, mode); + 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_) { + // Clear the last logged resource resolution. + ResetResourceResolution(); + last_resolution_.resid = resid; + } + // Might use this if density_override != 0. ResTable_config density_override_config; @@ -385,6 +532,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri desired_config = &density_override_config; } + // Retrieve the package group from the package id of the resource id. if (!is_valid_resid(resid)) { LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); return kInvalidCookie; @@ -393,8 +541,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri 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); - - const uint8_t package_idx = package_ids_[package_id]; + uint8_t package_idx = package_ids_[package_id]; if (package_idx == 0xff) { ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); @@ -402,8 +549,71 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri } const PackageGroup& package_group = package_groups_[package_idx]; - const size_t package_count = package_group.packages_.size(); + 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; + } + + if (!apk_assets_[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()) { + // 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; + 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)) { + continue; + } + + if (!overlay_result.config.isBetterThan(out_entry->config, desired_config) + && overlay_result.config.compare(out_entry->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_) { + last_resolution_.steps.push_back( + 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; + } + + return cookie; +} + +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 { ApkAssetsCookie best_cookie = kInvalidCookie; const LoadedPackage* best_package = nullptr; const ResTable_type* best_type = nullptr; @@ -412,13 +622,14 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri uint32_t best_offset = 0u; uint32_t type_flags = 0u; - Resolution::Step::Type resolution_type; + Resolution::Step::Type 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 // and we don't need to match the configurations, since they already matched. - const bool use_fast_path = !ignore_configuration && desired_config == &configuration_; + const bool use_fast_path = !ignore_configuration && &desired_config == &configuration_; + const size_t package_count = package_group.packages_.size(); for (size_t pi = 0; pi < package_count; pi++) { const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; @@ -431,20 +642,10 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri continue; } - uint16_t local_entry_idx = entry_idx; - - // If there is an IDMAP supplied with this package, translate the entry ID. - if (type_spec->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - continue; - } - } - - type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); - - // If the package is an overlay, then even configurations that are the same MUST be chosen. - const bool package_is_overlay = loaded_package->IsOverlay(); + // 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]; @@ -457,19 +658,37 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // configurations that do NOT match have been filtered-out. if (best_config == nullptr) { resolution_type = Resolution::Step::Type::INITIAL; - } else if (this_config.isBetterThan(*best_config, desired_config)) { - resolution_type = Resolution::Step::Type::BETTER_MATCH; - } else if (package_is_overlay && this_config.compare(*best_config) == 0) { - resolution_type = Resolution::Step::Type::OVERLAID; + } else if (this_config.isBetterThan(*best_config, &desired_config)) { + resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER + : Resolution::Step::Type::BETTER_MATCH; + } else if (package_is_loader && this_config.compare(*best_config) == 0) { + resolution_type = Resolution::Step::Type::OVERLAID_LOADER; } else { + if (resource_resolution_logging_enabled_) { + resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER + : Resolution::Step::Type::SKIPPED; + resolution_steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); + } continue; } // 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, local_entry_idx); + const uint32_t offset = LoadedPackage::GetEntryOffset(type, entry_idx); if (offset == ResTable_type::NO_ENTRY) { + if (resource_resolution_logging_enabled_) { + if (package_is_loader) { + resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER; + } else { + resolution_type = Resolution::Step::Type::NO_ENTRY; + } + resolution_steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); + } continue; } @@ -480,9 +699,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri best_offset = offset; if (resource_resolution_logging_enabled_) { - resolution_steps.push_back(Resolution::Step{resolution_type, - this_config.toString(), - &loaded_package->GetPackageName()}); + last_resolution_.steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); } } } else { @@ -497,16 +716,17 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri if (!ignore_configuration) { this_config.copyFromDtoH((*iter)->config); - if (!this_config.match(*desired_config)) { + if (!this_config.match(desired_config)) { continue; } if (best_config == nullptr) { resolution_type = Resolution::Step::Type::INITIAL; - } else if (this_config.isBetterThan(*best_config, desired_config)) { - resolution_type = Resolution::Step::Type::BETTER_MATCH; - } else if (package_is_overlay && this_config.compare(*best_config) == 0) { - resolution_type = Resolution::Step::Type::OVERLAID; + } else if (this_config.isBetterThan(*best_config, &desired_config)) { + resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER + : Resolution::Step::Type::BETTER_MATCH; + } else if (package_is_loader && this_config.compare(*best_config) == 0) { + resolution_type = Resolution::Step::Type::OVERLAID_LOADER; } else { continue; } @@ -514,7 +734,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // 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, local_entry_idx); + const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, entry_idx); if (offset == ResTable_type::NO_ENTRY) { continue; } @@ -532,9 +752,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri } if (resource_resolution_logging_enabled_) { - resolution_steps.push_back(Resolution::Step{resolution_type, - this_config.toString(), - &loaded_package->GetPackageName()}); + last_resolution_.steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); } } } @@ -549,38 +769,30 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri return kInvalidCookie; } - out_entry->entry = best_entry; + 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; - - if (resource_resolution_logging_enabled_) { - last_resolution.resid = resid; - last_resolution.cookie = best_cookie; - last_resolution.steps = resolution_steps; - - // Cache only the type/entry refs since that's all that's needed to build name - last_resolution.type_string_ref = - StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); - last_resolution.entry_string_ref = - StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); - } + StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); + out_entry->dynamic_ref_table = package_group.dynamic_ref_table.get(); return best_cookie; } +void AssetManager2::ResetResourceResolution() const { + last_resolution_.cookie = kInvalidCookie; + last_resolution_.resid = 0; + last_resolution_.steps.clear(); + last_resolution_.type_string_ref = StringPoolRef(); + last_resolution_.entry_string_ref = StringPoolRef(); +} + void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) { resource_resolution_logging_enabled_ = enabled; - if (!enabled) { - last_resolution.cookie = kInvalidCookie; - last_resolution.resid = 0; - last_resolution.steps.clear(); - last_resolution.type_string_ref = StringPoolRef(); - last_resolution.entry_string_ref = StringPoolRef(); + ResetResourceResolution(); } } @@ -590,24 +802,24 @@ std::string AssetManager2::GetLastResourceResolution() const { return std::string(); } - auto cookie = last_resolution.cookie; + auto cookie = last_resolution_.cookie; if (cookie == kInvalidCookie) { LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path."; return std::string(); } - uint32_t resid = last_resolution.resid; - std::vector<Resolution::Step>& steps = last_resolution.steps; + 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)); + apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); if (package != nullptr) { - ToResourceName(last_resolution.type_string_ref, - last_resolution.entry_string_ref, + ToResourceName(last_resolution_.type_string_ref, + last_resolution_.entry_string_ref, package->GetPackageName(), &resource_name); resource_name_string = ToFormattedResourceString(&resource_name); @@ -628,9 +840,27 @@ std::string AssetManager2::GetLastResourceResolution() const { case Resolution::Step::Type::BETTER_MATCH: prefix = "Found better"; break; + case Resolution::Step::Type::BETTER_MATCH_LOADER: + prefix = "Found better in loader"; + break; case Resolution::Step::Type::OVERLAID: prefix = "Overlaid"; break; + case Resolution::Step::Type::OVERLAID_LOADER: + prefix = "Overlaid by loader"; + break; + case Resolution::Step::Type::SKIPPED: + prefix = "Skipped"; + break; + case Resolution::Step::Type::SKIPPED_LOADER: + prefix = "Skipped loader"; + break; + case Resolution::Step::Type::NO_ENTRY: + prefix = "No entry"; + break; + case Resolution::Step::Type::NO_ENTRY_LOADER: + prefix = "No entry for loader"; + break; } if (!prefix.empty()) { @@ -654,25 +884,9 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return false; } - const uint8_t package_idx = package_ids_[get_package_id(resid)]; - if (package_idx == 0xff) { - LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", - get_package_id(resid), resid); - return false; - } - - const PackageGroup& package_group = package_groups_[package_idx]; - auto cookie_iter = std::find(package_group.cookies_.begin(), - package_group.cookies_.end(), cookie); - if (cookie_iter == package_group.cookies_.end()) { - return false; - } - - long package_pos = std::distance(package_group.cookies_.begin(), cookie_iter); - const LoadedPackage* package = package_group.packages_[package_pos].loaded_package_; return ToResourceName(entry.type_string_ref, entry.entry_string_ref, - package->GetPackageName(), + *entry.package_name, out_name); } @@ -699,7 +913,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, return kInvalidCookie; } - if (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) { + const ResTable_entry* table_entry = *entry.entry; + if (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) { if (!may_be_bag) { LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid); return kInvalidCookie; @@ -714,7 +929,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, } const Res_value* device_value = reinterpret_cast<const Res_value*>( - reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size)); + 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. @@ -777,6 +992,11 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { return bag; } +static bool compare_bag_entries(const ResolvedBag::Entry& entry1, + const ResolvedBag::Entry& entry2) { + return entry1.key < entry2.key; +} + 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()) { @@ -795,13 +1015,14 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& // 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. - if (dtohs(entry.entry->size) < sizeof(ResTable_map_entry) || - (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) { + 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) { // Not a bag, nothing to do. return nullptr; } - const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry.entry); + 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); @@ -811,13 +1032,15 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& child_resids.push_back(resid); uint32_t parent_resid = dtohl(map->parent.ident); - if (parent_resid == 0 || std::find(child_resids.begin(), child_resids.end(), parent_resid) + if (parent_resid == 0U || std::find(child_resids.begin(), child_resids.end(), parent_resid) != child_resids.end()) { - // There is no parent or that a circular dependency exist, meaning there is nothing to - // inherit and we can do a simple copy of the entries in the map. + // 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. 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) { uint32_t new_key = dtohl(map_entry->name.ident); @@ -843,8 +1066,15 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& new_entry->value.data, new_key); return nullptr; } + 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); + } + new_bag->type_spec_flags = entry.type_flags; new_bag->entry_count = static_cast<uint32_t>(entry_count); ResolvedBag* result = new_bag.get(); @@ -875,6 +1105,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& 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) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { @@ -907,6 +1138,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& memcpy(new_entry, parent_entry, sizeof(*new_entry)); } + sort_entries = sort_entries || + (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key)); if (child_key >= parent_entry->key) { // Move to the next parent entry if we used it or it was overridden. ++parent_entry; @@ -937,6 +1170,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& new_entry->value.dataType, new_entry->value.data, new_key); return nullptr; } + sort_entries = sort_entries || + (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key)); ++map_entry; ++new_entry; } @@ -956,6 +1191,10 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry))))); } + if (sort_entries) { + std::sort(new_bag->entries, new_bag->entries + actual_count, compare_bag_entries); + } + // Combine flags from the parent and our own bag. new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags; new_bag->entry_count = static_cast<uint32_t>(actual_count); @@ -1026,7 +1265,7 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, } if (resid != 0u) { - return fix_package_id(resid, package_group.dynamic_ref_table.mAssignedPackageId); + return fix_package_id(resid, package_group.dynamic_ref_table->mAssignedPackageId); } } } @@ -1079,17 +1318,27 @@ void AssetManager2::InvalidateCaches(uint32_t diff) { } } -uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) { +uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const { for (auto& package_group : package_groups_) { for (auto& package2 : package_group.packages_) { if (package2.loaded_package_ == package) { - return package_group.dynamic_ref_table.mAssignedPackageId; + return package_group.dynamic_ref_table->mAssignedPackageId; } } } return 0; } +DynamicLibManager* AssetManager2::GetDynamicLibManager() const { + auto dynamic_lib_manager = + std::get_if<std::unique_ptr<DynamicLibManager>>(&dynamic_lib_manager_); + if (dynamic_lib_manager) { + return (*dynamic_lib_manager).get(); + } else { + return *std::get_if<DynamicLibManager*>(&dynamic_lib_manager_); + } +} + std::unique_ptr<Theme> AssetManager2::NewTheme() { return std::unique_ptr<Theme>(new Theme(this)); } diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp index e1067fcd4d3d..6f05cbd0ebb3 100644 --- a/libs/androidfw/CursorWindow.cpp +++ b/libs/androidfw/CursorWindow.cpp @@ -69,7 +69,7 @@ status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** o result = window->clear(); if (!result) { LOG_WINDOW("Created new CursorWindow: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%d, mData=%p", + "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", window->mHeader->freeOffset, window->mHeader->numRows, window->mHeader->numColumns, @@ -124,7 +124,7 @@ status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursor CursorWindow* window = new CursorWindow(name, dupAshmemFd, data, size, true /*readOnly*/); LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%d, mData=%p", + "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", window->mHeader->freeOffset, window->mHeader->numRows, window->mHeader->numColumns, @@ -200,7 +200,7 @@ status_t CursorWindow::allocRow() { FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset)); memset(fieldDir, 0, fieldDirSize); - LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", + LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %zu bytes at offset %u\n", mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset); rowSlot->offset = fieldDirOffset; return OK; diff --git a/libs/androidfw/DisplayEventDispatcher.cpp b/libs/androidfw/DisplayEventDispatcher.cpp deleted file mode 100644 index d8a3f42690f4..000000000000 --- a/libs/androidfw/DisplayEventDispatcher.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2015 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 "DisplayEventDispatcher" - -#include <cinttypes> -#include <cstdint> - -#include <androidfw/DisplayEventDispatcher.h> -#include <gui/DisplayEventReceiver.h> -#include <utils/Log.h> -#include <utils/Looper.h> - -#include <utils/Timers.h> - -namespace android { - -// Number of events to read at a time from the DisplayEventDispatcher pipe. -// The value should be large enough that we can quickly drain the pipe -// using just a few large reads. -static const size_t EVENT_BUFFER_SIZE = 100; - -DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper, - ISurfaceComposer::VsyncSource vsyncSource, - ISurfaceComposer::ConfigChanged configChanged) : - mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) { - ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this); -} - -status_t DisplayEventDispatcher::initialize() { - status_t result = mReceiver.initCheck(); - if (result) { - ALOGW("Failed to initialize display event receiver, status=%d", result); - return result; - } - - int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, - this, NULL); - if (rc < 0) { - return UNKNOWN_ERROR; - } - return OK; -} - -void DisplayEventDispatcher::dispose() { - ALOGV("dispatcher %p ~ Disposing display event dispatcher.", this); - - if (!mReceiver.initCheck()) { - mLooper->removeFd(mReceiver.getFd()); - } -} - -status_t DisplayEventDispatcher::scheduleVsync() { - if (!mWaitingForVsync) { - ALOGV("dispatcher %p ~ Scheduling vsync.", this); - - // Drain all pending events. - nsecs_t vsyncTimestamp; - PhysicalDisplayId vsyncDisplayId; - uint32_t vsyncCount; - if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { - ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", - this, ns2ms(static_cast<nsecs_t>(vsyncTimestamp))); - } - - status_t status = mReceiver.requestNextVsync(); - if (status) { - ALOGW("Failed to request next vsync, status=%d", status); - return status; - } - - mWaitingForVsync = true; - } - return OK; -} - -int DisplayEventDispatcher::handleEvent(int, int events, void*) { - 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 - } - - // Drain all pending events, keep the last vsync. - nsecs_t vsyncTimestamp; - PhysicalDisplayId vsyncDisplayId; - uint32_t vsyncCount; - if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { - ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%" - ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d", - this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount); - mWaitingForVsync = false; - dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount); - } - - return 1; // keep the callback -} - -bool DisplayEventDispatcher::processPendingEvents( - nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount) { - bool gotVsync = false; - DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; - ssize_t n; - while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { - ALOGV("dispatcher %p ~ Read %d events.", this, int(n)); - for (ssize_t i = 0; i < n; i++) { - const DisplayEventReceiver::Event& ev = buf[i]; - switch (ev.header.type) { - case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: - // Later vsync events will just overwrite the info from earlier - // ones. That's fine, we only care about the most recent. - gotVsync = true; - *outTimestamp = ev.header.timestamp; - *outDisplayId = ev.header.displayId; - *outCount = ev.vsync.count; - break; - case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: - dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected); - break; - case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: - dispatchConfigChanged(ev.header.timestamp, ev.header.displayId, ev.config.configId); - break; - default: - ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type); - break; - } - } - } - if (n < 0) { - ALOGW("Failed to get events from display event dispatcher, status=%d", status_t(n)); - } - return gotVsync; -} -} diff --git a/libs/androidfw/DynamicLibManager.cpp b/libs/androidfw/DynamicLibManager.cpp new file mode 100644 index 000000000000..895b7695bf26 --- /dev/null +++ b/libs/androidfw/DynamicLibManager.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "androidfw/DynamicLibManager.h" + +namespace android { + +uint8_t DynamicLibManager::GetAssignedId(const std::string& library_package_name) { + auto lib_entry = shared_lib_package_ids_.find(library_package_name); + if (lib_entry != shared_lib_package_ids_.end()) { + return lib_entry->second; + } + + return shared_lib_package_ids_[library_package_name] = next_package_id_++; +} + +uint8_t DynamicLibManager::FindUnassignedId(uint8_t start_package_id) { + return (start_package_id < next_package_id_) ? next_package_id_ : start_package_id; +} + +} // namespace android diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index 7c1ee5cd7cfa..0b2fd9ec982d 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -20,6 +20,8 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "androidfw/ResourceTypes.h" +#include "androidfw/Util.h" #include "utils/ByteOrder.h" #include "utils/Trace.h" @@ -29,40 +31,132 @@ #endif #endif -#include "androidfw/ResourceTypes.h" - using ::android::base::StringPrintf; namespace android { -constexpr static inline bool is_valid_package_id(uint16_t id) { - return id != 0 && id <= 255; +static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) { + return dtohl(e1.target_id) < target_id; } -constexpr static inline bool is_valid_type_id(uint16_t id) { - // Type IDs and package IDs have the same constraints in the IDMAP. - return is_valid_package_id(id); +static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) { + return dtohl(e1.overlay_id) < overlay_id; } -bool LoadedIdmap::Lookup(const IdmapEntry_header* header, uint16_t input_entry_id, - uint16_t* output_entry_id) { - if (input_entry_id < dtohs(header->entry_id_offset)) { - // After applying the offset, the entry is not present. - return false; +size_t Idmap_header::Size() const { + return sizeof(Idmap_header) + sizeof(uint8_t) * dtohl(debug_info_size); +} + +OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap) + : data_header_(loaded_idmap->data_header_), + idmap_string_pool_(loaded_idmap->string_pool_.get()) { }; + +OverlayStringPool::~OverlayStringPool() { + uninit(); +} + +const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) 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); } - input_entry_id -= dtohs(header->entry_id_offset); - if (input_entry_id >= dtohs(header->entry_count)) { - // The entry is not present. - return false; + return ResStringPool::stringAt(idx, outLen); +} + +const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) 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); } - uint32_t result = dtohl(header->entries[input_entry_id]); - if (result == 0xffffffffu) { - return false; + return ResStringPool::string8At(idx, outLen); +} + +size_t OverlayStringPool::size() const { + return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U); +} + +OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header, + const Idmap_overlay_entry* entries, + uint8_t target_assigned_package_id) + : data_header_(data_header), + entries_(entries), + target_assigned_package_id_(target_assigned_package_id) { }; + +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); + + if (entry == end_entry || dtohl(entry->overlay_id) != *resId) { + // A mapping for the target resource id could not be found. + return DynamicRefTable::lookupResourceId(resId); } - *output_entry_id = static_cast<uint16_t>(result); - return true; + + *resId = (0x00FFFFFFU & dtohl(entry->target_id)) + | (((uint32_t) target_assigned_package_id_) << 24); + return NO_ERROR; +} + +status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const { + return DynamicRefTable::lookupResourceId(resId); +} + +IdmapResMap::IdmapResMap(const Idmap_data_header* data_header, + const Idmap_target_entry* entries, + uint8_t target_assigned_package_id, + const OverlayDynamicRefTable* overlay_ref_table) + : data_header_(data_header), + entries_(entries), + target_assigned_package_id_(target_assigned_package_id), + 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_) { + // 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); + + // 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)); } static bool is_word_aligned(const void* data) { @@ -95,24 +189,27 @@ static bool IsValidIdmapHeader(const StringPiece& data) { return false; } - if (!is_valid_package_id(dtohs(header->target_package_id))) { - LOG(ERROR) << StringPrintf("Target package ID in Idmap is invalid: 0x%02x", - dtohs(header->target_package_id)); - return false; - } - - if (dtohs(header->type_count) > 255) { - LOG(ERROR) << StringPrintf("Idmap has too many type mappings (was %d, max 255)", - (int)dtohs(header->type_count)); - return false; - } return true; } -LoadedIdmap::LoadedIdmap(const Idmap_header* header) : header_(header) { +LoadedIdmap::LoadedIdmap(const Idmap_header* header, + const Idmap_data_header* data_header, + const Idmap_target_entry* target_entries, + const Idmap_overlay_entry* overlay_entries, + ResStringPool* string_pool) + : header_(header), + data_header_(data_header), + target_entries_(target_entries), + overlay_entries_(overlay_entries), + string_pool_(string_pool) { + size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path), arraysize(header_->overlay_path)); overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length); + + length = strnlen(reinterpret_cast<const char*>(header_->target_path), + arraysize(header_->target_path)); + target_apk_path_.assign(reinterpret_cast<const char*>(header_->target_path), length); } std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_data) { @@ -121,70 +218,67 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_da return {}; } - const Idmap_header* header = reinterpret_cast<const Idmap_header*>(idmap_data.data()); - - // Can't use make_unique because LoadedImpl constructor is private. - std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(new LoadedIdmap(header)); + auto header = reinterpret_cast<const Idmap_header*>(idmap_data.data()); + const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + header->Size(); + size_t data_size = idmap_data.size() - header->Size(); + + // Currently idmap2 can only generate one data block. + auto data_header = reinterpret_cast<const Idmap_data_header*>(data_ptr); + data_ptr += sizeof(*data_header); + data_size -= sizeof(*data_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))) { + LOG(ERROR) << StringPrintf("Idmap too small for the number of target entries (%d)", + (int)dtohl(data_header->target_entry_count)); + return {}; + } - const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + sizeof(*header); - size_t data_size = idmap_data.size() - sizeof(*header); + // Advance the data pointer past the target entries. + const size_t target_entry_size_bytes = + (dtohl(data_header->target_entry_count) * sizeof(Idmap_target_entry)); + data_ptr += target_entry_size_bytes; + data_size -= target_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) < + static_cast<size_t>(dtohl(data_header->overlay_entry_count))) { + LOG(ERROR) << StringPrintf("Idmap too small for the number of overlay entries (%d)", + (int)dtohl(data_header->overlay_entry_count)); + return {}; + } - size_t type_maps_encountered = 0u; - while (data_size >= sizeof(IdmapEntry_header)) { - if (!is_word_aligned(data_ptr)) { - LOG(ERROR) << "Type mapping in Idmap is not word aligned"; - return {}; - } + // Advance the data pointer past the target 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; - // Validate the type IDs. - const IdmapEntry_header* entry_header = reinterpret_cast<const IdmapEntry_header*>(data_ptr); - if (!is_valid_type_id(dtohs(entry_header->target_type_id)) || !is_valid_type_id(dtohs(entry_header->overlay_type_id))) { - LOG(ERROR) << StringPrintf("Invalid type map (0x%02x -> 0x%02x)", - dtohs(entry_header->target_type_id), - dtohs(entry_header->overlay_type_id)); - return {}; - } + // Read the idmap string pool that holds the value of inline string entries. + if (data_size < dtohl(data_header->string_pool_length)) { + LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)", + (int)dtohl(data_header->string_pool_length)); + return {}; + } - // Make sure there is enough space for the entries declared in the header. - if ((data_size - sizeof(*entry_header)) / sizeof(uint32_t) < - static_cast<size_t>(dtohs(entry_header->entry_count))) { - LOG(ERROR) << StringPrintf("Idmap too small for the number of entries (%d)", - (int)dtohs(entry_header->entry_count)); + 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 (err != NO_ERROR) { + LOG(ERROR) << "idmap string pool corrupt."; return {}; } - - // Only add a non-empty overlay. - if (dtohs(entry_header->entry_count != 0)) { - loaded_idmap->type_map_[static_cast<uint8_t>(dtohs(entry_header->overlay_type_id))] = - entry_header; - } - - const size_t entry_size_bytes = - sizeof(*entry_header) + (dtohs(entry_header->entry_count) * sizeof(uint32_t)); - data_ptr += entry_size_bytes; - data_size -= entry_size_bytes; - type_maps_encountered++; } - // Verify that we parsed all the type maps. - if (type_maps_encountered != static_cast<size_t>(dtohs(header->type_count))) { - LOG(ERROR) << "Parsed " << type_maps_encountered << " type maps but expected " - << (int)dtohs(header->type_count); - return {}; - } - return std::move(loaded_idmap); -} + // Can't use make_unique because LoadedIdmap constructor is private. + std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>( + new LoadedIdmap(header, data_header, target_entries, overlay_entries, + idmap_string_pool.release())); -uint8_t LoadedIdmap::TargetPackageId() const { - return static_cast<uint8_t>(dtohs(header_->target_package_id)); -} - -const IdmapEntry_header* LoadedIdmap::GetEntryMapForType(uint8_t type_id) const { - auto iter = type_map_.find(type_id); - if (iter != type_map_.end()) { - return iter->second; - } - return nullptr; + return std::move(loaded_idmap); } } // namespace android diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 72873abc6a42..70bb441f94cb 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -51,9 +51,8 @@ namespace { // the Type structs. class TypeSpecPtrBuilder { public: - explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header, - const IdmapEntry_header* idmap_header) - : header_(header), idmap_header_(idmap_header) { + explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header) + : header_(header) { } void AddType(const ResTable_type* type) { @@ -70,7 +69,6 @@ class TypeSpecPtrBuilder { TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); type_spec->type_spec = header_; - type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); return TypeSpecPtr(type_spec); @@ -80,7 +78,6 @@ class TypeSpecPtrBuilder { DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder); const ResTable_typeSpec* header_; - const IdmapEntry_header* idmap_header_; std::vector<const ResTable_type*> types_; }; @@ -400,8 +397,7 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { } std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, - const LoadedIdmap* loaded_idmap, - bool system, bool load_as_shared_library) { + package_property_t property_flags) { ATRACE_NAME("LoadedPackage::Load"); std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage()); @@ -415,19 +411,24 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, return {}; } - loaded_package->system_ = system; + if ((property_flags & PROPERTY_SYSTEM) != 0) { + loaded_package->property_flags_ |= PROPERTY_SYSTEM; + } - loaded_package->package_id_ = dtohl(header->id); - if (loaded_package->package_id_ == 0 || - (loaded_package->package_id_ == kAppPackageId && load_as_shared_library)) { - // Package ID of 0 means this is a shared library. - loaded_package->dynamic_ = true; + if ((property_flags & PROPERTY_LOADER) != 0) { + loaded_package->property_flags_ |= PROPERTY_LOADER; } - if (loaded_idmap != nullptr) { - // This is an overlay and so it needs to pretend to be the target package. - loaded_package->package_id_ = loaded_idmap->TargetPackageId(); - loaded_package->overlay_ = true; + if ((property_flags & PROPERTY_OVERLAY) != 0) { + // Overlay resources must have an exclusive resource id space for referencing internal + // resources. + loaded_package->property_flags_ |= PROPERTY_OVERLAY | PROPERTY_DYNAMIC; + } + + loaded_package->package_id_ = dtohl(header->id); + if (loaded_package->package_id_ == 0 || + (loaded_package->package_id_ == kAppPackageId && (property_flags & PROPERTY_DYNAMIC) != 0)) { + loaded_package->property_flags_ |= PROPERTY_DYNAMIC; } if (header->header.headerSize >= sizeof(ResTable_package)) { @@ -511,16 +512,9 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, return {}; } - // If this is an overlay, associate the mapping of this type to the target type - // from the IDMAP. - const IdmapEntry_header* idmap_entry_header = nullptr; - if (loaded_idmap != nullptr) { - idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id); - } - 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, idmap_entry_header); + builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec); loaded_package->resource_ids_.set(type_spec->id, entry_count); } else { LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x", @@ -681,28 +675,24 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, return {}; } - // We only add the type to the package if there is no IDMAP, or if the type is - // overlaying something. - if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { - // If this is an overlay, insert it at the target type ID. - if (type_spec_ptr->idmap_entries != nullptr) { - type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; - } - loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr); - } + loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr); } return std::move(loaded_package); } bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, - bool load_as_shared_library) { + package_property_t property_flags) { const ResTable_header* header = chunk.header<ResTable_header>(); if (header == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE too small."; return false; } + if (loaded_idmap != nullptr) { + global_string_pool_ = util::make_unique<OverlayStringPool>(loaded_idmap); + } + const size_t package_count = dtohl(header->packageCount); size_t packages_seen = 0; @@ -714,9 +704,9 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, switch (child_chunk.type()) { case RES_STRING_POOL_TYPE: // Only use the first string pool. Ignore others. - if (global_string_pool_.getError() == NO_INIT) { - status_t err = global_string_pool_.setTo(child_chunk.header<ResStringPool_header>(), - child_chunk.size()); + if (global_string_pool_->getError() == NO_INIT) { + status_t err = global_string_pool_->setTo(child_chunk.header<ResStringPool_header>(), + child_chunk.size()); if (err != NO_ERROR) { LOG(ERROR) << "RES_STRING_POOL_TYPE corrupt."; return false; @@ -735,7 +725,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, packages_seen++; std::unique_ptr<const LoadedPackage> loaded_package = - LoadedPackage::Load(child_chunk, loaded_idmap, system_, load_as_shared_library); + LoadedPackage::Load(child_chunk, property_flags); if (!loaded_package) { return false; } @@ -758,20 +748,19 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, } std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data, - const LoadedIdmap* loaded_idmap, bool system, - bool load_as_shared_library) { - ATRACE_NAME("LoadedArsc::LoadTable"); + const LoadedIdmap* loaded_idmap, + const package_property_t property_flags) { + ATRACE_NAME("LoadedArsc::Load"); // Not using make_unique because the constructor is private. std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc()); - loaded_arsc->system_ = system; ChunkIterator iter(data.data(), data.size()); while (iter.HasNext()) { const Chunk chunk = iter.Next(); switch (chunk.type()) { case RES_TABLE_TYPE: - if (!loaded_arsc->LoadTable(chunk, loaded_idmap, load_as_shared_library)) { + if (!loaded_arsc->LoadTable(chunk, loaded_idmap, property_flags)) { return {}; } break; diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 66df59b4c83b..4d7e5dfea4f7 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -1063,6 +1063,11 @@ size_t ResStringPool::bytes() const return (mError == NO_ERROR) ? mHeader->header.size : 0; } +const void* ResStringPool::data() const +{ + return mHeader; +} + bool ResStringPool::isSorted() const { return (mHeader->flags&ResStringPool_header::SORTED_FLAG)!=0; @@ -1358,11 +1363,10 @@ int32_t ResXMLParser::getAttributeData(size_t idx) const (((const uint8_t*)tag) + dtohs(tag->attributeStart) + (dtohs(tag->attributeSize)*idx)); - if (attr->typedValue.dataType != Res_value::TYPE_DYNAMIC_REFERENCE || - mTree.mDynamicRefTable == NULL) { + if (mTree.mDynamicRefTable == NULL || + !mTree.mDynamicRefTable->requiresLookup(&attr->typedValue)) { return dtohl(attr->typedValue.data); } - uint32_t data = dtohl(attr->typedValue.data); if (mTree.mDynamicRefTable->lookupResourceId(&data) == NO_ERROR) { return data; @@ -1608,10 +1612,9 @@ uint32_t ResXMLParser::getSourceResourceId() const static volatile int32_t gCount = 0; -ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable) +ResXMLTree::ResXMLTree(std::shared_ptr<const DynamicRefTable> dynamicRefTable) : ResXMLParser(*this) - , mDynamicRefTable((dynamicRefTable != nullptr) ? dynamicRefTable->clone() - : std::unique_ptr<DynamicRefTable>(nullptr)) + , mDynamicRefTable(std::move(dynamicRefTable)) , mError(NO_INIT), mOwnedData(NULL) { if (kDebugResXMLTree) { @@ -1622,7 +1625,7 @@ ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable) ResXMLTree::ResXMLTree() : ResXMLParser(*this) - , mDynamicRefTable(std::unique_ptr<DynamicRefTable>(nullptr)) + , mDynamicRefTable(nullptr) , mError(NO_INIT), mOwnedData(NULL) { if (kDebugResXMLTree) { @@ -4784,7 +4787,7 @@ void ResTable::setParameters(const ResTable_config* params) packageGroup->clearBagCache(); // Find which configurations match the set of parameters. This allows for a much - // faster lookup in getEntry() if the set of values is narrowed down. + // faster lookup in Lookup() if the set of values is narrowed down. for (size_t t = 0; t < packageGroup->types.size(); t++) { if (packageGroup->types[t].isEmpty()) { continue; @@ -6892,19 +6895,11 @@ DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib) mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID; } -std::unique_ptr<DynamicRefTable> DynamicRefTable::clone() const { - std::unique_ptr<DynamicRefTable> clone = std::unique_ptr<DynamicRefTable>( - new DynamicRefTable(mAssignedPackageId, mAppAsLib)); - clone->addMappings(*this); - return clone; -} - status_t DynamicRefTable::load(const ResTable_lib_header* const header) { const uint32_t entryCount = dtohl(header->count); - const uint32_t sizeOfEntries = sizeof(ResTable_lib_entry) * entryCount; const uint32_t expectedSize = dtohl(header->header.size) - dtohl(header->header.headerSize); - if (sizeOfEntries > expectedSize) { + if (entryCount > (expectedSize / sizeof(ResTable_lib_entry))) { ALOGE("ResTable_lib_header size %u is too small to fit %u entries (x %u).", expectedSize, entryCount, (uint32_t)sizeof(ResTable_lib_entry)); return UNKNOWN_ERROR; @@ -7016,21 +7011,29 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const { return NO_ERROR; } +bool DynamicRefTable::requiresLookup(const Res_value* value) const { + // Only resolve non-dynamic references and attributes if the package is loaded as a + // library or if a shared library is attempting to retrieve its own resource + if ((value->dataType == Res_value::TYPE_REFERENCE || + value->dataType == Res_value::TYPE_ATTRIBUTE) && + (mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) { + return true; + } + return value->dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE || + value->dataType == Res_value::TYPE_DYNAMIC_REFERENCE; +} + status_t DynamicRefTable::lookupResourceValue(Res_value* value) const { + if (!requiresLookup(value)) { + return NO_ERROR; + } + uint8_t resolvedType = Res_value::TYPE_REFERENCE; switch (value->dataType) { case Res_value::TYPE_ATTRIBUTE: resolvedType = Res_value::TYPE_ATTRIBUTE; FALLTHROUGH_INTENDED; case Res_value::TYPE_REFERENCE: - // Only resolve non-dynamic references and attributes if the package is loaded as a - // library or if a shared library is attempting to retrieve its own resource - if (!(mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) { - return NO_ERROR; - } - - // If the package is loaded as shared library, the resource reference - // also need to be fixed. break; case Res_value::TYPE_DYNAMIC_ATTRIBUTE: resolvedType = Res_value::TYPE_ATTRIBUTE; diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING index a58b47fcff9d..d1a6a5c18299 100644 --- a/libs/androidfw/TEST_MAPPING +++ b/libs/androidfw/TEST_MAPPING @@ -3,6 +3,9 @@ { "name": "libandroidfw_tests", "host": true + }, + { + "name": "FrameworksResourceLoaderTests" } ] }
\ No newline at end of file diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index 49fc82bff11e..879b050b65bd 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -24,6 +24,7 @@ #include "android-base/unique_fd.h" #include "androidfw/Asset.h" +#include "androidfw/Idmap.h" #include "androidfw/LoadedArsc.h" #include "androidfw/misc.h" @@ -34,79 +35,157 @@ namespace android { class LoadedIdmap; +// Interface for retrieving assets provided by an ApkAssets. +class AssetsProvider { + public: + virtual ~AssetsProvider() = default; + + // Opens a file for reading. + std::unique_ptr<Asset> Open(const std::string& path, + Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM, + bool* file_exists = nullptr) const { + return OpenInternal(path, mode, file_exists); + } + + // Iterate over all files and directories provided by the zip. The order of iteration is stable. + virtual bool ForEachFile(const std::string& /* path */, + const std::function<void(const StringPiece&, FileType)>& /* f */) const { + return true; + } + + protected: + AssetsProvider() = default; + + virtual std::unique_ptr<Asset> OpenInternal(const std::string& path, + Asset::AccessMode mode, + bool* file_exists) const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(AssetsProvider); +}; + +class ZipAssetsProvider; + // Holds an APK. class ApkAssets { public: + // This means the data extends to the end of the file. + static constexpr off64_t kUnknownLength = -1; + // Creates an ApkAssets. // If `system` is true, the package is marked as a system package, and allows some functions to // filter out this package when computing what configurations/resources are available. - static std::unique_ptr<const ApkAssets> Load(const std::string& path, bool system = false); + static std::unique_ptr<const ApkAssets> Load( + const std::string& path, package_property_t flags = 0U, + std::unique_ptr<const AssetsProvider> override_asset = nullptr); - // Creates an ApkAssets, but forces any package with ID 0x7f to be loaded as a shared library. - // If `system` is true, the package is marked as a system package, and allows some functions to - // filter out this package when computing what configurations/resources are available. - static std::unique_ptr<const ApkAssets> LoadAsSharedLibrary(const std::string& path, - bool system = false); + // Creates an ApkAssets from the given file descriptor, and takes ownership of the file + // descriptor. The `friendly_name` is some name that will be used to identify the source of + // this ApkAssets in log messages and other debug scenarios. + // If `length` equals kUnknownLength, offset must equal 0; otherwise, the apk data will be read + // using the `offset` into the file descriptor and will be `length` bytes long. + static std::unique_ptr<const ApkAssets> LoadFromFd( + base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U, + std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0, + off64_t length = kUnknownLength); + + // Creates an ApkAssets from the given path which points to a resources.arsc. + static std::unique_ptr<const ApkAssets> LoadTable( + const std::string& path, package_property_t flags = 0U, + std::unique_ptr<const AssetsProvider> override_asset = nullptr); + + // Creates an ApkAssets from the given file descriptor which points to an resources.arsc, and + // takes ownership of the file descriptor. + // If `length` equals kUnknownLength, offset must equal 0; otherwise, the .arsc data will be read + // using the `offset` into the file descriptor and will be `length` bytes long. + static std::unique_ptr<const ApkAssets> LoadTableFromFd( + base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U, + std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0, + off64_t length = kUnknownLength); // Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay // data. - // If `system` is true, the package is marked as a system package, and allows some functions to - // filter out this package when computing what configurations/resources are available. static std::unique_ptr<const ApkAssets> LoadOverlay(const std::string& idmap_path, - bool system = false); - - // Creates an ApkAssets from the given file descriptor, and takes ownership of the file - // descriptor. The `friendly_name` is some name that will be used to identify the source of - // this ApkAssets in log messages and other debug scenarios. - // If `system` is true, the package is marked as a system package, and allows some functions to - // filter out this package when computing what configurations/resources are available. - // If `force_shared_lib` is true, any package with ID 0x7f is loaded as a shared library. - static std::unique_ptr<const ApkAssets> LoadFromFd(base::unique_fd fd, - const std::string& friendly_name, bool system, - bool force_shared_lib); + package_property_t flags = 0U); - std::unique_ptr<Asset> Open(const std::string& path, - Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const; + // Creates an ApkAssets from the directory path. File-based resources are read within the + // directory as if the directory is an APK. + static std::unique_ptr<const ApkAssets> LoadFromDir( + const std::string& path, package_property_t flags = 0U, + std::unique_ptr<const AssetsProvider> override_asset = nullptr); - bool ForEachFile(const std::string& path, - const std::function<void(const StringPiece&, FileType)>& f) const; + // Creates a totally empty ApkAssets with no resources table and no file entries. + static std::unique_ptr<const ApkAssets> LoadEmpty( + package_property_t flags = 0U, + std::unique_ptr<const AssetsProvider> override_asset = nullptr); inline const std::string& GetPath() const { return path_; } + inline const AssetsProvider* GetAssetsProvider() const { + return assets_provider_.get(); + } + // This is never nullptr. inline const LoadedArsc* GetLoadedArsc() const { return loaded_arsc_.get(); } + inline const LoadedIdmap* GetLoadedIdmap() const { + return loaded_idmap_.get(); + } + + inline bool IsLoader() const { + return (property_flags_ & PROPERTY_LOADER) != 0; + } + inline bool IsOverlay() const { - return idmap_asset_.get() != nullptr; + return loaded_idmap_ != nullptr; } bool IsUpToDate() const; + // Creates an Asset from a file on disk. + static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path); + + // Creates an Asset from a file descriptor. + // + // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset + // must equal 0; otherwise, the asset data will be read using the `offset` into the file + // descriptor and will be `length` bytes long. + static std::unique_ptr<Asset> CreateAssetFromFd(base::unique_fd fd, + const char* path, + off64_t offset = 0, + off64_t length = kUnknownLength); private: DISALLOW_COPY_AND_ASSIGN(ApkAssets); - static std::unique_ptr<const ApkAssets> LoadImpl(base::unique_fd fd, const std::string& path, - std::unique_ptr<Asset> idmap_asset, - std::unique_ptr<const LoadedIdmap> loaded_idmap, - bool system, bool load_as_shared_library); - - // Creates an Asset from any file on the file system. - static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path); + static std::unique_ptr<const ApkAssets> LoadImpl( + std::unique_ptr<const AssetsProvider> assets, const std::string& path, + package_property_t property_flags, + std::unique_ptr<const AssetsProvider> override_assets = nullptr, + std::unique_ptr<Asset> idmap_asset = nullptr, + std::unique_ptr<const LoadedIdmap> idmap = nullptr); - ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path, time_t last_mod_time); + static std::unique_ptr<const ApkAssets> LoadTableImpl( + std::unique_ptr<Asset> resources_asset, const std::string& path, + package_property_t property_flags, + std::unique_ptr<const AssetsProvider> override_assets = nullptr); - using ZipArchivePtr = std::unique_ptr<ZipArchive, void(*)(ZipArchiveHandle)>; + ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider, + std::string path, + time_t last_mod_time, + package_property_t property_flags); - ZipArchivePtr zip_handle_; + std::unique_ptr<const AssetsProvider> assets_provider_; const std::string path_; time_t last_mod_time_; + package_property_t property_flags_ = 0U; std::unique_ptr<Asset> resources_asset_; std::unique_ptr<Asset> idmap_asset_; std::unique_ptr<const LoadedArsc> loaded_arsc_; + std::unique_ptr<const LoadedIdmap> loaded_idmap_; }; } // namespace android diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h index 9d12a35395c9..298509eb37a1 100644 --- a/libs/androidfw/include/androidfw/Asset.h +++ b/libs/androidfw/include/androidfw/Asset.h @@ -26,6 +26,7 @@ #include <memory> +#include <android-base/unique_fd.h> #include <utils/Compat.h> #include <utils/Errors.h> #include <utils/String8.h> @@ -121,6 +122,11 @@ public: */ const char* getAssetSource(void) const { return mAssetSource.string(); } + /* + * Create the asset from a file descriptor. + */ + static Asset* createFromFd(const int fd, const char* fileName, AccessMode mode); + protected: /* * Adds this Asset to the global Asset list for debugging and @@ -153,6 +159,7 @@ private: /* AssetManager needs access to our "create" functions */ friend class AssetManager; friend class ApkAssets; + friend class ZipAssetsProvider; /* * Create the asset from a named file on disk. @@ -197,8 +204,14 @@ private: */ 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". + */ static std::unique_ptr<Asset> createFromUncompressedMap(std::unique_ptr<FileMap> dataMap, - AccessMode mode); + base::unique_fd fd, AccessMode mode); /* * Create the asset from a memory-mapped file segment with compressed @@ -251,9 +264,9 @@ public: /* * Use a memory-mapped region. * - * On success, the object takes ownership of "dataMap". + * On success, the object takes ownership of "dataMap" and "fd". */ - status_t openChunk(FileMap* dataMap); + status_t openChunk(FileMap* dataMap, base::unique_fd fd); /* * Standard Asset interfaces. @@ -268,11 +281,12 @@ public: virtual bool isAllocated(void) const { return mBuf != NULL; } private: - 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 - FILE* mFp; // for read/seek - char* mFileName; // for opening + 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 + FILE* mFp; // for read/seek + char* mFileName; // for opening + base::unique_fd mFd; // for opening file descriptors /* * To support getBuffer() we either need to read the entire thing into diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 1e2b36cb1703..b2cec2a42994 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -27,6 +27,7 @@ #include "androidfw/ApkAssets.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" +#include "androidfw/DynamicLibManager.h" #include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" @@ -94,6 +95,7 @@ class AssetManager2 { }; AssetManager2(); + explicit AssetManager2(DynamicLibManager* dynamic_lib_manager); // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets // are not owned by the AssetManager, and must have a longer lifetime. @@ -122,7 +124,11 @@ class AssetManager2 { // Returns the DynamicRefTable for the ApkAssets represented by the cookie. // This may be nullptr if the APK represented by `cookie` has no resource table. - const DynamicRefTable* GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const; + std::shared_ptr<const DynamicRefTable> GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const; + + // Returns a string representation of the overlayable API of a package. + 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; @@ -232,12 +238,14 @@ class AssetManager2 { ResTable_config* in_out_selected_config, uint32_t* in_out_flags, uint32_t* out_last_reference) const; - // Enables or disables resource resolution logging. Clears stored steps when - // disabled. + // 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. + // Returns formatted log of last resource resolution path, or empty if no resource has been + // resolved yet. std::string GetLastResourceResolution() const; const std::vector<uint32_t> GetBagResIdStack(uint32_t resid); @@ -257,10 +265,13 @@ class AssetManager2 { // Creates a new Theme from this AssetManager. std::unique_ptr<Theme> NewTheme(); - void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func) const { + void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func, + package_property_t excluded_property_flags = 0U) const { for (const PackageGroup& package_group : package_groups_) { - if (!func(package_group.packages_.front().loaded_package_->GetPackageName(), - package_group.dynamic_ref_table.mAssignedPackageId)) { + const auto loaded_package = package_group.packages_.front().loaded_package_; + if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U + && !func(loaded_package->GetPackageName(), + package_group.dynamic_ref_table->mAssignedPackageId)) { return; } } @@ -271,6 +282,50 @@ class AssetManager2 { private: DISALLOW_COPY_AND_ASSIGN(AssetManager2); + // 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; + }; + + // Represents an single package. + struct ConfiguredPackage { + // A pointer to the immutable, loaded package info. + const LoadedPackage* loaded_package_; + + // A mutable AssetManager-specific list of configurations that match the AssetManager's + // current configuration. This is used as an optimization to avoid checking every single + // candidate configuration when looking up resources. + ByteBucketArray<FilteredConfigGroup> filtered_configs_; + }; + + // Represents a Runtime Resource Overlay that overlays resources in the logical package. + struct ConfiguredOverlay { + // The set of package groups that overlay this package group. + IdmapResMap overlay_res_maps_; + + // The cookie of the overlay assets. + ApkAssetsCookie cookie; + }; + + // Represents a logical package, which can be made up of many individual packages. Each package + // in a PackageGroup shares the same package name and package ID. + struct PackageGroup { + // The set of packages that make-up this group. + std::vector<ConfiguredPackage> packages_; + + // The cookies associated with each package in the group. They share the same order as + // packages_. + std::vector<ApkAssetsCookie> cookies_; + + // Runtime Resource Overlays that overlay resources in this package group. + std::vector<ConfiguredOverlay> overlays_; + + // A library reference table that contains build-package ID to runtime-package ID mappings. + std::shared_ptr<DynamicRefTable> dynamic_ref_table = std::make_shared<DynamicRefTable>(); + }; + // 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 @@ -291,6 +346,11 @@ class AssetManager2 { ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, bool ignore_configuration, FindEntryResult* out_entry) 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; + // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. void BuildDynamicRefTable(); @@ -303,49 +363,22 @@ class AssetManager2 { // This should always be called when mutating the AssetManager's configuration or ApkAssets set. void RebuildFilterList(bool filter_incompatible_configs = true); + // Retrieves the APK paths of overlays that overlay non-system packages. + std::set<std::string> GetNonSystemOverlayPaths() const; + // 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); // Retrieve the assigned package id of the package if loaded into this AssetManager - uint8_t GetAssignedPackageId(const LoadedPackage* package); + uint8_t GetAssignedPackageId(const LoadedPackage* package) const; + + DynamicLibManager* GetDynamicLibManager() const; // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector<const ApkAssets*> apk_assets_; - // 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; - }; - - // Represents an single package. - struct ConfiguredPackage { - // A pointer to the immutable, loaded package info. - const LoadedPackage* loaded_package_; - - // A mutable AssetManager-specific list of configurations that match the AssetManager's - // current configuration. This is used as an optimization to avoid checking every single - // candidate configuration when looking up resources. - ByteBucketArray<FilteredConfigGroup> filtered_configs_; - }; - - // Represents a logical package, which can be made up of many individual packages. Each package - // in a PackageGroup shares the same package name and package ID. - struct PackageGroup { - // The set of packages that make-up this group. - std::vector<ConfiguredPackage> packages_; - - // The cookies associated with each package in the group. They share the same order as - // packages_. - std::vector<ApkAssetsCookie> cookies_; - - // A library reference table that contains build-package ID to runtime-package ID mappings. - DynamicRefTable dynamic_ref_table; - }; - // DynamicRefTables for shared library package resolution. // These are ordered according to apk_assets_. The mappings may change depending on what is // in apk_assets_, therefore they must be stored in the AssetManager and not in the @@ -360,6 +393,9 @@ class AssetManager2 { // may need to be purged. ResTable_config configuration_; + // Component responsible for assigning package ids to shared libraries. + std::variant<std::unique_ptr<DynamicLibManager>, DynamicLibManager*> dynamic_lib_manager_; + // 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_; @@ -378,7 +414,13 @@ class AssetManager2 { enum class Type { INITIAL, BETTER_MATCH, - OVERLAID + BETTER_MATCH_LOADER, + OVERLAID, + OVERLAID_LOADER, + SKIPPED, + SKIPPED_LOADER, + NO_ENTRY, + NO_ENTRY_LOADER, }; // Marks what kind of override this step was. @@ -408,7 +450,7 @@ class AssetManager2 { }; // Record of the last resolved resource's resolution path. - mutable Resolution last_resolution; + mutable Resolution last_resolution_; }; class Theme { diff --git a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h deleted file mode 100644 index 8bc25202b3ab..000000000000 --- a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2015 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 <gui/DisplayEventReceiver.h> -#include <utils/Log.h> -#include <utils/Looper.h> - -namespace android { - -class DisplayEventDispatcher : public LooperCallback { -public: - explicit DisplayEventDispatcher(const sp<Looper>& looper, - ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp, - ISurfaceComposer::ConfigChanged configChanged = ISurfaceComposer::eConfigChangedSuppress); - - status_t initialize(); - void dispose(); - status_t scheduleVsync(); - -protected: - virtual ~DisplayEventDispatcher() = default; - -private: - sp<Looper> mLooper; - DisplayEventReceiver mReceiver; - bool mWaitingForVsync; - - virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0; - virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, - bool connected) = 0; - virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, - int32_t configId) = 0; - - virtual int handleEvent(int receiveFd, int events, void* data); - bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, - uint32_t* outCount); -}; -} diff --git a/libs/androidfw/include/androidfw/DynamicLibManager.h b/libs/androidfw/include/androidfw/DynamicLibManager.h new file mode 100644 index 000000000000..1ff7079573d2 --- /dev/null +++ b/libs/androidfw/include/androidfw/DynamicLibManager.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 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_DYNAMICLIBMANAGER_H +#define ANDROIDFW_DYNAMICLIBMANAGER_H + +#include <string> +#include <unordered_map> + +#include "android-base/macros.h" + +namespace android { + +// Manages assigning resource ids for dynamic resources. +class DynamicLibManager { + public: + DynamicLibManager() = default; + + // Retrieves the assigned package id for the library. + uint8_t GetAssignedId(const std::string& library_package_name); + + // Queries in ascending order for the first available package id that is not currently assigned to + // a library. + uint8_t FindUnassignedId(uint8_t start_package_id); + + private: + DISALLOW_COPY_AND_ASSIGN(DynamicLibManager); + + uint8_t next_package_id_ = 0x02; + std::unordered_map<std::string, uint8_t> shared_lib_package_ids_; +}; + +} // namespace android + +#endif //ANDROIDFW_DYNAMICLIBMANAGER_H diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index fd02e6f63b74..ccb57f373473 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -20,20 +20,123 @@ #include <memory> #include <string> #include <unordered_map> +#include <variant> #include "android-base/macros.h" - #include "androidfw/StringPiece.h" +#include "androidfw/ResourceTypes.h" +#include "utils/ByteOrder.h" namespace android { -struct Idmap_header; -struct IdmapEntry_header; +class LoadedIdmap; +class IdmapResMap; + +// A string pool for overlay apk assets. The string pool holds the strings of the overlay resources +// table and additionally allows for loading strings from the idmap string pool. The idmap string +// pool strings are offset after the end of the overlay resource table string pool entries so +// queries for strings defined inline in the idmap do not conflict with queries for overlay +// resource table strings. +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; + size_t size() const override; + + explicit OverlayStringPool(const LoadedIdmap* loaded_idmap); + private: + const Idmap_data_header* data_header_; + const ResStringPool* idmap_string_pool_; +}; + +// A dynamic reference table for loaded overlay packages that rewrites the resource id of overlay +// resources to the resource id of corresponding target resources. +class OverlayDynamicRefTable : public DynamicRefTable { + public: + ~OverlayDynamicRefTable() override = default; + status_t lookupResourceId(uint32_t* resId) const override; + + private: + explicit OverlayDynamicRefTable(const Idmap_data_header* data_header, + const Idmap_overlay_entry* entries, + uint8_t target_assigned_package_id); + + // Rewrites a compile-time overlay resource id to the runtime resource id of corresponding target + // resource. + virtual status_t lookupResourceIdNoRewrite(uint32_t* resId) const; + + const Idmap_data_header* data_header_; + const Idmap_overlay_entry* entries_; + const int8_t target_assigned_package_id_; + + friend LoadedIdmap; + friend IdmapResMap; +}; + +// 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: + // 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) {}; + explicit Result(uint32_t value) : data_(value) {}; + explicit Result(ResTable_entry_handle&& value) : data_(value) { }; + + // Returns `true` if the resource is overlaid. + inline explicit operator bool() const { + return !std::get_if<nullptr_t>(&data_); + } + + inline bool IsResourceId() const { + return std::get_if<uint32_t>(&data_); + } + + inline uint32_t GetResourceId() const { + return *std::get_if<uint32_t>(&data_); + } + + inline bool IsTableEntry() const { + return std::get_if<ResTable_entry_handle>(&data_); + } + + inline const ResTable_entry_handle& GetTableEntry() const { + return *std::get_if<ResTable_entry_handle>(&data_); + } + + private: + std::variant<uint32_t, nullptr_t, ResTable_entry_handle> data_; + }; + + // Looks up the value that overlays the target resource id. + Result Lookup(uint32_t target_res_id) const; + + inline const OverlayDynamicRefTable* GetOverlayDynamicRefTable() const { + return overlay_ref_table_; + } + + private: + explicit IdmapResMap(const Idmap_data_header* data_header, + const Idmap_target_entry* entries, + uint8_t target_assigned_package_id, + const OverlayDynamicRefTable* overlay_ref_table); + + const Idmap_data_header* data_header_; + const Idmap_target_entry* entries_; + const uint8_t target_assigned_package_id_; + const OverlayDynamicRefTable* overlay_ref_table_; + + friend LoadedIdmap; +}; // Represents a loaded/parsed IDMAP for a Runtime Resource Overlay (RRO). -// An RRO and its target APK have different resource IDs assigned to their resources. Overlaying -// a resource is done by resource name. An IDMAP is a generated mapping between the resource IDs -// of the RRO and the target APK for each resource with the same name. +// An RRO and its target APK have different resource IDs assigned to their resources. +// An IDMAP is a generated mapping between the resource IDs of the RRO and the target APK. // A LoadedIdmap can be set alongside the overlay's LoadedArsc to allow the overlay ApkAssets to // masquerade as the target ApkAssets resources. class LoadedIdmap { @@ -41,34 +144,52 @@ class LoadedIdmap { // Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed. static std::unique_ptr<const LoadedIdmap> Load(const StringPiece& idmap_data); - // Performs a lookup of the expected entry ID for the given IDMAP entry header. - // Returns true if the mapping exists and fills `output_entry_id` with the result. - static bool Lookup(const IdmapEntry_header* header, uint16_t input_entry_id, - uint16_t* output_entry_id); - - // Returns the package ID for which this overlay should apply. - uint8_t TargetPackageId() const; - // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated. inline const std::string& OverlayApkPath() const { return overlay_apk_path_; } - // Returns the mapping of target entry ID to overlay entry ID for the given target type. - const IdmapEntry_header* GetEntryMapForType(uint8_t type_id) const; + // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated. + inline const std::string& TargetApkPath() const { + return target_apk_path_; + } + + // 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); + } + + // Returns a dynamic reference table for a loaded overlay package. + inline const OverlayDynamicRefTable GetOverlayDynamicRefTable( + uint8_t target_assigned_package_id) const { + return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id); + } protected: // Exposed as protected so that tests can subclass and mock this class out. LoadedIdmap() = default; - const Idmap_header* header_ = nullptr; + const Idmap_header* header_; + const Idmap_data_header* data_header_; + const Idmap_target_entry* target_entries_; + const Idmap_overlay_entry* overlay_entries_; + const std::unique_ptr<ResStringPool> string_pool_; + std::string overlay_apk_path_; - std::unordered_map<uint8_t, const IdmapEntry_header*> type_map_; + std::string target_apk_path_; private: DISALLOW_COPY_AND_ASSIGN(LoadedIdmap); - explicit LoadedIdmap(const Idmap_header* header); + explicit LoadedIdmap(const Idmap_header* header, + const Idmap_data_header* data_header, + const Idmap_target_entry* target_entries, + const Idmap_overlay_entry* overlay_entries, + ResStringPool* string_pool); + + friend OverlayStringPool; }; } // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 950f5413f550..89ff9f52125d 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -51,10 +51,6 @@ struct TypeSpec { // and under which configurations it varies. const ResTable_typeSpec* type_spec; - // Pointer to the mmapped data where the IDMAP mappings for this type - // exist. May be nullptr if no IDMAP exists. - const IdmapEntry_header* idmap_entries; - // The number of types that follow this struct. // There is a type for each configuration that entries are defined for. size_t type_count; @@ -73,6 +69,26 @@ struct TypeSpec { } }; +// Flags that change the behavior of loaded packages. +// Keep in sync with f/b/android/content/res/ApkAssets.java +using package_property_t = uint32_t; +enum : package_property_t { + // The package contains framework resource values specified by the system. + // This allows some functions to filter out this package when computing + // what configurations/resources are available. + PROPERTY_SYSTEM = 1U << 0U, + + // The package is a shared library or has a package id of 7f and is loaded as a shared library by + // force. + PROPERTY_DYNAMIC = 1U << 1U, + + // The package has been loaded dynamically using a ResourcesProvider. + PROPERTY_LOADER = 1U << 2U, + + // The package is a RRO. + PROPERTY_OVERLAY = 1U << 3U, +}; + // TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of // ResTable_type pointers. // TypeSpecPtr is a managed pointer that knows how to delete itself. @@ -136,8 +152,7 @@ class LoadedPackage { } static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk, - const LoadedIdmap* loaded_idmap, bool system, - bool load_as_shared_library); + package_property_t property_flags); ~LoadedPackage(); @@ -174,17 +189,26 @@ class LoadedPackage { // Returns true if this package is dynamic (shared library) and needs to have an ID assigned. inline bool IsDynamic() const { - return dynamic_; + return (property_flags_ & PROPERTY_DYNAMIC) != 0; + } + + // Returns true if this package is a Runtime Resource Overlay. + inline bool IsOverlay() const { + return (property_flags_ & PROPERTY_OVERLAY) != 0; } // Returns true if this package originates from a system provided resource. inline bool IsSystem() const { - return system_; + return (property_flags_ & PROPERTY_SYSTEM) != 0; } - // Returns true if this package is from an overlay ApkAssets. - inline bool IsOverlay() const { - return overlay_; + // Returns true if this package is a custom loader and should behave like an overlay. + inline bool IsCustomLoader() const { + return (property_flags_ & PROPERTY_LOADER) != 0; + } + + inline package_property_t GetPropertyFlags() const { + return property_flags_; } // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a @@ -216,9 +240,6 @@ class LoadedPackage { const TypeSpecPtr& ptr = type_specs_[i]; if (ptr != nullptr) { uint8_t type_id = ptr->type_spec->id; - if (ptr->idmap_entries != nullptr) { - type_id = ptr->idmap_entries->target_type_id; - } f(ptr.get(), type_id - 1); } } @@ -255,17 +276,17 @@ class LoadedPackage { ResStringPool type_string_pool_; ResStringPool key_string_pool_; std::string package_name_; + bool defines_overlayable_ = false; int package_id_ = -1; int type_id_offset_ = 0; - bool dynamic_ = false; - bool system_ = false; - bool overlay_ = false; - bool defines_overlayable_ = false; + package_property_t property_flags_ = 0U; ByteBucketArray<TypeSpecPtr> type_specs_; ByteBucketArray<uint32_t> resource_ids_; std::vector<DynamicPackageEntry> dynamic_package_map_; std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_; + + // A map of overlayable name to actor std::unordered_map<std::string, std::string> overlayable_map_; }; @@ -281,8 +302,7 @@ class LoadedArsc { // ID. static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data, const LoadedIdmap* loaded_idmap = nullptr, - bool system = false, - bool load_as_shared_library = false); + package_property_t property_flags = 0U); // Create an empty LoadedArsc. This is used when an APK has no resources.arsc. static std::unique_ptr<const LoadedArsc> CreateEmpty(); @@ -290,7 +310,7 @@ class LoadedArsc { // Returns the string pool where all string resource values // (Res_value::dataType == Res_value::TYPE_STRING) are indexed. inline const ResStringPool* GetStringPool() const { - return &global_string_pool_; + return global_string_pool_.get(); } // Gets a pointer to the package with the specified package ID, or nullptr if no such package @@ -302,20 +322,15 @@ class LoadedArsc { return packages_; } - // Returns true if this is a system provided resource. - inline bool IsSystem() const { - return system_; - } - private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); LoadedArsc() = default; - bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library); + bool LoadTable( + const Chunk& chunk, const LoadedIdmap* loaded_idmap, package_property_t property_flags); - ResStringPool global_string_pool_; + std::unique_ptr<ResStringPool> global_string_pool_ = util::make_unique<ResStringPool>(); std::vector<std::unique_ptr<const LoadedPackage>> packages_; - bool system_ = false; }; } // namespace android diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h index 64924f433245..8891512958f0 100644 --- a/libs/androidfw/include/androidfw/MutexGuard.h +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -47,7 +47,8 @@ class Guarded { static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer"); public: - explicit Guarded() : guarded_() { + template <typename ...Args> + explicit Guarded(Args&& ...args) : guarded_(std::forward<Args>(args)...) { } template <typename U = T> diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index fc635aaeb0d8..2bfc7fc38d1c 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -22,6 +22,7 @@ #include <androidfw/Asset.h> #include <androidfw/LocaleData.h> +#include <androidfw/StringPiece.h> #include <utils/Errors.h> #include <utils/String16.h> #include <utils/Vector.h> @@ -34,12 +35,13 @@ #include <android/configuration.h> +#include <array> #include <memory> namespace android { constexpr const static uint32_t kIdmapMagic = 0x504D4449u; -constexpr const static uint32_t kIdmapCurrentVersion = 0x00000001u; +constexpr const static uint32_t kIdmapCurrentVersion = 0x00000003u; /** * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of @@ -492,7 +494,7 @@ class ResStringPool public: ResStringPool(); ResStringPool(const void* data, size_t size, bool copyData=false); - ~ResStringPool(); + virtual ~ResStringPool(); void setToEmpty(); status_t setTo(const void* data, size_t size, bool copyData=false); @@ -506,10 +508,10 @@ public: inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const { return stringAt(ref.index, outLen); } - const char16_t* stringAt(size_t idx, size_t* outLen) const; + virtual const char16_t* stringAt(size_t idx, size_t* outLen) const; // Note: returns null if the string pool is not UTF8. - const char* string8At(size_t idx, size_t* outLen) const; + virtual const char* string8At(size_t idx, size_t* outLen) const; // Return string whether the pool is UTF8 or UTF16. Does not allow you // to distinguish null. @@ -520,9 +522,11 @@ public: ssize_t indexOfString(const char16_t* str, size_t strLen) const; - size_t size() const; + virtual size_t size() const; size_t styleCount() const; size_t bytes() const; + const void* data() const; + bool isSorted() const; bool isUTF8() const; @@ -810,7 +814,7 @@ public: * The tree stores a clone of the specified DynamicRefTable, so any changes to the original * DynamicRefTable will not affect this tree after instantiation. **/ - explicit ResXMLTree(const DynamicRefTable* dynamicRefTable); + explicit ResXMLTree(std::shared_ptr<const DynamicRefTable> dynamicRefTable); ResXMLTree(); ~ResXMLTree(); @@ -825,7 +829,7 @@ private: status_t validateNode(const ResXMLTree_node* node) const; - std::unique_ptr<const DynamicRefTable> mDynamicRefTable; + std::shared_ptr<const DynamicRefTable> mDynamicRefTable; status_t mError; void* mOwnedData; @@ -1582,6 +1586,50 @@ 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) { + return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry)); + } + + 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 @@ -1630,43 +1678,66 @@ struct ResTable_overlayable_header */ struct ResTable_overlayable_policy_header { - struct ResChunk_header header; - + /** + * Flags for a bitmask for all possible overlayable policy options. + * + * Any changes to this set should also update aidl/android/os/OverlayablePolicy.aidl + */ enum PolicyFlags : uint32_t { + // Base + NONE = 0x00000000, + // Any overlay can overlay these resources. - POLICY_PUBLIC = 0x00000001, + PUBLIC = 0x00000001, // The overlay must reside of the system partition or must have existed on the system partition // before an upgrade to overlay these resources. - POLICY_SYSTEM_PARTITION = 0x00000002, + SYSTEM_PARTITION = 0x00000002, // The overlay must reside of the vendor partition or must have existed on the vendor partition // before an upgrade to overlay these resources. - POLICY_VENDOR_PARTITION = 0x00000004, + VENDOR_PARTITION = 0x00000004, // The overlay must reside of the product partition or must have existed on the product // partition before an upgrade to overlay these resources. - POLICY_PRODUCT_PARTITION = 0x00000008, + PRODUCT_PARTITION = 0x00000008, - // The overlay must be signed with the same signature as the actor of the target resource, - // which can be separate or the same as the target package with the resource. - POLICY_SIGNATURE = 0x00000010, + // The overlay must be signed with the same signature as the package containing the target + // resource + SIGNATURE = 0x00000010, // The overlay must reside of the odm partition or must have existed on the odm // partition before an upgrade to overlay these resources. - POLICY_ODM_PARTITION = 0x00000020, + ODM_PARTITION = 0x00000020, // The overlay must reside of the oem partition or must have existed on the oem // partition before an upgrade to overlay these resources. - POLICY_OEM_PARTITION = 0x00000040, + OEM_PARTITION = 0x00000040, + + // The overlay must be signed with the same signature as the actor declared for the target + // resource + ACTOR_SIGNATURE = 0x00000080, }; - uint32_t policy_flags; + + using PolicyBitmask = uint32_t; + + struct ResChunk_header header; + + PolicyFlags policy_flags; // The number of ResTable_ref that follow this header. uint32_t entry_count; }; -struct alignas(uint32_t) Idmap_header { +inline ResTable_overlayable_policy_header::PolicyFlags& operator |=( + ResTable_overlayable_policy_header::PolicyFlags& first, + ResTable_overlayable_policy_header::PolicyFlags second) { + first = static_cast<ResTable_overlayable_policy_header::PolicyFlags>(first | second); + return first; +} + +#pragma pack(push, 1) +struct Idmap_header { // Always 0x504D4449 ('IDMP') uint32_t magic; @@ -1678,17 +1749,32 @@ struct alignas(uint32_t) Idmap_header { uint8_t target_path[256]; uint8_t overlay_path[256]; - uint16_t target_package_id; - uint16_t type_count; -} __attribute__((packed)); + uint32_t debug_info_size; + uint8_t debug_info[0]; -struct alignas(uint32_t) IdmapEntry_header { - uint16_t target_type_id; - uint16_t overlay_type_id; - uint16_t entry_count; - uint16_t entry_id_offset; - uint32_t entries[0]; -} __attribute__((packed)); + size_t Size() const; +}; + +struct Idmap_data_header { + uint8_t target_package_id; + uint8_t overlay_package_id; + uint32_t target_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; +}; + +struct Idmap_overlay_entry { + uint32_t overlay_id; + uint32_t target_id; +}; +#pragma pack(pop) class AssetManager2; @@ -1706,6 +1792,7 @@ class DynamicRefTable public: DynamicRefTable(); DynamicRefTable(uint8_t packageId, bool appAsLib); + virtual ~DynamicRefTable() = default; // Loads an unmapped reference table from the package. status_t load(const ResTable_lib_header* const header); @@ -1719,12 +1806,12 @@ public: void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId); - // Creates a new clone of the reference table - std::unique_ptr<DynamicRefTable> clone() const; + // Returns whether or not the value must be looked up. + bool requiresLookup(const Res_value* value) const; // Performs the actual conversion of build-time resource ID to run-time // resource ID. - status_t lookupResourceId(uint32_t* resId) const; + virtual status_t lookupResourceId(uint32_t* resId) const; status_t lookupResourceValue(Res_value* value) const; inline const KeyedVector<String16, uint8_t>& entries() const { diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h index aa1466fde778..9a3646b49db8 100644 --- a/libs/androidfw/include/androidfw/Util.h +++ b/libs/androidfw/include/androidfw/Util.h @@ -19,12 +19,19 @@ #include <cstdlib> #include <memory> +#include <sstream> #include <vector> #include "android-base/macros.h" #include "androidfw/StringPiece.h" +#ifdef __ANDROID__ +#define ANDROID_LOG(x) LOG(x) +#else +#define ANDROID_LOG(x) std::stringstream() +#endif + namespace android { namespace util { diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index e2b9f0040989..19db25ce8811 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -42,7 +42,7 @@ TEST(ApkAssetsTest, LoadApk) { const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); ASSERT_THAT(loaded_arsc, NotNull()); ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkFromFd) { @@ -50,14 +50,13 @@ TEST(ApkAssetsTest, LoadApkFromFd) { unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); ASSERT_THAT(fd.get(), Ge(0)); - std::unique_ptr<const ApkAssets> loaded_apk = - ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); + std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path); ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); ASSERT_THAT(loaded_arsc, NotNull()); ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { @@ -70,7 +69,7 @@ TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); - loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); + loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk", PROPERTY_DYNAMIC); ASSERT_THAT(loaded_apk, NotNull()); loaded_arsc = loaded_apk->GetLoadedArsc(); @@ -79,47 +78,16 @@ TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } -TEST(ApkAssetsTest, LoadApkWithIdmap) { - std::string contents; - ResTable target_table; - const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; - ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); - ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); - - ResTable overlay_table; - const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; - ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); - ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); - - util::unique_cptr<void> idmap_data; - void* temp_data; - size_t idmap_len; - - ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), - overlay_path.c_str(), &temp_data, &idmap_len), - Eq(NO_ERROR)); - idmap_data.reset(temp_data); - - TemporaryFile tf; - ASSERT_TRUE(base::WriteFully(tf.fd, idmap_data.get(), idmap_len)); - close(tf.fd); - - // Open something so that the destructor of TemporaryFile closes a valid fd. - tf.fd = open("/dev/null", O_WRONLY); - - ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); -} - TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); ASSERT_THAT(loaded_apk, NotNull()); - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml", + Asset::ACCESS_BUFFER), NotNull()); } - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml", + Asset::ACCESS_BUFFER), NotNull()); } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { @@ -127,7 +95,8 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); ASSERT_THAT(loaded_apk, NotNull()); - auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); + auto asset = loaded_apk->GetAssetsProvider()->Open("assets/uncompressed.txt", + Asset::ACCESS_UNKNOWN); ASSERT_THAT(asset, NotNull()); off64_t start, length; diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 40c8e46e4d84..ac32699c6dfd 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -63,10 +63,12 @@ class AssetManager2Test : public ::testing::Test { libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk"); ASSERT_NE(nullptr, libclient_assets_); - appaslib_assets_ = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); + appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk", + PROPERTY_DYNAMIC); ASSERT_NE(nullptr, appaslib_assets_); - system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/); + system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", + PROPERTY_SYSTEM); ASSERT_NE(nullptr, system_assets_); app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk"); @@ -214,6 +216,25 @@ TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data); } +TEST_F(AssetManager2Test, AssignsUnchangingPackageIdToSharedLibrary) { + DynamicLibManager lib_manager; + AssetManager2 assetmanager(&lib_manager); + assetmanager.SetApkAssets( + {lib_one_assets_.get(), lib_two_assets_.get(), libclient_assets_.get()}); + + AssetManager2 assetmanager2(&lib_manager); + assetmanager2.SetApkAssets( + {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + + uint32_t res_id = assetmanager.GetResourceId("com.android.lib_one:string/foo"); + ASSERT_NE(0U, res_id); + + uint32_t res_id_2 = assetmanager2.GetResourceId("com.android.lib_one:string/foo"); + ASSERT_NE(0U, res_id_2); + + ASSERT_EQ(res_id, res_id_2); +} + TEST_F(AssetManager2Test, GetSharedLibraryResourceName) { AssetManager2 assetmanager; assetmanager.SetApkAssets({lib_one_assets_.get()}); @@ -266,6 +287,27 @@ TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) { EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); } +TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) { + AssetManager2 assetmanager; + + // libclient is built with lib_one and then lib_two in order. + // Reverse the order to test that proper package ID re-assignment is happening. + 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); + + // First attribute comes from lib_two. + 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)); +} + TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) { AssetManager2 assetmanager; @@ -707,7 +749,7 @@ TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { EXPECT_EQ("", resultDisabled); } -TEST_F(AssetManager2Test, GetOverlayableMap) { +TEST_F(AssetManager2Test, GetOverlayablesToString) { ResTable_config desired_config; memset(&desired_config, 0, sizeof(desired_config)); @@ -718,9 +760,17 @@ TEST_F(AssetManager2Test, GetOverlayableMap) { const auto map = assetmanager.GetOverlayableMapForPackage(0x7f); ASSERT_NE(nullptr, map); - ASSERT_EQ(2, map->size()); + ASSERT_EQ(3, map->size()); ASSERT_EQ(map->at("OverlayableResources1"), "overlay://theme"); ASSERT_EQ(map->at("OverlayableResources2"), "overlay://com.android.overlayable"); + ASSERT_EQ(map->at("OverlayableResources3"), ""); + + std::string api; + ASSERT_TRUE(assetmanager.GetOverlayablesToString("com.android.overlayable", &api)); + ASSERT_EQ(api.find("not_overlayable"), std::string::npos); + ASSERT_NE(api.find("resource='com.android.overlayable:string/overlayable2' overlayable='OverlayableResources1' actor='overlay://theme' policy='0x0000000a'\n"), + std::string::npos); + } } // namespace android diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index c8dbe205fee2..24361b5817f4 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -67,7 +67,7 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) { AssetManager2 assetmanager; - auto apk_assets = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/styles/styles.apk"); + auto apk_assets = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk", PROPERTY_DYNAMIC); ASSERT_NE(nullptr, apk_assets); assetmanager.SetApkAssets({apk_assets.get()}); diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index 10b83a75304d..41ba637da5d7 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -14,114 +14,231 @@ * limitations under the License. */ +#include "android-base/file.h" +#include "androidfw/ApkAssets.h" +#include "androidfw/AssetManager2.h" #include "androidfw/ResourceTypes.h" #include "utils/String16.h" #include "utils/String8.h" #include "TestHelpers.h" -#include "data/basic/R.h" +#include "data/overlay/R.h" +#include "data/overlayable/R.h" +#include "data/system/R.h" -using ::com::android::basic::R; +namespace overlay = com::android::overlay; +namespace overlayable = com::android::overlayable; namespace android { +namespace { + class IdmapTest : public ::testing::Test { protected: void SetUp() override { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", - &contents)); - ASSERT_EQ(NO_ERROR, target_table_.add(contents.data(), contents.size(), 0, true)); - - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", - "resources.arsc", &overlay_data_)); - ResTable overlay_table; - ASSERT_EQ(NO_ERROR, overlay_table.add(overlay_data_.data(), overlay_data_.size())); - - char target_name[256] = "com.android.basic"; - ASSERT_EQ(NO_ERROR, overlay_table.createIdmap(target_table_, 0, 0, target_name, target_name, - &data_, &data_size_)); - } + // Move to the test data directory so the idmap can locate the overlay APK. + std::string original_path = base::GetExecutableDirectory(); + chdir(GetTestDataPath().c_str()); + + system_assets_ = ApkAssets::Load("system/system.apk"); + ASSERT_NE(nullptr, system_assets_); - void TearDown() override { - ::free(data_); + overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap"); + ASSERT_NE(nullptr, overlay_assets_); + + overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk"); + ASSERT_NE(nullptr, overlayable_assets_); + chdir(original_path.c_str()); } - ResTable target_table_; - std::string overlay_data_; - void* data_ = nullptr; - size_t data_size_ = 0; + protected: + std::unique_ptr<const ApkAssets> system_assets_; + std::unique_ptr<const ApkAssets> overlay_assets_; + std::unique_ptr<const ApkAssets> overlayable_assets_; }; -TEST_F(IdmapTest, CanLoadIdmap) { - ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); +std::string GetStringFromApkAssets(const AssetManager2& asset_manager, const Res_value& value, + ApkAssetsCookie cookie) { + auto assets = asset_manager.GetApkAssets(); + const ResStringPool* string_pool = assets[cookie]->GetLoadedArsc()->GetStringPool(); + return GetStringFromPool(string_pool, value.data); +} + } TEST_F(IdmapTest, OverlayOverridesResourceValue) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); Res_value val; - ssize_t block = target_table_.getResource(R::string::test2, &val, false); - ASSERT_GE(block, 0); - ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); - const ResStringPool* pool = target_table_.getTableStringBlock(block); - ASSERT_TRUE(pool != NULL); - ASSERT_LT(val.data, pool->size()); - - size_t str_len; - const char16_t* target_str16 = pool->stringAt(val.data, &str_len); - ASSERT_TRUE(target_str16 != NULL); - ASSERT_EQ(String16("test2"), String16(target_str16, str_len)); - - ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); - - ssize_t new_block = target_table_.getResource(R::string::test2, &val, false); - ASSERT_GE(new_block, 0); - ASSERT_NE(block, new_block); - ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); - pool = target_table_.getTableStringBlock(new_block); - ASSERT_TRUE(pool != NULL); - ASSERT_LT(val.data, pool->size()); - - target_str16 = pool->stringAt(val.data, &str_len); - ASSERT_TRUE(target_str16 != NULL); - ASSERT_EQ(String16("test2-overlay"), String16(target_str16, str_len)); + 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"); } -TEST_F(IdmapTest, OverlaidResourceHasSameName) { - ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); +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"); +} + +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)); +} + +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); +} + +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"); +} - ResTable::resource_name res_name; - ASSERT_TRUE(target_table_.getResourceName(R::array::integerArray1, true, &res_name)); +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); +} - ASSERT_TRUE(res_name.package != NULL); - ASSERT_TRUE(res_name.type != NULL); - ASSERT_TRUE(res_name.name8 != NULL); +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, + Asset::ACCESS_RANDOM); + auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(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); + + while (xml_tree->next() != ResXMLParser::START_TAG) { } + + // The resource id of @id/hello_view should be rewritten to the resource id/hello_view within the + // target. + ASSERT_EQ(xml_tree->getAttributeNameResID(0), 0x010100d0 /* android:attr/id */); + ASSERT_EQ(xml_tree->getAttributeDataType(0), Res_value::TYPE_REFERENCE); + ASSERT_EQ(xml_tree->getAttributeData(0), overlayable::R::id::hello_view); + + // The resource id of @android:string/yes should not be rewritten even though it overlays + // string/overlayable10 in the target. + ASSERT_EQ(xml_tree->getAttributeNameResID(1), 0x0101014f /* android:attr/text */); + ASSERT_EQ(xml_tree->getAttributeDataType(1), Res_value::TYPE_REFERENCE); + ASSERT_EQ(xml_tree->getAttributeData(1), 0x01040013 /* android:string/yes */); + + // The resource id of the attribute within the overlay should be rewritten to the resource id of + // the attribute in the target. + ASSERT_EQ(xml_tree->getAttributeNameResID(2), overlayable::R::attr::max_lines); + ASSERT_EQ(xml_tree->getAttributeDataType(2), Res_value::TYPE_INT_DEC); + ASSERT_EQ(xml_tree->getAttributeData(2), 4); +} - EXPECT_EQ(String16("com.android.basic"), String16(res_name.package, res_name.packageLen)); - EXPECT_EQ(String16("array"), String16(res_name.type, res_name.typeLen)); - EXPECT_EQ(String8("integerArray1"), String8(res_name.name8, res_name.nameLen)); +TEST_F(IdmapTest, OverlaidResourceHasSameName) { + AssetManager2 asset_manager; + 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"); } -constexpr const uint32_t kNonOverlaidResourceId = 0x7fff0000u; +TEST_F(IdmapTest, OverlayLoaderInterop) { + std::string contents; + auto loader_assets = ApkAssets::LoadTable(GetTestDataPath() + "/loader/resources.arsc", + PROPERTY_LOADER); -TEST_F(IdmapTest, OverlayDoesNotIncludeNonOverlaidResources) { - // First check that the resource we're trying to not include when overlaid is present when - // the overlay is loaded as a standalone APK. - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(overlay_data_.data(), overlay_data_.size(), 0, true)); + AssetManager2 asset_manager; + asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(), + overlay_assets_.get()}); Res_value val; - ssize_t block = table.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/); - ASSERT_GE(block, 0); - - // Now add the overlay and verify that the unoverlaid resource is gone. - ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); - block = target_table_.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/); - ASSERT_LT(block, 0); + 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"); } } // namespace diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index d58e8d20c8aa..2d69dfe4f429 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -25,6 +25,7 @@ #include "data/overlayable/R.h" #include "data/sparse/R.h" #include "data/styles/R.h" +#include "data/system/R.h" namespace app = com::android::app; namespace basic = com::android::basic; @@ -40,6 +41,8 @@ using ::testing::NotNull; using ::testing::SizeIs; using ::testing::StrEq; +using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; + namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { @@ -143,8 +146,7 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, - true /*load_as_shared_library*/); + LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, PROPERTY_DYNAMIC); ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); @@ -221,68 +223,12 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { ASSERT_THAT(type_spec->types[0], NotNull()); } -class MockLoadedIdmap : public LoadedIdmap { - public: - MockLoadedIdmap() : LoadedIdmap() { - local_header_.magic = kIdmapMagic; - local_header_.version = kIdmapCurrentVersion; - local_header_.target_package_id = 0x08; - local_header_.type_count = 1; - header_ = &local_header_; - - entry_header = util::unique_cptr<IdmapEntry_header>( - (IdmapEntry_header*)::malloc(sizeof(IdmapEntry_header) + sizeof(uint32_t))); - entry_header->target_type_id = 0x03; - entry_header->overlay_type_id = 0x02; - entry_header->entry_id_offset = 1; - entry_header->entry_count = 1; - entry_header->entries[0] = 0x00000000u; - type_map_[entry_header->overlay_type_id] = entry_header.get(); - } - - private: - Idmap_header local_header_; - util::unique_cptr<IdmapEntry_header> entry_header; -}; - -TEST(LoadedArscTest, LoadOverlay) { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &contents)); - - MockLoadedIdmap loaded_idmap; - - std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(StringPiece(contents), &loaded_idmap); - ASSERT_THAT(loaded_arsc, NotNull()); - - const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); - ASSERT_THAT(package, NotNull()); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); - - // The entry being overlaid doesn't exist at the original entry index. - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); - - // Since this is an overlay, the actual entry ID must be mapped. - ASSERT_THAT(type_spec->idmap_entries, NotNull()); - uint16_t target_entry_id = 0u; - ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); - ASSERT_THAT(target_entry_id, Eq(0x0u)); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); -} - TEST(LoadedArscTest, LoadOverlayable) { std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, - false /*load_as_shared_library*/); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = loaded_arsc->GetPackageById( @@ -296,29 +242,29 @@ TEST(LoadedArscTest, LoadOverlayable) { ASSERT_THAT(info, NotNull()); EXPECT_THAT(info->name, Eq("OverlayableResources1")); EXPECT_THAT(info->actor, Eq("overlay://theme")); - EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC)); + EXPECT_THAT(info->policy_flags, Eq(PolicyFlags::PUBLIC)); info = package->GetOverlayableInfo(overlayable::R::string::overlayable2); ASSERT_THAT(info, NotNull()); EXPECT_THAT(info->name, Eq("OverlayableResources1")); EXPECT_THAT(info->actor, Eq("overlay://theme")); EXPECT_THAT(info->policy_flags, - Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION - | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION)); + Eq(PolicyFlags::SYSTEM_PARTITION + | PolicyFlags::PRODUCT_PARTITION)); info = package->GetOverlayableInfo(overlayable::R::string::overlayable3); ASSERT_THAT(info, NotNull()); EXPECT_THAT(info->name, Eq("OverlayableResources2")); EXPECT_THAT(info->actor, Eq("overlay://com.android.overlayable")); EXPECT_THAT(info->policy_flags, - Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION - | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION)); + Eq(PolicyFlags::VENDOR_PARTITION + | PolicyFlags::PRODUCT_PARTITION)); info = package->GetOverlayableInfo(overlayable::R::string::overlayable4); EXPECT_THAT(info->name, Eq("OverlayableResources1")); EXPECT_THAT(info->actor, Eq("overlay://theme")); ASSERT_THAT(info, NotNull()); - EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC)); + EXPECT_THAT(info->policy_flags, Eq(PolicyFlags::PUBLIC)); } TEST(LoadedArscTest, ResourceIdentifierIterator) { @@ -382,9 +328,42 @@ TEST(LoadedArscTest, GetOverlayableMap) { ASSERT_EQ(std::string("com.android.overlayable"), packages[0]->GetPackageName()); const auto map = packages[0]->GetOverlayableMap(); - ASSERT_EQ(2, map.size()); + ASSERT_EQ(3, map.size()); ASSERT_EQ(map.at("OverlayableResources1"), "overlay://theme"); ASSERT_EQ(map.at("OverlayableResources2"), "overlay://com.android.overlayable"); + ASSERT_EQ(map.at("OverlayableResources3"), ""); +} + +TEST(LoadedArscTest, LoadCustomLoader) { + std::string contents; + + std::unique_ptr<Asset> + asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc"); + + const StringPiece data( + reinterpret_cast<const char*>(asset->getBuffer(true /*wordAligned*/)), + asset->getLength()); + + std::unique_ptr<const LoadedArsc> loaded_arsc = + LoadedArsc::Load(data, nullptr, PROPERTY_LOADER); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(overlayable::R::string::overlayable11)); + ASSERT_THAT(package, NotNull()); + EXPECT_THAT(package->GetPackageName(), StrEq("com.android.loader")); + EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); + + const uint8_t type_index = get_type_id(overlayable::R::string::overlayable11) - 1; + const uint16_t entry_index = get_entry_id(overlayable::R::string::overlayable11); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + 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()); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index be5ecd94a588..16b9c75982fb 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -36,7 +36,7 @@ namespace android { class ThemeTest : public ::testing::Test { public: void SetUp() override { - system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/); + system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", PROPERTY_SYSTEM); ASSERT_NE(nullptr, system_assets_); style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); diff --git a/libs/androidfw/tests/data/lib_two/R.h b/libs/androidfw/tests/data/lib_two/R.h index 92b9cc10e7a8..fd5a910961cb 100644 --- a/libs/androidfw/tests/data/lib_two/R.h +++ b/libs/androidfw/tests/data/lib_two/R.h @@ -30,16 +30,22 @@ struct R { }; }; + struct integer { + enum : uint32_t { + bar = 0x02020000, // default + }; + }; + struct string { enum : uint32_t { - LibraryString = 0x02020000, // default - foo = 0x02020001, // default + LibraryString = 0x02030000, // default + foo = 0x02030001, // default }; }; struct style { enum : uint32_t { - Theme = 0x02030000, // default + Theme = 0x02040000, // default }; }; }; diff --git a/libs/androidfw/tests/data/lib_two/lib_two.apk b/libs/androidfw/tests/data/lib_two/lib_two.apk Binary files differindex 486c23000276..8193db637eed 100644 --- a/libs/androidfw/tests/data/lib_two/lib_two.apk +++ b/libs/androidfw/tests/data/lib_two/lib_two.apk diff --git a/libs/androidfw/tests/data/lib_two/res/values/values.xml b/libs/androidfw/tests/data/lib_two/res/values/values.xml index 340d14c34c5d..4e1d69aa5a5a 100644 --- a/libs/androidfw/tests/data/lib_two/res/values/values.xml +++ b/libs/androidfw/tests/data/lib_two/res/values/values.xml @@ -18,14 +18,17 @@ <public type="attr" name="attr3" id="0x00010000" /> <attr name="attr3" format="integer" /> - <public type="string" name="LibraryString" id="0x00020000" /> + <public type="integer" name="bar" id="0x00020000" /> + <integer name="bar">1337</integer> + + <public type="string" name="LibraryString" id="0x00030000" /> <string name="LibraryString">Hi from library two</string> - <public type="string" name="foo" id="0x00020001" /> + <public type="string" name="foo" id="0x00030001" /> <string name="foo">Foo from lib_two</string> - <public type="style" name="Theme" id="0x02030000" /> + <public type="style" name="Theme" id="0x00040000" /> <style name="Theme"> - <item name="com.android.lib_two:attr3">800</item> + <item name="com.android.lib_two:attr3">@integer/bar</item> </style> </resources> diff --git a/libs/androidfw/tests/data/libclient/R.h b/libs/androidfw/tests/data/libclient/R.h index 43d1f9bb68e7..e21b3ebae826 100644 --- a/libs/androidfw/tests/data/libclient/R.h +++ b/libs/androidfw/tests/data/libclient/R.h @@ -34,6 +34,7 @@ struct R { struct style { enum : uint32_t { Theme = 0x7f020000, // default + ThemeMultiLib = 0x7f020001, // default }; }; diff --git a/libs/androidfw/tests/data/libclient/libclient.apk b/libs/androidfw/tests/data/libclient/libclient.apk Binary files differindex 17990248e862..4b9a8833c64a 100644 --- a/libs/androidfw/tests/data/libclient/libclient.apk +++ b/libs/androidfw/tests/data/libclient/libclient.apk diff --git a/libs/androidfw/tests/data/libclient/res/values/values.xml b/libs/androidfw/tests/data/libclient/res/values/values.xml index fead7c323767..a29f473e3c17 100644 --- a/libs/androidfw/tests/data/libclient/res/values/values.xml +++ b/libs/androidfw/tests/data/libclient/res/values/values.xml @@ -27,6 +27,12 @@ <item name="bar">@com.android.lib_one:string/foo</item> </style> + <public type="style" name="ThemeMultiLib" id="0x7f020001" /> + <style name="ThemeMultiLib" > + <item name="com.android.lib_one:attr1">@com.android.lib_one:string/foo</item> + <item name="com.android.lib_two:attr3">@com.android.lib_two:integer/bar</item> + </style> + <public type="string" name="foo_one" id="0x7f030000" /> <string name="foo_one">@com.android.lib_one:string/foo</string> diff --git a/libs/androidfw/tests/data/loader/AndroidManifest.xml b/libs/androidfw/tests/data/loader/AndroidManifest.xml new file mode 100644 index 000000000000..4c0bb47f59bf --- /dev/null +++ b/libs/androidfw/tests/data/loader/AndroidManifest.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest package="com.android.loader"> + <application> + </application> +</manifest> diff --git a/libs/androidfw/tests/data/loader/build b/libs/androidfw/tests/data/loader/build new file mode 100755 index 000000000000..457ec51ffe69 --- /dev/null +++ b/libs/androidfw/tests/data/loader/build @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Copyright (C) 2018 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. +# + +set -e + +FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk + +rm resources.arsc +aapt2 compile --dir res -o compiled.flata +aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o loader.apk compiled.flata +unzip loader.apk resources.arsc +rm loader.apk +rm compiled.flata diff --git a/libs/androidfw/tests/data/loader/res/values/public.xml b/libs/androidfw/tests/data/loader/res/values/public.xml new file mode 100644 index 000000000000..3293229104ce --- /dev/null +++ b/libs/androidfw/tests/data/loader/res/values/public.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> + +<resources> + <public type="string" name="overlayable11" id="0x7f01000b" /> +</resources>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/loader/res/values/values.xml b/libs/androidfw/tests/data/loader/res/values/values.xml new file mode 100644 index 000000000000..0653536508f8 --- /dev/null +++ b/libs/androidfw/tests/data/loader/res/values/values.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> + +<resources> + <string name="overlayable11">loader</string> +</resources> diff --git a/libs/androidfw/tests/data/loader/resources.arsc b/libs/androidfw/tests/data/loader/resources.arsc Binary files differnew file mode 100644 index 000000000000..2bdb288dbcab --- /dev/null +++ b/libs/androidfw/tests/data/loader/resources.arsc diff --git a/libs/androidfw/tests/data/overlay/AndroidManifest.xml b/libs/androidfw/tests/data/overlay/AndroidManifest.xml index a56ac18e900b..28a11489fae0 100644 --- a/libs/androidfw/tests/data/overlay/AndroidManifest.xml +++ b/libs/androidfw/tests/data/overlay/AndroidManifest.xml @@ -15,7 +15,9 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.basic"> - <application> - </application> + package="com.android.test.overlay"> + <overlay + android:targetPackage="com.android.test.basic" + android:targetName="OverlayableResources3" + android:resourcesMap="@xml/overlays"/> </manifest> diff --git a/libs/androidfw/tests/data/overlay/R.h b/libs/androidfw/tests/data/overlay/R.h new file mode 100644 index 000000000000..f3dbed29d7ee --- /dev/null +++ b/libs/androidfw/tests/data/overlay/R.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 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 TESTS_DATA_OVERLAY_R_H_ +#define TESTS_DATA_OVERLAY_R_H_ + +#include <cstdint> + +namespace com { +namespace android { +namespace overlay { + +struct R { + struct string { + enum : uint32_t { + internal = 0x7f040000, + }; + }; +}; + +} // namespace overlay +} // namespace android +} // namespace com + +#endif /* TESTS_DATA_OVERLAY_R_H_ */ diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build index 716b1bd9db64..99dfd63051a7 100755 --- a/libs/androidfw/tests/data/overlay/build +++ b/libs/androidfw/tests/data/overlay/build @@ -17,6 +17,15 @@ set -e +FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk + aapt2 compile --dir res -o compiled.flata -aapt2 link --manifest AndroidManifest.xml -o overlay.apk compiled.flata +aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o overlay.apk compiled.flata \ + --no-auto-version rm compiled.flata + +# Navigate back a directory so the idmap can find the overlays in the test data directory when being +# loaded during testing. +cd ../ +idmap2 create --target-apk-path overlayable/overlayable.apk \ + --overlay-apk-path overlay/overlay.apk --idmap-path overlay/overlay.idmap diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk Binary files differindex d37874dcbb40..62e98662e68d 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 differnew file mode 100644 index 000000000000..3759ed650033 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/overlay.idmap diff --git a/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml b/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml new file mode 100644 index 000000000000..54dc6c09acf1 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/hello_view" + android:text="@android:string/yes" + app:max_lines="4"/>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/overlay/res/values/values.xml b/libs/androidfw/tests/data/overlay/res/values/values.xml index 8e4417e457d1..ba018ec19e9f 100644 --- a/libs/androidfw/tests/data/overlay/res/values/values.xml +++ b/libs/androidfw/tests/data/overlay/res/values/values.xml @@ -13,13 +13,11 @@ See the License for the specific language governing permissions and limitations under the License. --> - <resources> - <string name="test2">test2-overlay</string> - <integer-array name="integerArray1"> - <item>10</item> - <item>11</item> - </integer-array> - <public type="animator" name="unoverlaid" id="0x7fff0000" /> - <item type="animator" name="unoverlaid">@null</item> + <string name="overlay1">Overlay One</string> + <string name="overlay2">Overlay Two</string> + <string name="overlay3">@string/internal</string> + <string name="overlay4">@string/overlay2</string> + <string name="internal">Internal</string> + <attr name="max_lines" format="integer" /> </resources> diff --git a/libs/androidfw/tests/data/overlay/res/xml/overlays.xml b/libs/androidfw/tests/data/overlay/res/xml/overlays.xml new file mode 100644 index 000000000000..9eca2faa76f4 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/res/xml/overlays.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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 language governing permissions and + limitations under the License. +--> +<overlay> + <!-- Overlays string/overlayable5 with the string "Overlay One". --> + <item target="string/overlayable5" value="@string/overlay1"/> + + <!-- Overlays string/overlayable6 and string/overlayable7 with the string "Overlay Two". --> + <item target="string/overlayable7" value="@string/overlay2" /> + <item target="string/overlayable6" value="@string/overlay2" /> + + <!-- Overlays string/overlayable8 with a reference to @string/internal. --> + <item target="string/overlayable8" value="@string/overlay3" /> + + <!-- Overlays string/overlayable9 with a reference to @string/overlay2. The reference to + @string/overlay2 should be rewritten to @string/overlayable7 in the target. --> + <item target="string/overlayable9" value="@string/overlay4" /> + + <!-- Overlays string/overlayable10 with the string "yes". --> + <item target="string/overlayable10" value="@android:string/yes" /> + + <!-- Overlays string/overlayable11 with the string "Hardcoded string". --> + <item target="string/overlayable11" value="Hardcoded string" /> + + <!-- Overlays string/overlayable10 with the string "yes". --> + <item target="integer/config_int" value="42" /> + + <!-- @attr/max_lines and @id/hello_view should be rewritten to @attr/max_lines and + @id/hello_view in the target. --> + <item target="layout/hello_view" value="@layout/hello_view" /> + <item target="attr/max_lines" value="@attr/max_lines" /> + <item target="id/hello_view" value="@id/hello_view" /> +</overlay> + + diff --git a/libs/androidfw/tests/data/overlayable/R.h b/libs/androidfw/tests/data/overlayable/R.h index e46e264da318..35125a62ff4b 100644 --- a/libs/androidfw/tests/data/overlayable/R.h +++ b/libs/androidfw/tests/data/overlayable/R.h @@ -31,6 +31,43 @@ struct R { overlayable2 = 0x7f010002, overlayable3 = 0x7f010003, overlayable4 = 0x7f010004, + overlayable5 = 0x7f010005, + overlayable6 = 0x7f010006, + overlayable7 = 0x7f010007, + overlayable8 = 0x7f010008, + overlayable9 = 0x7f010009, + overlayable10 = 0x7f01000a, + overlayable11 = 0x7f01000b, + }; + }; + + struct attr { + enum : uint32_t { + max_lines = 0x7f020000, + }; + }; + + struct boolean { + enum : uint32_t { + config_bool = 0x7f030000, + }; + }; + + struct id { + enum : uint32_t { + hello_view = 0x7f040000, + }; + }; + + struct integer { + enum : uint32_t { + config_integer = 0x7f050000, + }; + }; + + struct layout { + enum : uint32_t { + hello_view = 0x7f060000, }; }; }; diff --git a/libs/androidfw/tests/data/overlayable/build b/libs/androidfw/tests/data/overlayable/build index 98fdc5101160..0aa97d639c30 100755 --- a/libs/androidfw/tests/data/overlayable/build +++ b/libs/androidfw/tests/data/overlayable/build @@ -17,6 +17,9 @@ set -e +FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk + aapt2 compile --dir res -o compiled.flata -aapt2 link --manifest AndroidManifest.xml -o overlayable.apk compiled.flata +aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o overlayable.apk compiled.flata \ + --no-auto-version rm compiled.flata diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk Binary files differindex 047e6afde86b..9dc9c15f68a9 100644 --- a/libs/androidfw/tests/data/overlayable/overlayable.apk +++ b/libs/androidfw/tests/data/overlayable/overlayable.apk diff --git a/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml b/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml new file mode 100644 index 000000000000..268118a91bff --- /dev/null +++ b/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/hello_view" + android:text="None" + app:max_lines="0"/>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml index fcdbe94466c1..b3e8f7d8e84b 100644 --- a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml +++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml @@ -15,27 +15,41 @@ --> <resources> -<overlayable name="OverlayableResources1" actor="overlay://theme"> - <!-- Any overlay can overlay the value of @string/overlayable1 --> - <item type="string" name="overlayable1" /> + <overlayable name="OverlayableResources1" actor="overlay://theme"> + <!-- Any overlay on the product or system partition can overlay the value of + @string/overlayable2 --> + <policy type="product|system"> + <item type="string" name="overlayable2" /> + </policy> - <!-- Any overlay on the product or system partition can overlay the value of - @string/overlayable2 --> - <policy type="product|system"> - <item type="string" name="overlayable2" /> - </policy> + <!-- Any overlay can overlay the value of @string/overlayable4 --> + <policy type="public"> + <item type="string" name="overlayable1" /> + <item type="string" name="overlayable4" /> + </policy> + </overlayable> - <!-- Any overlay can overlay the value of @string/overlayable4 --> - <policy type="public"> - <item type="string" name="overlayable4" /> - </policy> -</overlayable> + <overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable"> + <!-- Any overlay on the vendor or product partition can overlay the value of + @string/overlayable3 --> + <policy type="vendor|product"> + <item type="string" name="overlayable3" /> + </policy> + </overlayable> -<overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable"> - <!-- Any overlay on the vendor or product partition can overlay the value of - @string/overlayable3 --> - <policy type="vendor|product"> - <item type="string" name="overlayable3" /> - </policy> -</overlayable> + <overlayable name="OverlayableResources3"> + <policy type="public"> + <item type="string" name="overlayable5" /> + <item type="string" name="overlayable6" /> + <item type="string" name="overlayable7" /> + <item type="string" name="overlayable8" /> + <item type="string" name="overlayable9" /> + <item type="string" name="overlayable10" /> + <item type="string" name="overlayable11" /> + <item type="integer" name="config_int" /> + <item type="id" name="hello_view" /> + <item type="attr" name="max_lines" /> + <item type="layout" name="hello_view" /> + </policy> + </overlayable> </resources>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/overlayable/res/values/public.xml b/libs/androidfw/tests/data/overlayable/res/values/public.xml index 5676d7cc64c9..042a311b2b88 100644 --- a/libs/androidfw/tests/data/overlayable/res/values/public.xml +++ b/libs/androidfw/tests/data/overlayable/res/values/public.xml @@ -20,4 +20,21 @@ <public type="string" name="overlayable2" id="0x7f010002" /> <public type="string" name="overlayable3" id="0x7f010003" /> <public type="string" name="overlayable4" id="0x7f010004" /> + <public type="string" name="overlayable5" id="0x7f010005" /> + <public type="string" name="overlayable6" id="0x7f010006" /> + <public type="string" name="overlayable7" id="0x7f010007" /> + <public type="string" name="overlayable8" id="0x7f010008" /> + <public type="string" name="overlayable9" id="0x7f010009" /> + <public type="string" name="overlayable10" id="0x7f01000a" /> + <public type="string" name="overlayable11" id="0x7f01000b" /> + + <public type="attr" name="max_lines" id="0x7f020000" /> + + <public type="bool" name="config_bool" id="0x7f030000" /> + + <public type="id" name="hello_view" id="0x7f040000" /> + + <public type="integer" name="config_int" id="0x7f050000" /> + + <public type="layout" name="hello_view" id="0x7f060000" /> </resources>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/overlayable/res/values/values.xml b/libs/androidfw/tests/data/overlayable/res/values/values.xml index a86b31282bc9..235772d35fd0 100644 --- a/libs/androidfw/tests/data/overlayable/res/values/values.xml +++ b/libs/androidfw/tests/data/overlayable/res/values/values.xml @@ -20,4 +20,15 @@ <string name="overlayable2">Overlayable Two</string> <string name="overlayable3">Overlayable Three</string> <string name="overlayable4">Overlayable Four</string> + <string name="overlayable5">Overlayable Five</string> + <string name="overlayable6">Overlayable Six</string> + <string name="overlayable7">Overlayable Seven</string> + <string name="overlayable8">Overlayable Eight</string> + <string name="overlayable9">Overlayable Nine</string> + <string name="overlayable10">Overlayable Ten</string> + <string name="overlayable11">Overlayable Eleven</string> + + <integer name="config_int">0</integer> + <bool name="config_bool">false</bool> + <attr name="max_lines" format="integer" /> </resources> diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h index becb38830fb3..c0160c0f78a9 100644 --- a/libs/androidfw/tests/data/system/R.h +++ b/libs/androidfw/tests/data/system/R.h @@ -40,6 +40,13 @@ struct R { number = 0x01030000, // sv }; }; + + struct string { + enum : uint32_t { + no = 0x01040009, + yes = 0x01040013, + }; + }; }; } // namespace android diff --git a/libs/androidfw/tests/data/system/res/values/public.xml b/libs/androidfw/tests/data/system/res/values/public.xml new file mode 100644 index 000000000000..077874d0b0fe --- /dev/null +++ b/libs/androidfw/tests/data/system/res/values/public.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> + +<resources> + <public type="string" name="no" id="0x01040009" /> + <public type="string" name="yes" id="0x01040013" /> +</resources>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/system/res/values/values.xml b/libs/androidfw/tests/data/system/res/values/values.xml new file mode 100644 index 000000000000..0629c1d13795 --- /dev/null +++ b/libs/androidfw/tests/data/system/res/values/values.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 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. +--> + +<resources> + <string name="yes">yes</string> + <string name="no">no</string> +</resources>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/system/system.apk b/libs/androidfw/tests/data/system/system.apk Binary files differindex 9045d6c4de21..1f7e00733366 100644 --- a/libs/androidfw/tests/data/system/system.apk +++ b/libs/androidfw/tests/data/system/system.apk |