From 1a48fa659bd877712dee5611524f903fd22aaae8 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Sun, 10 Jan 2021 08:36:36 -0800 Subject: Dependency injection of AssetProviders into ApkAssets Creates ApkAssets creation methods that allow an AssetsProvider to be specified. During idmap verification and creation, idmap2 currently opens the target package and overlay package several times: 1) When the crc of the package is calculated in idmap2 verify 2) When the manifest of an overlay is parsed 3) When an ApkAssets is opened. Opening large zip files (like framework-res.apk) is slow. If we opened the zip one time as an ApkAssets, the resources.arsc would have to be parsed (which means mmaping/unmapping and touching a lot of resources.arsc pages). This would cause idmap2 to preform unnecessary work just to check the crc of some files. This change allows a ZipAssetsProvider to be created and then moved for the creation of an ApkAssets. The zip file only needs to be opened once and the resources.arsc is not parsed until reading resources is actually necessary. Bug: 172471315 Test: libandroidfw_tests Test: CtsResourcesLoaderTests Change-Id: I940bb2c13844c7f028776a623a9ecef45a4813bf --- libs/androidfw/AssetsProvider.cpp | 398 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100644 libs/androidfw/AssetsProvider.cpp (limited to 'libs/androidfw/AssetsProvider.cpp') diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp new file mode 100644 index 000000000000..23cacf88a6db --- /dev/null +++ b/libs/androidfw/AssetsProvider.cpp @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2021 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/AssetsProvider.h" + +#include + +#include +#include +#include +#include + +namespace android { +namespace { +constexpr const char* kEmptyDebugString = ""; +} // namespace + +std::unique_ptr AssetsProvider::Open(const std::string& path, Asset::AccessMode mode, + bool* file_exists) const { + return OpenInternal(path, mode, file_exists); +} + +std::unique_ptr AssetsProvider::CreateAssetFromFile(const std::string& path) { + base::unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_CLOEXEC)); + if (!fd.ok()) { + LOG(ERROR) << "Failed to open file '" << path << "': " << base::SystemErrorCodeToString(errno); + return {}; + } + + return CreateAssetFromFd(std::move(fd), path.c_str()); +} + +std::unique_ptr AssetsProvider::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") << "': " + << base::SystemErrorCodeToString(errno); + return {}; + } + } + + incfs::IncFsFileMap file_map; + if (!file_map.Create(fd, offset, static_cast(length), path)) { + LOG(ERROR) << "Failed to mmap file '" << ((path != nullptr) ? path : "anon") << "': " + << base::SystemErrorCodeToString(errno); + return {}; + } + + // 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), + Asset::AccessMode::ACCESS_RANDOM, + (path != nullptr) ? base::unique_fd(-1) : std::move(fd)); +} + +ZipAssetsProvider::PathOrDebugName::PathOrDebugName(std::string&& value, bool is_path) + : value_(std::forward(value)), is_path_(is_path) {} + +const std::string* ZipAssetsProvider::PathOrDebugName::GetPath() const { + return is_path_ ? &value_ : nullptr; +} + +const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const { + return value_; +} + +ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, + time_t last_mod_time) + : zip_handle_(handle, ::CloseArchive), + name_(std::forward(path)), + last_mod_time_(last_mod_time) {} + +std::unique_ptr ZipAssetsProvider::Create(std::string path) { + ZipArchiveHandle handle; + if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) { + LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); + CloseArchive(handle); + return {}; + } + + struct stat sb{.st_mtime = -1}; + if (stat(path.c_str(), &sb) < 0) { + // Stat requires execute permissions on all directories path to the file. If the process does + // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will + // always have to return true. + LOG(WARNING) << "Failed to stat file '" << path << "': " + << base::SystemErrorCodeToString(errno); + } + + return std::unique_ptr( + new ZipAssetsProvider(handle, PathOrDebugName{std::move(path), + true /* is_path */}, sb.st_mtime)); +} + +std::unique_ptr ZipAssetsProvider::Create(base::unique_fd fd, + std::string friendly_name, + off64_t offset, + off64_t len) { + ZipArchiveHandle handle; + const int released_fd = fd.release(); + const int32_t result = (len == AssetsProvider::kUnknownLength) + ? ::OpenArchiveFd(released_fd, friendly_name.c_str(), &handle) + : ::OpenArchiveFdRange(released_fd, friendly_name.c_str(), &handle, len, offset); + + if (result != 0) { + LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset + << " and length " << len << ": " << ::ErrorCodeString(result); + CloseArchive(handle); + return {}; + } + + struct stat sb{.st_mtime = -1}; + if (fstat(released_fd, &sb) < 0) { + // Stat requires execute permissions on all directories path to the file. If the process does + // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will + // always have to return true. + LOG(WARNING) << "Failed to fstat file '" << friendly_name << "': " + << base::SystemErrorCodeToString(errno); + } + + return std::unique_ptr( + new ZipAssetsProvider(handle, PathOrDebugName{std::move(friendly_name), + false /* is_path */}, sb.st_mtime)); +} + +std::unique_ptr ZipAssetsProvider::OpenInternal(const std::string& path, + Asset::AccessMode mode, + bool* file_exists) const { + if (file_exists != nullptr) { + *file_exists = false; + } + + ZipEntry entry; + if (FindEntry(zip_handle_.get(), path, &entry) != 0) { + return {}; + } + + if (file_exists != nullptr) { + *file_exists = true; + } + + const int fd = GetFileDescriptor(zip_handle_.get()); + const off64_t fd_offset = GetFileDescriptorOffset(zip_handle_.get()); + incfs::IncFsFileMap asset_map; + if (entry.method == kCompressDeflated) { + if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, + name_.GetDebugName().c_str())) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() + << "'"; + return {}; + } + + std::unique_ptr asset = + Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode); + if (asset == nullptr) { + LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << name_.GetDebugName() + << "'"; + return {}; + } + return asset; + } + + if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, + name_.GetDebugName().c_str())) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'"; + return {}; + } + + base::unique_fd ufd; + if (name_.GetPath() == nullptr) { + // If the zip name does not represent a path, create a new `fd` for the new Asset to own in + // order to create new file descriptors using Asset::openFileDescriptor. If the zip name is a + // path, it will be used to create new file descriptors. + ufd = base::unique_fd(dup(fd)); + if (!ufd.ok()) { + LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << name_.GetDebugName() << "'"; + return {}; + } + } + + auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd)); + if (asset == nullptr) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'"; + return {}; + } + return asset; +} + +bool ZipAssetsProvider::ForEachFile(const std::string& root_path, + const std::function& f) + const { + 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 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; +} + +const std::string& ZipAssetsProvider::GetDebugName() const { + return name_.GetDebugName(); +} + +bool ZipAssetsProvider::IsUpToDate() const { + struct stat sb{}; + if (fstat(GetFileDescriptor(zip_handle_.get()), &sb) < 0) { + // If fstat fails on the zip archive, return true so the zip archive the resource system does + // attempt to refresh the ApkAsset. + return true; + } + return last_mod_time_ == sb.st_mtime; +} + +DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, time_t last_mod_time) + : dir_(std::forward(path)), last_mod_time_(last_mod_time) {} + +std::unique_ptr DirectoryAssetsProvider::Create(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; + } + + if (path[path.size() - 1] != OS_PATH_SEPARATOR) { + path += OS_PATH_SEPARATOR; + } + + return std::unique_ptr(new DirectoryAssetsProvider(std::move(path), + sb.st_mtime)); +} + +std::unique_ptr DirectoryAssetsProvider::OpenInternal(const std::string& path, + Asset::AccessMode /* mode */, + bool* file_exists) const { + const std::string resolved_path = dir_ + path; + if (file_exists != nullptr) { + struct stat sb{}; + *file_exists = (stat(resolved_path.c_str(), &sb) != -1) && S_ISREG(sb.st_mode); + } + + return CreateAssetFromFile(resolved_path); +} + +bool DirectoryAssetsProvider::ForEachFile( + const std::string& /* root_path */, + const std::function& /* f */) + const { + return true; +} + +const std::string& DirectoryAssetsProvider::GetDebugName() const { + return dir_; +} + +bool DirectoryAssetsProvider::IsUpToDate() const { + struct stat sb{}; + if (stat(dir_.c_str(), &sb) < 0) { + // If stat fails on the zip archive, return true so the zip archive the resource system does + // attempt to refresh the ApkAsset. + return true; + } + return last_mod_time_ == sb.st_mtime; +} + +MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr&& primary, + std::unique_ptr&& secondary) + : primary_(std::forward>(primary)), + secondary_(std::forward>(secondary)) { + if (primary_->GetDebugName() == kEmptyDebugString) { + debug_name_ = secondary_->GetDebugName(); + } else if (secondary_->GetDebugName() == kEmptyDebugString) { + debug_name_ = primary_->GetDebugName(); + } else { + debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName(); + } +} + +std::unique_ptr MultiAssetsProvider::Create( + std::unique_ptr&& primary, std::unique_ptr&& secondary) { + if (primary == nullptr || secondary == nullptr) { + return nullptr; + } + return std::unique_ptr(new MultiAssetsProvider(std::move(primary), + std::move(secondary))); +} + +std::unique_ptr MultiAssetsProvider::OpenInternal(const std::string& path, + Asset::AccessMode mode, + bool* file_exists) const { + auto asset = primary_->Open(path, mode, file_exists); + return (asset) ? std::move(asset) : secondary_->Open(path, mode, file_exists); +} + +bool MultiAssetsProvider::ForEachFile(const std::string& root_path, + const std::function& f) + const { + return primary_->ForEachFile(root_path, f) && secondary_->ForEachFile(root_path, f); +} + +const std::string& MultiAssetsProvider::GetDebugName() const { + return debug_name_; +} + +bool MultiAssetsProvider::IsUpToDate() const { + return primary_->IsUpToDate() && secondary_->IsUpToDate(); +} + +std::unique_ptr EmptyAssetsProvider::Create() { + return std::make_unique(); +} + +std::unique_ptr EmptyAssetsProvider::OpenInternal(const std::string& /* path */, + Asset::AccessMode /* mode */, + bool* file_exists) const { + if (file_exists) { + *file_exists = false; + } + return nullptr; +} + +bool EmptyAssetsProvider::ForEachFile( + const std::string& /* root_path */, + const std::function& /* f */) const { + return true; +} + +const std::string& EmptyAssetsProvider::GetDebugName() const { + const static std::string kEmpty = kEmptyDebugString; + return kEmpty; +} + +bool EmptyAssetsProvider::IsUpToDate() const { + return true; +} + +} // namespace android \ No newline at end of file -- cgit v1.2.3-59-g8ed1b From 2ed8bfa7fda3c42280e0816c6cf1af852981723b Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Fri, 8 Jan 2021 13:34:28 -0800 Subject: Add fabricated RRO generation to libidmap2 Fabricated Runtime Resource Overlays are overlays that are generated at runtime and are stored in the data/ partition. The system can fabricate RROs at runtime to dynamically theme the device. Idmaps can now be created from APK RROs and fabricated RROs. Rather than operating on ApkAssets, libidmap2 now operates on abstract resource "containers" that supply resource values. Target resource containers implement methods needed to query overlayable and target overlay information. Currently only APKs can be loaded as target resource containers. Overlay resource containers implement methods to supply the mapping of target resource to overlay value and other overlay information. The format of a fabricated RRO is as follows: 0x00 - 0x04 : fabricated overlay magic (always FRRO) 0x04 - 0x08 : file format version 0x08 - 0x0c : crc of version + proto data 0x0c - EOF : proto fabricated overlay data The magic is used to quickly detect if the file is a fabricated overlay. The version is incremented whenever backwards incompatible changes are made to the proto file format. Idmap must always be able to upgrade fabricated overlays from previous versions to new versions, so all previous versions must be checked into the tree. Bug: 172471315 Test: libidmap2_tests && libandroidfw_tests Change-Id: I4c9f29da278672e5695fb57d131a44c11a835180 --- cmds/idmap2/Android.bp | 58 ++- cmds/idmap2/idmap2/CommandUtils.cpp | 17 +- cmds/idmap2/idmap2/Create.cpp | 25 +- cmds/idmap2/idmap2/CreateMultiple.cpp | 17 +- cmds/idmap2/idmap2/Lookup.cpp | 14 +- cmds/idmap2/idmap2d/Idmap2Service.cpp | 132 +++---- cmds/idmap2/idmap2d/Idmap2Service.h | 42 +- cmds/idmap2/include/idmap2/FabricatedOverlay.h | 103 +++++ cmds/idmap2/include/idmap2/Idmap.h | 35 +- cmds/idmap2/include/idmap2/PrettyPrintVisitor.h | 5 +- cmds/idmap2/include/idmap2/RawPrintVisitor.h | 5 +- cmds/idmap2/include/idmap2/ResourceContainer.h | 105 +++++ cmds/idmap2/include/idmap2/ResourceMapping.h | 114 ++---- cmds/idmap2/include/idmap2/ResourceUtils.h | 33 +- cmds/idmap2/include/idmap2/XmlParser.h | 17 +- cmds/idmap2/include/idmap2/ZipFile.h | 60 --- cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp | 4 - cmds/idmap2/libidmap2/FabricatedOverlay.cpp | 297 ++++++++++++++ cmds/idmap2/libidmap2/Idmap.cpp | 103 ++--- cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp | 32 +- cmds/idmap2/libidmap2/RawPrintVisitor.cpp | 46 +-- cmds/idmap2/libidmap2/ResourceContainer.cpp | 440 +++++++++++++++++++++ cmds/idmap2/libidmap2/ResourceMapping.cpp | 401 ++++--------------- cmds/idmap2/libidmap2/ResourceUtils.cpp | 78 ---- cmds/idmap2/libidmap2/XmlParser.cpp | 18 +- cmds/idmap2/libidmap2/ZipFile.cpp | 70 ---- cmds/idmap2/libidmap2/proto/fabricated_v1.proto | 56 +++ cmds/idmap2/tests/BinaryStreamVisitorTests.cpp | 2 +- cmds/idmap2/tests/FabricatedOverlayTests.cpp | 138 +++++++ cmds/idmap2/tests/IdmapTests.cpp | 176 +++++---- cmds/idmap2/tests/PrettyPrintVisitorTests.cpp | 26 +- cmds/idmap2/tests/RawPrintVisitorTests.cpp | 36 +- cmds/idmap2/tests/ResourceMappingTests.cpp | 95 +++-- cmds/idmap2/tests/ResourceUtilsTests.cpp | 33 +- cmds/idmap2/tests/TestHelpers.h | 55 ++- cmds/idmap2/tests/XmlParserTests.cpp | 22 +- cmds/idmap2/tests/ZipFileTests.cpp | 66 ---- core/jni/android_content_res_ApkAssets.cpp | 2 +- .../test-apps/AppOverlayOne/res/xml/overlays.xml | 1 - libs/androidfw/ApkAssets.cpp | 10 +- libs/androidfw/AssetManager2.cpp | 80 ++-- libs/androidfw/AssetsProvider.cpp | 12 +- libs/androidfw/Idmap.cpp | 27 +- libs/androidfw/ResourceTypes.cpp | 11 + libs/androidfw/include/androidfw/AssetManager2.h | 2 +- libs/androidfw/include/androidfw/AssetsProvider.h | 2 + libs/androidfw/include/androidfw/Idmap.h | 7 +- libs/androidfw/include/androidfw/ResourceTypes.h | 15 +- libs/androidfw/tests/data/overlay/overlay.idmap | Bin 640 -> 636 bytes 49 files changed, 1888 insertions(+), 1257 deletions(-) create mode 100644 cmds/idmap2/include/idmap2/FabricatedOverlay.h create mode 100644 cmds/idmap2/include/idmap2/ResourceContainer.h delete mode 100644 cmds/idmap2/include/idmap2/ZipFile.h create mode 100644 cmds/idmap2/libidmap2/FabricatedOverlay.cpp create mode 100644 cmds/idmap2/libidmap2/ResourceContainer.cpp delete mode 100644 cmds/idmap2/libidmap2/ZipFile.cpp create mode 100644 cmds/idmap2/libidmap2/proto/fabricated_v1.proto create mode 100644 cmds/idmap2/tests/FabricatedOverlayTests.cpp delete mode 100644 cmds/idmap2/tests/ZipFileTests.cpp (limited to 'libs/androidfw/AssetsProvider.cpp') diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index 50f400122fe1..34d38d2b1b04 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -51,13 +51,18 @@ cc_library { static: { enabled: false, }, + static_libs: [ + "libidmap2_protos", + ], shared_libs: [ "libandroidfw", "libbase", "libcutils", + "libidmap2_policies", + "libprotobuf-cpp-lite", "libutils", + "libz", "libziparchive", - "libidmap2_policies", ], }, host: { @@ -68,14 +73,29 @@ cc_library { "libandroidfw", "libbase", "libcutils", + "libidmap2_policies", + "libidmap2_protos", + "libprotobuf-cpp-lite", "libutils", + "libz", "libziparchive", - "libidmap2_policies", ], }, }, } +cc_library { + name: "libidmap2_protos", + srcs: [ + "libidmap2/proto/*.proto", + ], + host_supported: true, + proto: { + type: "lite", + export_proto_headers: true, + }, +} + cc_library { name: "libidmap2_policies", defaults: [ @@ -88,9 +108,6 @@ cc_library { enabled: true, }, android: { - static: { - enabled: false, - }, shared_libs: [ "libandroidfw", ], @@ -119,6 +136,7 @@ cc_test { srcs: [ "tests/BinaryStreamVisitorTests.cpp", "tests/CommandLineOptionsTests.cpp", + "tests/FabricatedOverlayTests.cpp", "tests/FileUtilsTests.cpp", "tests/Idmap2BinaryTests.cpp", "tests/IdmapTests.cpp", @@ -130,20 +148,27 @@ cc_test { "tests/ResourceUtilsTests.cpp", "tests/ResultTests.cpp", "tests/XmlParserTests.cpp", - "tests/ZipFileTests.cpp", ], - static_libs: ["libgmock"], + required: [ + "idmap2", + ], + static_libs: [ + "libgmock", + "libidmap2_protos", + ], target: { android: { shared_libs: [ "libandroidfw", "libbase", "libidmap2", + "libidmap2_policies", "liblog", + "libprotobuf-cpp-lite", "libutils", "libz", + "libz", "libziparchive", - "libidmap2_policies", ], }, host: { @@ -152,10 +177,11 @@ cc_test { "libbase", "libcutils", "libidmap2", + "libidmap2_policies", "liblog", + "libprotobuf-cpp-lite", "libutils", "libziparchive", - "libidmap2_policies", ], shared_libs: [ "libz", @@ -189,6 +215,9 @@ cc_binary { "idmap2/Lookup.cpp", "idmap2/Main.cpp", ], + static_libs: [ + "libidmap2_protos", + ], target: { android: { shared_libs: [ @@ -196,9 +225,11 @@ cc_binary { "libbase", "libcutils", "libidmap2", + "libidmap2_policies", + "libprotobuf-cpp-lite", "libutils", + "libz", "libziparchive", - "libidmap2_policies", ], }, host: { @@ -207,10 +238,11 @@ cc_binary { "libbase", "libcutils", "libidmap2", + "libidmap2_policies", "liblog", + "libprotobuf-cpp-lite", "libutils", "libziparchive", - "libidmap2_policies", ], shared_libs: [ "libz", @@ -236,11 +268,13 @@ cc_binary { "libbinder", "libcutils", "libidmap2", + "libidmap2_policies", + "libprotobuf-cpp-lite", "libutils", "libziparchive", - "libidmap2_policies", ], static_libs: [ + "libidmap2_protos", "libidmap2daidl", ], init_rc: ["idmap2d/idmap2d.rc"], diff --git a/cmds/idmap2/idmap2/CommandUtils.cpp b/cmds/idmap2/idmap2/CommandUtils.cpp index 09867f3a9c20..bf30a76d581a 100644 --- a/cmds/idmap2/idmap2/CommandUtils.cpp +++ b/cmds/idmap2/idmap2/CommandUtils.cpp @@ -25,7 +25,9 @@ using android::idmap2::Error; using android::idmap2::IdmapHeader; +using android::idmap2::OverlayResourceContainer; using android::idmap2::Result; +using android::idmap2::TargetResourceContainer; using android::idmap2::Unit; Result Verify(const std::string& idmap_path, const std::string& target_path, @@ -39,11 +41,20 @@ Result Verify(const std::string& idmap_path, const std::string& target_pat return Error("failed to parse idmap header"); } - const auto header_ok = header->IsUpToDate(target_path, overlay_path, overlay_name, - fulfilled_policies, enforce_overlayable); + auto target = TargetResourceContainer::FromPath(target_path); + if (!target) { + return Error("failed to load target '%s'", target_path.c_str()); + } + + auto overlay = OverlayResourceContainer::FromPath(overlay_path); + if (!overlay) { + return Error("failed to load overlay '%s'", overlay_path.c_str()); + } + + const auto header_ok = header->IsUpToDate(**target, **overlay, overlay_name, fulfilled_policies, + enforce_overlayable); if (!header_ok) { return Error(header_ok.GetError(), "idmap not up to date"); } - return Unit{}; } diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp index c93c717a15d2..977a0bbadafb 100644 --- a/cmds/idmap2/idmap2/Create.cpp +++ b/cmds/idmap2/idmap2/Create.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include "androidfw/ResourceTypes.h" @@ -31,12 +30,13 @@ #include "idmap2/PolicyUtils.h" #include "idmap2/SysTrace.h" -using android::ApkAssets; using android::idmap2::BinaryStreamVisitor; using android::idmap2::CommandLineOptions; using android::idmap2::Error; using android::idmap2::Idmap; +using android::idmap2::OverlayResourceContainer; using android::idmap2::Result; +using android::idmap2::TargetResourceContainer; using android::idmap2::Unit; using android::idmap2::utils::kIdmapFilePermissionMask; using android::idmap2::utils::PoliciesToBitmaskResult; @@ -93,18 +93,18 @@ Result Create(const std::vector& args) { fulfilled_policies |= PolicyFlags::PUBLIC; } - const std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); - if (!target_apk) { - return Error("failed to load apk %s", target_apk_path.c_str()); + const auto target = TargetResourceContainer::FromPath(target_apk_path); + if (!target) { + return Error("failed to load target '%s'", target_apk_path.c_str()); } - const std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path); - if (!overlay_apk) { - return Error("failed to load apk %s", overlay_apk_path.c_str()); + const auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + if (!overlay) { + return Error("failed to load apk overlay '%s'", overlay_apk_path.c_str()); } - const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, overlay_name, - fulfilled_policies, !ignore_overlayable); + const auto idmap = Idmap::FromContainers(**target, **overlay, overlay_name, fulfilled_policies, + !ignore_overlayable); if (!idmap) { return Error(idmap.GetError(), "failed to create idmap"); } @@ -112,13 +112,14 @@ Result Create(const std::vector& args) { umask(kIdmapFilePermissionMask); std::ofstream fout(idmap_path); if (fout.fail()) { - return Error("failed to open idmap path %s", idmap_path.c_str()); + return Error("failed to open idmap path '%s'", idmap_path.c_str()); } + BinaryStreamVisitor visitor(fout); (*idmap)->accept(&visitor); fout.close(); if (fout.fail()) { - return Error("failed to write to idmap path %s", idmap_path.c_str()); + return Error("failed to write to idmap path '%s'", idmap_path.c_str()); } return Unit{}; diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp index 5db391caac30..953d99f8dcfb 100644 --- a/cmds/idmap2/idmap2/CreateMultiple.cpp +++ b/cmds/idmap2/idmap2/CreateMultiple.cpp @@ -34,13 +34,14 @@ #include "idmap2/PolicyUtils.h" #include "idmap2/SysTrace.h" -using android::ApkAssets; using android::base::StringPrintf; using android::idmap2::BinaryStreamVisitor; using android::idmap2::CommandLineOptions; using android::idmap2::Error; using android::idmap2::Idmap; +using android::idmap2::OverlayResourceContainer; using android::idmap2::Result; +using android::idmap2::TargetResourceContainer; using android::idmap2::Unit; using android::idmap2::utils::kIdmapCacheDir; using android::idmap2::utils::kIdmapFilePermissionMask; @@ -91,9 +92,9 @@ Result CreateMultiple(const std::vector& args) { fulfilled_policies |= PolicyFlags::PUBLIC; } - const std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); - if (!target_apk) { - return Error("failed to load apk %s", target_apk_path.c_str()); + const auto target = TargetResourceContainer::FromPath(target_apk_path); + if (!target) { + return Error("failed to load target '%s'", target_apk_path.c_str()); } std::vector idmap_paths; @@ -108,14 +109,14 @@ Result CreateMultiple(const std::vector& args) { // TODO(b/175014391): Support multiple overlay tags in OverlayConfig if (!Verify(idmap_path, target_apk_path, overlay_apk_path, "", fulfilled_policies, !ignore_overlayable)) { - const std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path); - if (!overlay_apk) { + const auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + if (!overlay) { LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str(); continue; } - const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, "", fulfilled_policies, - !ignore_overlayable); + const auto idmap = + Idmap::FromContainers(**target, **overlay, "", fulfilled_policies, !ignore_overlayable); if (!idmap) { LOG(WARNING) << "failed to create idmap"; continue; diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp index 43a1951a5ba9..f41e57cc66d6 100644 --- a/cmds/idmap2/idmap2/Lookup.cpp +++ b/cmds/idmap2/idmap2/Lookup.cpp @@ -37,7 +37,6 @@ #include "idmap2/Result.h" #include "idmap2/SysTrace.h" #include "idmap2/XmlParser.h" -#include "idmap2/ZipFile.h" #include "utils/String16.h" #include "utils/String8.h" @@ -52,10 +51,10 @@ using android::base::StringPrintf; using android::idmap2::CommandLineOptions; using android::idmap2::Error; using android::idmap2::IdmapHeader; +using android::idmap2::OverlayResourceContainer; using android::idmap2::ResourceId; using android::idmap2::Result; using android::idmap2::Unit; -using android::idmap2::utils::ExtractOverlayManifestInfo; namespace { @@ -195,12 +194,17 @@ Result Lookup(const std::vector& args) { } apk_assets.push_back(std::move(target_apk)); - auto manifest_info = ExtractOverlayManifestInfo(idmap_header->GetOverlayPath(), - idmap_header->GetOverlayName()); + auto overlay = OverlayResourceContainer::FromPath(idmap_header->GetOverlayPath()); + if (!overlay) { + return overlay.GetError(); + } + + auto manifest_info = (*overlay)->FindOverlayInfo(idmap_header->GetOverlayName()); if (!manifest_info) { return manifest_info.GetError(); } - target_package_name = manifest_info->target_package; + + target_package_name = (*manifest_info).target_package; } else if (target_path != idmap_header->GetTargetPath()) { return Error("different target APKs (expected target APK %s but %s has target APK %s)", target_path.c_str(), idmap_path.c_str(), idmap_header->GetTargetPath().c_str()); diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index 93537d32299b..f62630e702d4 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -35,17 +35,16 @@ #include "idmap2/Idmap.h" #include "idmap2/Result.h" #include "idmap2/SysTrace.h" -#include "idmap2/ZipFile.h" #include "utils/String8.h" using android::IPCThreadState; using android::base::StringPrintf; using android::binder::Status; using android::idmap2::BinaryStreamVisitor; -using android::idmap2::GetPackageCrc; using android::idmap2::Idmap; using android::idmap2::IdmapHeader; -using android::idmap2::ZipFile; +using android::idmap2::OverlayResourceContainer; +using android::idmap2::TargetResourceContainer; using android::idmap2::utils::kIdmapCacheDir; using android::idmap2::utils::kIdmapFilePermissionMask; using android::idmap2::utils::UidHasWriteAccessToPath; @@ -68,40 +67,24 @@ Status error(const std::string& msg) { PolicyBitmask ConvertAidlArgToPolicyBitmask(int32_t arg) { return static_cast(arg); } - -Status GetCrc(const std::string& apk_path, uint32_t* out_crc) { - const auto zip = ZipFile::Open(apk_path); - if (!zip) { - return error(StringPrintf("failed to open apk %s", apk_path.c_str())); - } - - const auto crc = GetPackageCrc(*zip); - if (!crc) { - return error(crc.GetErrorMessage()); - } - - *out_crc = *crc; - return ok(); -} - } // namespace namespace android::os { -Status Idmap2Service::getIdmapPath(const std::string& overlay_apk_path, +Status Idmap2Service::getIdmapPath(const std::string& overlay_path, int32_t user_id ATTRIBUTE_UNUSED, std::string* _aidl_return) { assert(_aidl_return); - SYSTRACE << "Idmap2Service::getIdmapPath " << overlay_apk_path; - *_aidl_return = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); + SYSTRACE << "Idmap2Service::getIdmapPath " << overlay_path; + *_aidl_return = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path); return ok(); } -Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path, - int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) { +Status Idmap2Service::removeIdmap(const std::string& overlay_path, int32_t user_id ATTRIBUTE_UNUSED, + bool* _aidl_return) { assert(_aidl_return); - SYSTRACE << "Idmap2Service::removeIdmap " << overlay_apk_path; + SYSTRACE << "Idmap2Service::removeIdmap " << overlay_path; const uid_t uid = IPCThreadState::self()->getCallingUid(); - const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); + const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path); if (!UidHasWriteAccessToPath(uid, idmap_path)) { *_aidl_return = false; return error(base::StringPrintf("failed to unlink %s: calling uid %d lacks write access", @@ -115,85 +98,79 @@ Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path, return ok(); } -Status Idmap2Service::verifyIdmap(const std::string& target_apk_path, - const std::string& overlay_apk_path, int32_t fulfilled_policies, - bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED, - bool* _aidl_return) { - SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_apk_path; +Status Idmap2Service::verifyIdmap(const std::string& target_path, const std::string& overlay_path, + int32_t fulfilled_policies, bool enforce_overlayable, + int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) { + SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_path; assert(_aidl_return); - const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); + const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path); std::ifstream fin(idmap_path); const std::unique_ptr header = IdmapHeader::FromBinaryStream(fin); fin.close(); if (!header) { *_aidl_return = false; - return error("failed to parse idmap header"); + LOG(WARNING) << "failed to parse idmap header of '" << idmap_path << "'"; + return ok(); } - uint32_t target_crc; - if (target_apk_path == kFrameworkPath && android_crc_) { - target_crc = *android_crc_; - } else { - auto target_crc_status = GetCrc(target_apk_path, &target_crc); - if (!target_crc_status.isOk()) { - *_aidl_return = false; - return target_crc_status; - } - - // Loading the framework zip can take several milliseconds. Cache the crc of the framework - // resource APK to reduce repeated work during boot. - if (target_apk_path == kFrameworkPath) { - android_crc_ = target_crc; - } + const auto target = GetTargetContainer(target_path); + if (!target) { + *_aidl_return = false; + LOG(WARNING) << "failed to load target '" << target_path << "'"; + return ok(); } - uint32_t overlay_crc; - auto overlay_crc_status = GetCrc(overlay_apk_path, &overlay_crc); - if (!overlay_crc_status.isOk()) { + const auto overlay = OverlayResourceContainer::FromPath(overlay_path); + if (!overlay) { *_aidl_return = false; - return overlay_crc_status; + LOG(WARNING) << "failed to load overlay '" << overlay_path << "'"; + return ok(); } // TODO(162841629): Support passing overlay name to idmap2d verify auto up_to_date = - header->IsUpToDate(target_apk_path, overlay_apk_path, "", target_crc, overlay_crc, + header->IsUpToDate(*GetPointer(*target), **overlay, "", ConvertAidlArgToPolicyBitmask(fulfilled_policies), enforce_overlayable); *_aidl_return = static_cast(up_to_date); - return *_aidl_return ? ok() : error(up_to_date.GetErrorMessage()); + if (!up_to_date) { + LOG(WARNING) << "idmap '" << idmap_path + << "' not up to date : " << up_to_date.GetErrorMessage(); + } + return ok(); } -Status Idmap2Service::createIdmap(const std::string& target_apk_path, - const std::string& overlay_apk_path, int32_t fulfilled_policies, - bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED, +Status Idmap2Service::createIdmap(const std::string& target_path, const std::string& overlay_path, + int32_t fulfilled_policies, bool enforce_overlayable, + int32_t user_id ATTRIBUTE_UNUSED, std::optional* _aidl_return) { assert(_aidl_return); - SYSTRACE << "Idmap2Service::createIdmap " << target_apk_path << " " << overlay_apk_path; + SYSTRACE << "Idmap2Service::createIdmap " << target_path << " " << overlay_path; _aidl_return->reset(); const PolicyBitmask policy_bitmask = ConvertAidlArgToPolicyBitmask(fulfilled_policies); - const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); + const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path); const uid_t uid = IPCThreadState::self()->getCallingUid(); if (!UidHasWriteAccessToPath(uid, idmap_path)) { return error(base::StringPrintf("will not write to %s: calling uid %d lacks write accesss", idmap_path.c_str(), uid)); } - const std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); - if (!target_apk) { - return error("failed to load apk " + target_apk_path); + const auto target = GetTargetContainer(target_path); + if (!target) { + return error("failed to load target '%s'" + target_path); } - const std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path); - if (!overlay_apk) { - return error("failed to load apk " + overlay_apk_path); + const auto overlay = OverlayResourceContainer::FromPath(overlay_path); + if (!overlay) { + return error("failed to load apk overlay '%s'" + overlay_path); } // TODO(162841629): Support passing overlay name to idmap2d create - const auto idmap = - Idmap::FromApkAssets(*target_apk, *overlay_apk, "", policy_bitmask, enforce_overlayable); + const auto idmap = Idmap::FromContainers(*GetPointer(*target), **overlay, "", policy_bitmask, + enforce_overlayable); if (!idmap) { return error(idmap.GetErrorMessage()); } @@ -220,4 +197,25 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path, return ok(); } +idmap2::Result Idmap2Service::GetTargetContainer( + const std::string& target_path) { + if (target_path == kFrameworkPath) { + if (framework_apk_cache_ == nullptr) { + // Initialize the framework APK cache. + auto target = TargetResourceContainer::FromPath(target_path); + if (!target) { + return target.GetError(); + } + framework_apk_cache_ = std::move(*target); + } + return {framework_apk_cache_.get()}; + } + + auto target = TargetResourceContainer::FromPath(target_path); + if (!target) { + return target.GetError(); + } + return {std::move(*target)}; +} + } // namespace android::os diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h index 0127e874b444..869a4d981f42 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.h +++ b/cmds/idmap2/idmap2d/Idmap2Service.h @@ -18,12 +18,13 @@ #define IDMAP2_IDMAP2D_IDMAP2SERVICE_H_ #include +#include #include +#include +#include #include -#include "android/os/BnIdmap2.h" - namespace android::os { class Idmap2Service : public BinderService, public BnIdmap2 { @@ -32,27 +33,44 @@ class Idmap2Service : public BinderService, public BnIdmap2 { return "idmap"; } - binder::Status getIdmapPath(const std::string& overlay_apk_path, int32_t user_id, + binder::Status getIdmapPath(const std::string& overlay_path, int32_t user_id, std::string* _aidl_return) override; - binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id, + binder::Status removeIdmap(const std::string& overlay_path, int32_t user_id, bool* _aidl_return) override; - binder::Status verifyIdmap(const std::string& target_apk_path, - const std::string& overlay_apk_path, int32_t fulfilled_policies, - bool enforce_overlayable, int32_t user_id, + binder::Status verifyIdmap(const std::string& target_path, const std::string& overlay_path, + int32_t fulfilled_policies, bool enforce_overlayable, int32_t user_id, bool* _aidl_return) override; - binder::Status createIdmap(const std::string& target_apk_path, - const std::string& overlay_apk_path, int32_t fulfilled_policies, - bool enforce_overlayable, int32_t user_id, + binder::Status createIdmap(const std::string& target_path, const std::string& overlay_path, + int32_t fulfilled_policies, bool enforce_overlayable, int32_t user_id, std::optional* _aidl_return) override; private: - // Cache the crc of the android framework package since the crc cannot change without a reboot. - std::optional android_crc_; + // idmap2d is killed after a period of inactivity, so any information stored on this class should + // be able to be recalculated if idmap2 dies and restarts. + std::unique_ptr framework_apk_cache_; + + template + using MaybeUniquePtr = std::variant, T*>; + + using TargetResourceContainerPtr = MaybeUniquePtr; + idmap2::Result GetTargetContainer(const std::string& target_path); + + template + WARN_UNUSED static const T* GetPointer(const MaybeUniquePtr& ptr); }; +template +const T* Idmap2Service::GetPointer(const MaybeUniquePtr& ptr) { + auto u = std::get_if(&ptr); + if (u != nullptr) { + return *u; + } + return std::get>(ptr).get(); +} + } // namespace android::os #endif // IDMAP2_IDMAP2D_IDMAP2SERVICE_H_ diff --git a/cmds/idmap2/include/idmap2/FabricatedOverlay.h b/cmds/idmap2/include/idmap2/FabricatedOverlay.h new file mode 100644 index 000000000000..d82315723f71 --- /dev/null +++ b/cmds/idmap2/include/idmap2/FabricatedOverlay.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2021 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 IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H +#define IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H + +#include + +#include +#include +#include +#include + +#include "idmap2/ResourceContainer.h" +#include "idmap2/Result.h" + +namespace android::idmap2 { + +struct FabricatedOverlay { + struct Builder { + Builder(const std::string& package_name, const std::string& name, + const std::string& target_package_name); + + Builder& SetOverlayable(const std::string& name); + + Builder& SetResourceValue(const std::string& resource_name, uint8_t data_type, + uint32_t data_value); + + WARN_UNUSED Result Build(); + + private: + struct Entry { + std::string resource_name; + DataType data_type; + DataValue data_value; + }; + + std::string package_name_; + std::string name_; + std::string target_package_name_; + std::string target_overlayable_; + std::vector entries_; + }; + + Result ToBinaryStream(std::ostream& stream); + static Result FromBinaryStream(std::istream& stream); + + private: + struct SerializedData { + std::unique_ptr data; + size_t data_size; + uint32_t crc; + }; + + Result InitializeData() const; + Result GetCrc() const; + + FabricatedOverlay(pb::FabricatedOverlay&& overlay, std::optional crc_from_disk = {}); + + pb::FabricatedOverlay overlay_pb_; + std::optional crc_from_disk_; + mutable std::optional data_; + + friend struct FabricatedOverlayContainer; +}; + +struct FabricatedOverlayContainer : public OverlayResourceContainer { + static Result> FromPath(std::string path); + static std::unique_ptr FromOverlay(FabricatedOverlay&& overlay); + + // inherited from OverlayResourceContainer + WARN_UNUSED Result FindOverlayInfo(const std::string& name) const override; + WARN_UNUSED Result GetOverlayData(const OverlayManifestInfo& info) const override; + + // inherited from ResourceContainer + WARN_UNUSED Result GetCrc() const override; + WARN_UNUSED const std::string& GetPath() const override; + WARN_UNUSED Result GetResourceName(ResourceId id) const override; + + ~FabricatedOverlayContainer() override; + + private: + FabricatedOverlayContainer(FabricatedOverlay&& overlay, std::string&& path); + FabricatedOverlay overlay_; + std::string path_; +}; + +} // namespace android::idmap2 + +#endif // IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h index 1b815c1197ca..58aff42b1e98 100644 --- a/cmds/idmap2/include/idmap2/Idmap.h +++ b/cmds/idmap2/include/idmap2/Idmap.h @@ -23,8 +23,8 @@ * debug_info * data := data_header target_entry* target_inline_entry* overlay_entry* * string_pool - * data_header := target_package_id overlay_package_id padding(2) target_entry_count - * target_inline_entry_count overlay_entry_count string_pool_index + * data_header := target_entry_count target_inline_entry_count overlay_entry_count + * string_pool_index * target_entry := target_id overlay_id * target_inline_entry := target_id Res_value::size padding(1) Res_value::type * Res_value::value @@ -68,11 +68,10 @@ #include #include "android-base/macros.h" -#include "androidfw/ApkAssets.h" #include "androidfw/ResourceTypes.h" #include "androidfw/StringPiece.h" +#include "idmap2/ResourceContainer.h" #include "idmap2/ResourceMapping.h" -#include "idmap2/ZipFile.h" namespace android::idmap2 { @@ -85,9 +84,6 @@ static constexpr const uint32_t kIdmapMagic = android::kIdmapMagic; // current version of the idmap binary format; must be incremented when the format is changed static constexpr const uint32_t kIdmapCurrentVersion = android::kIdmapCurrentVersion; -// Retrieves a crc generated using all of the files within the zip that can affect idmap generation. -Result GetPackageCrc(const ZipFile& zip_info); - class IdmapHeader { public: static std::unique_ptr FromBinaryStream(std::istream& stream); @@ -135,9 +131,9 @@ class IdmapHeader { // Invariant: anytime the idmap data encoding is changed, the idmap version // field *must* be incremented. Because of this, we know that if the idmap // header is up-to-date the entire file is up-to-date. - Result IsUpToDate(const std::string& target_path, const std::string& overlay_path, - const std::string& overlay_name, PolicyBitmask fulfilled_policies, - bool enforce_overlayable) const; + Result IsUpToDate(const TargetResourceContainer& target, + const OverlayResourceContainer& overlay, const std::string& overlay_name, + PolicyBitmask fulfilled_policies, bool enforce_overlayable) const; Result IsUpToDate(const std::string& target_path, const std::string& overlay_path, const std::string& overlay_name, uint32_t target_crc, @@ -169,14 +165,6 @@ class IdmapData { public: static std::unique_ptr FromBinaryStream(std::istream& stream); - inline PackageId GetTargetPackageId() const { - return target_package_id_; - } - - inline PackageId GetOverlayPackageId() const { - return overlay_package_id_; - } - inline uint32_t GetTargetEntryCount() const { return target_entry_count; } @@ -196,8 +184,6 @@ class IdmapData { void accept(Visitor* v) const; private: - PackageId target_package_id_; - PackageId overlay_package_id_; uint32_t target_entry_count; uint32_t target_entry_inline_count; uint32_t overlay_entry_count; @@ -275,11 +261,10 @@ class Idmap { // file is used; change this in the next version of idmap to use a named // package instead; also update FromApkAssets to take additional parameters: // the target and overlay package names - static Result> FromApkAssets(const ApkAssets& target_apk_assets, - const ApkAssets& overlay_apk_assets, - const std::string& overlay_name, - const PolicyBitmask& fulfilled_policies, - bool enforce_overlayable); + static Result> FromContainers( + const TargetResourceContainer& target, const OverlayResourceContainer& overlay, + const std::string& overlay_name, const PolicyBitmask& fulfilled_policies, + bool enforce_overlayable); const std::unique_ptr& GetHeader() const { return header_; diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h index 2b4c76124175..4464201a1f2e 100644 --- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h +++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h @@ -41,9 +41,8 @@ class PrettyPrintVisitor : public Visitor { private: std::ostream& stream_; - AssetManager2 target_am_; - AssetManager2 overlay_am_; - std::vector> apk_assets_; + std::unique_ptr target_; + std::unique_ptr overlay_; }; } // namespace idmap2 diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h index 45835164ef8e..ebd0d1eb2fbc 100644 --- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h +++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h @@ -49,10 +49,9 @@ class RawPrintVisitor : public Visitor { void pad(size_t padding); std::ostream& stream_; - std::vector> apk_assets_; - AssetManager2 target_am_; - AssetManager2 overlay_am_; size_t offset_; + std::unique_ptr target_; + std::unique_ptr overlay_; }; } // namespace idmap2 diff --git a/cmds/idmap2/include/idmap2/ResourceContainer.h b/cmds/idmap2/include/idmap2/ResourceContainer.h new file mode 100644 index 000000000000..7a22dc668e62 --- /dev/null +++ b/cmds/idmap2/include/idmap2/ResourceContainer.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2021 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 IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H +#define IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H + +#include +#include +#include + +#include "idmap2/Policies.h" +#include "idmap2/ResourceUtils.h" + +namespace android::idmap2 { + +struct ResourceContainer { + WARN_UNUSED virtual Result GetCrc() const = 0; + WARN_UNUSED virtual const std::string& GetPath() const = 0; + WARN_UNUSED virtual Result GetResourceName(ResourceId id) const = 0; + + virtual ~ResourceContainer() = default; +}; + +struct TargetResourceContainer : public ResourceContainer { + static Result> FromPath(std::string path); + + WARN_UNUSED virtual Result DefinesOverlayable() const = 0; + WARN_UNUSED virtual Result GetOverlayableInfo( + ResourceId id) const = 0; + WARN_UNUSED virtual Result GetResourceId(const std::string& name) const = 0; + + ~TargetResourceContainer() override = default; +}; + +struct OverlayManifestInfo { + std::string name; // NOLINT(misc-non-private-member-variables-in-classes) + std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes) + std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes) + ResourceId resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes) +}; + +struct OverlayData { + struct ResourceIdValue { + // The overlay resource id. + ResourceId overlay_id; + + // Whether or not references to the overlay resource id should be rewritten to its corresponding + // target id during resource resolution. + bool rewrite_id; + }; + + struct Value { + std::string resource_name; + std::variant value; + }; + + struct InlineStringPoolData { + // The binary data of the android::ResStringPool string pool. + std::unique_ptr data; + + // The length of the binary data. + uint32_t data_length; + + // The offset added to TargetValue#data_value (the index of the string in the inline string + // pool) in order to prevent the indices of the overlay resource table string pool from + // colliding with the inline string pool indices. + uint32_t string_pool_offset; + }; + + // The overlay's mapping of target resource name to overlaid value. Use a vector to enforce that + // the overlay pairs are inserted into the ResourceMapping in the specified ordered. + std::vector pairs; + + // If the overlay maps a target resource to a string literal (not a string resource), then the + // this field contains information about the string pool in which the string literal resides so it + // can be inlined into an idmap. + std::optional string_pool_data; +}; + +struct OverlayResourceContainer : public ResourceContainer { + static Result> FromPath(std::string path); + + WARN_UNUSED virtual Result FindOverlayInfo( + const std::string& name) const = 0; + WARN_UNUSED virtual Result GetOverlayData(const OverlayManifestInfo& info) const = 0; + + ~OverlayResourceContainer() override = default; +}; + +} // namespace android::idmap2 + +#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h index f66916ccb78c..5a0a384f75a3 100644 --- a/cmds/idmap2/include/idmap2/ResourceMapping.h +++ b/cmds/idmap2/include/idmap2/ResourceMapping.h @@ -17,30 +17,22 @@ #ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_ #define IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_ +#include + #include #include #include -#include "androidfw/ApkAssets.h" +#include "idmap2/FabricatedOverlay.h" #include "idmap2/LogInfo.h" #include "idmap2/Policies.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" #include "idmap2/XmlParser.h" -using android::idmap2::utils::OverlayManifestInfo; - using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask; namespace android::idmap2 { - -struct TargetValue { - typedef uint8_t DataType; - typedef uint32_t DataValue; - DataType data_type; - DataValue data_value; -}; - using TargetResourceMap = std::map>; using OverlayResourceMap = std::map; @@ -49,94 +41,60 @@ class ResourceMapping { // Creates a ResourceMapping using the target and overlay APKs. Setting enforce_overlayable to // `false` disables all overlayable and policy enforcement: this is intended for backwards // compatibility pre-Q and unit tests. - static Result FromApkAssets(const ApkAssets& target_apk_assets, - const ApkAssets& overlay_apk_assets, - const OverlayManifestInfo& overlay_info, - const PolicyBitmask& fulfilled_policies, - bool enforce_overlayable, LogInfo& log_info); + static Result FromContainers(const TargetResourceContainer& target, + const OverlayResourceContainer& overlay, + const OverlayManifestInfo& overlay_info, + const PolicyBitmask& fulfilled_policies, + bool enforce_overlayable, LogInfo& log_info); // Retrieves the mapping of target resource id to overlay value. - inline const TargetResourceMap& GetTargetToOverlayMap() const { - return target_map_; - } + WARN_UNUSED const TargetResourceMap& GetTargetToOverlayMap() const; // Retrieves the mapping of overlay resource id to target resource id. This allows a reference to // an overlay resource to appear as a reference to its corresponding target resource at runtime. - OverlayResourceMap GetOverlayToTargetMap() const; - - // Retrieves the build-time package id of the target package. - inline uint32_t GetTargetPackageId() const { - return target_package_id_; - } - - // Retrieves the build-time package id of the overlay package. - inline uint32_t GetOverlayPackageId() const { - return overlay_package_id_; - } + WARN_UNUSED const OverlayResourceMap& GetOverlayToTargetMap() const; // Retrieves the offset that was added to the index of inline string overlay values so the indices // do not collide with the indices of the overlay resource table string pool. - inline uint32_t GetStringPoolOffset() const { - return string_pool_offset_; - } + WARN_UNUSED uint32_t GetStringPoolOffset() const; // Retrieves the raw string pool data from the xml referenced in android:resourcesMap. - inline const StringPiece GetStringPoolData() const { - return StringPiece(reinterpret_cast(string_pool_data_.get()), - string_pool_data_length_); - } + WARN_UNUSED StringPiece GetStringPoolData() const; private: ResourceMapping() = default; - // Maps a target resource id to an overlay resource id. - // If rewrite_overlay_reference is `true` then references to the overlay - // resource should appear as a reference to its corresponding target resource at runtime. - Result AddMapping(ResourceId target_resource, ResourceId overlay_resource, - bool rewrite_overlay_reference); - - // Maps a target resource id to a data type and value combination. - // The `data_type` is the runtime format of the data value (see Res_value::dataType). - Result AddMapping(ResourceId target_resource, TargetValue::DataType data_type, - TargetValue::DataValue data_value); - - // Removes the overlay value mapping for the target resource. - void RemoveMapping(ResourceId target_resource); - - // Parses the mapping of target resources to overlay resources to generate a ResourceMapping. - static Result CreateResourceMapping(const AssetManager2* target_am, - const LoadedPackage* target_package, - const LoadedPackage* overlay_package, - size_t string_pool_offset, - const XmlParser& overlay_parser, - LogInfo& log_info); - - // Generates a ResourceMapping that maps target resources to overlay resources by name. To overlay - // a target resource, a resource must exist in the overlay with the same type and entry name as - // the target resource. - static Result CreateResourceMappingLegacy(const AssetManager2* target_am, - const AssetManager2* overlay_am, - const LoadedPackage* target_package, - const LoadedPackage* overlay_package, - LogInfo& log_info); - - // Removes resources that do not pass policy or overlayable checks of the target package. - void FilterOverlayableResources(const AssetManager2* target_am, - const LoadedPackage* target_package, - const LoadedPackage* overlay_package, - const OverlayManifestInfo& overlay_info, - const PolicyBitmask& fulfilled_policies, LogInfo& log_info); + // Maps a target resource id to an overlay resource id or a android::Res_value value. + // + // If `allow_rewriting_` is true, then the overlay-to-target map will be populated if the target + // resource id is mapped to an overlay resource id. + Result AddMapping(ResourceId target_resource, + const std::variant& value); TargetResourceMap target_map_; - std::multimap overlay_map_; - - uint32_t target_package_id_ = 0; - uint32_t overlay_package_id_ = 0; + OverlayResourceMap overlay_map_; uint32_t string_pool_offset_ = 0; uint32_t string_pool_data_length_ = 0; std::unique_ptr string_pool_data_ = nullptr; }; +inline const TargetResourceMap& ResourceMapping::GetTargetToOverlayMap() const { + return target_map_; +} + +inline const OverlayResourceMap& ResourceMapping::GetOverlayToTargetMap() const { + return overlay_map_; +} + +inline uint32_t ResourceMapping::GetStringPoolOffset() const { + return string_pool_offset_; +} + +inline StringPiece ResourceMapping::GetStringPoolData() const { + return StringPiece(reinterpret_cast(string_pool_data_.get()), + string_pool_data_length_); +} + } // namespace android::idmap2 #endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_ diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h index cd14d3e7254c..a0202dfee473 100644 --- a/cmds/idmap2/include/idmap2/ResourceUtils.h +++ b/cmds/idmap2/include/idmap2/ResourceUtils.h @@ -22,19 +22,26 @@ #include "androidfw/AssetManager2.h" #include "idmap2/Result.h" -#include "idmap2/ZipFile.h" namespace android::idmap2 { -// use typedefs to let the compiler warn us about implicit casts -typedef uint32_t ResourceId; // 0xpptteeee -typedef uint8_t PackageId; // pp in 0xpptteeee -typedef uint8_t TypeId; // tt in 0xpptteeee -typedef uint16_t EntryId; // eeee in 0xpptteeee - #define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16) #define EXTRACT_ENTRY(resid) (0x0000ffff & (resid)) +// use typedefs to let the compiler warn us about implicit casts +using ResourceId = uint32_t; // 0xpptteeee +using PackageId = uint8_t; // pp in 0xpptteeee +using TypeId = uint8_t; // tt in 0xpptteeee +using EntryId = uint16_t; // eeee in 0xpptteeee + +using DataType = uint8_t; // Res_value::dataType +using DataValue = uint32_t; // Res_value::data + +struct TargetValue { + DataType data_type; + DataValue data_value; +}; + namespace utils { // Returns whether the Res_value::data_type represents a dynamic or regular resource reference. @@ -43,20 +50,10 @@ bool IsReference(uint8_t data_type); // Converts the Res_value::data_type to a human-readable string representation. StringPiece DataTypeToString(uint8_t data_type); -struct OverlayManifestInfo { - std::string name; // NOLINT(misc-non-private-member-variables-in-classes) - std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes) - std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes) - uint32_t resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes) -}; - -Result ExtractOverlayManifestInfo(const std::string& path, - const std::string& name); - +// Retrieves the type and entry name of the resource in the AssetManager in the form type/entry. Result ResToTypeEntryName(const AssetManager2& am, ResourceId resid); } // namespace utils - } // namespace android::idmap2 #endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_ diff --git a/cmds/idmap2/include/idmap2/XmlParser.h b/cmds/idmap2/include/idmap2/XmlParser.h index 1c74ab3bb691..c968a5e6c04f 100644 --- a/cmds/idmap2/include/idmap2/XmlParser.h +++ b/cmds/idmap2/include/idmap2/XmlParser.h @@ -30,8 +30,7 @@ namespace android::idmap2 { -class XmlParser { - public: +struct XmlParser { using Event = ResXMLParser::event_code_t; class iterator; @@ -127,23 +126,19 @@ class XmlParser { }; // Creates a new xml parser beginning at the first tag. - static Result> Create(const void* data, size_t size, - bool copy_data = false); - ~XmlParser(); + static Result Create(const void* data, size_t size, bool copy_data = false); inline iterator tree_iterator() const { - return iterator(tree_); + return iterator(*tree_); } inline const ResStringPool& get_strings() const { - return tree_.getStrings(); + return tree_->getStrings(); } private: - XmlParser() = default; - mutable ResXMLTree tree_; - - DISALLOW_COPY_AND_ASSIGN(XmlParser); + explicit XmlParser(std::unique_ptr tree); + mutable std::unique_ptr tree_; }; } // namespace android::idmap2 diff --git a/cmds/idmap2/include/idmap2/ZipFile.h b/cmds/idmap2/include/idmap2/ZipFile.h deleted file mode 100644 index 8f50e36256f6..000000000000 --- a/cmds/idmap2/include/idmap2/ZipFile.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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. - */ - -#ifndef IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_ -#define IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_ - -#include -#include - -#include "android-base/macros.h" -#include "idmap2/Result.h" -#include "ziparchive/zip_archive.h" - -namespace android::idmap2 { - -struct MemoryChunk { - size_t size; - uint8_t buf[0]; - - static std::unique_ptr Allocate(size_t size); - - private: - MemoryChunk() { - } -}; - -class ZipFile { - public: - static std::unique_ptr Open(const std::string& path); - - std::unique_ptr Uncompress(const std::string& entryPath) const; - Result Crc(const std::string& entryPath) const; - - ~ZipFile(); - - private: - explicit ZipFile(const ::ZipArchiveHandle handle) : handle_(handle) { - } - - const ::ZipArchiveHandle handle_; - - DISALLOW_COPY_AND_ASSIGN(ZipFile); -}; - -} // namespace android::idmap2 - -#endif // IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_ diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp index c16310792d12..3bbe9d91deb6 100644 --- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp +++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp @@ -87,10 +87,6 @@ void BinaryStreamVisitor::visit(const IdmapData& data) { } void BinaryStreamVisitor::visit(const IdmapData::Header& header) { - Write8(header.GetTargetPackageId()); - Write8(header.GetOverlayPackageId()); - Write8(0U); // padding - Write8(0U); // padding Write32(header.GetTargetEntryCount()); Write32(header.GetTargetInlineEntryCount()); Write32(header.GetOverlayEntryCount()); diff --git a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp new file mode 100644 index 000000000000..4a348ac232bc --- /dev/null +++ b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2021 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 "idmap2/FabricatedOverlay.h" + +#include +#include +#include +#include +#include + +#include + +namespace android::idmap2 { + +namespace { +bool Read32(std::istream& stream, uint32_t* out) { + uint32_t value; + if (stream.read(reinterpret_cast(&value), sizeof(uint32_t))) { + *out = dtohl(value); + return true; + } + return false; +} + +void Write32(std::ostream& stream, uint32_t value) { + uint32_t x = htodl(value); + stream.write(reinterpret_cast(&x), sizeof(uint32_t)); +} +} // namespace + +FabricatedOverlay::FabricatedOverlay(pb::FabricatedOverlay&& overlay, + std::optional crc_from_disk) + : overlay_pb_(std::forward(overlay)), crc_from_disk_(crc_from_disk) { +} + +FabricatedOverlay::Builder::Builder(const std::string& package_name, const std::string& name, + const std::string& target_package_name) { + package_name_ = package_name; + name_ = name; + target_package_name_ = target_package_name; +} + +FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetOverlayable(const std::string& name) { + target_overlayable_ = name; + return *this; +} + +FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue( + const std::string& resource_name, uint8_t data_type, uint32_t data_value) { + entries_.emplace_back(Entry{resource_name, data_type, data_value}); + return *this; +} + +Result FabricatedOverlay::Builder::Build() { + std::map>> entries; + for (const auto& res_entry : entries_) { + StringPiece package_substr; + StringPiece type_name; + StringPiece entry_name; + if (!android::ExtractResourceName(StringPiece(res_entry.resource_name), &package_substr, + &type_name, &entry_name)) { + return Error("failed to parse resource name '%s'", res_entry.resource_name.c_str()); + } + + std::string package_name = + package_substr.empty() ? target_package_name_ : package_substr.to_string(); + if (type_name.empty()) { + return Error("resource name '%s' missing type name", res_entry.resource_name.c_str()); + } + + if (entry_name.empty()) { + return Error("resource name '%s' missing entry name", res_entry.resource_name.c_str()); + } + + auto package = entries.find(package_name); + if (package == entries.end()) { + package = entries + .insert(std::make_pair<>( + package_name, std::map>())) + .first; + } + + auto type = package->second.find(type_name.to_string()); + if (type == package->second.end()) { + type = + package->second + .insert(std::make_pair<>(type_name.to_string(), std::map())) + .first; + } + + auto entry = type->second.find(entry_name.to_string()); + if (entry == type->second.end()) { + entry = type->second.insert(std::make_pair<>(entry_name.to_string(), TargetValue())).first; + } + + entry->second = TargetValue{res_entry.data_type, res_entry.data_value}; + } + + pb::FabricatedOverlay overlay_pb; + overlay_pb.set_package_name(package_name_); + overlay_pb.set_name(name_); + overlay_pb.set_target_package_name(target_package_name_); + overlay_pb.set_target_overlayable(target_overlayable_); + + for (const auto& package : entries) { + auto package_pb = overlay_pb.add_packages(); + package_pb->set_name(package.first); + + for (const auto& type : package.second) { + auto type_pb = package_pb->add_types(); + type_pb->set_name(type.first); + + for (const auto& entry : type.second) { + auto entry_pb = type_pb->add_entries(); + entry_pb->set_name(entry.first); + pb::ResourceValue* value = entry_pb->mutable_res_value(); + value->set_data_type(entry.second.data_type); + value->set_data_value(entry.second.data_value); + } + } + } + + return FabricatedOverlay(std::move(overlay_pb)); +} + +Result FabricatedOverlay::FromBinaryStream(std::istream& stream) { + uint32_t magic; + if (!Read32(stream, &magic)) { + return Error("Failed to read fabricated overlay magic."); + } + + if (magic != kFabricatedOverlayMagic) { + return Error("Not a fabricated overlay file."); + } + + uint32_t version; + if (!Read32(stream, &version)) { + return Error("Failed to read fabricated overlay version."); + } + + if (version != 1) { + return Error("Invalid fabricated overlay version '%u'.", version); + } + + uint32_t crc; + if (!Read32(stream, &crc)) { + return Error("Failed to read fabricated overlay version."); + } + + pb::FabricatedOverlay overlay{}; + if (!overlay.ParseFromIstream(&stream)) { + return Error("Failed read fabricated overlay proto."); + } + + // If the proto version is the latest version, then the contents of the proto must be the same + // when the proto is re-serialized; otherwise, the crc must be calculated because migrating the + // proto to the latest version will likely change the contents of the fabricated overlay. + return FabricatedOverlay(std::move(overlay), version == kFabricatedOverlayCurrentVersion + ? std::optional(crc) + : std::nullopt); +} + +Result FabricatedOverlay::InitializeData() const { + if (!data_.has_value()) { + auto size = overlay_pb_.ByteSizeLong(); + auto data = std::unique_ptr(new uint8_t[size]); + + // Ensure serialization is deterministic + google::protobuf::io::ArrayOutputStream array_stream(data.get(), size); + google::protobuf::io::CodedOutputStream output_stream(&array_stream); + output_stream.SetSerializationDeterministic(true); + overlay_pb_.SerializeWithCachedSizes(&output_stream); + if (output_stream.HadError() || size != output_stream.ByteCount()) { + return Error("Failed to serialize fabricated overlay."); + } + + // Calculate the crc using the proto data and the version. + uint32_t crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, reinterpret_cast(&kFabricatedOverlayCurrentVersion), + sizeof(uint32_t)); + crc = crc32(crc, data.get(), size); + data_ = SerializedData{std::move(data), size, crc}; + } + return &(*data_); +} +Result FabricatedOverlay::GetCrc() const { + if (crc_from_disk_.has_value()) { + return *crc_from_disk_; + } + auto data = InitializeData(); + if (!data) { + return data.GetError(); + } + return (*data)->crc; +} + +Result FabricatedOverlay::ToBinaryStream(std::ostream& stream) { + auto data = InitializeData(); + if (!data) { + return data.GetError(); + } + + Write32(stream, kFabricatedOverlayMagic); + Write32(stream, kFabricatedOverlayCurrentVersion); + Write32(stream, (*data)->crc); + stream.write(reinterpret_cast((*data)->data.get()), (*data)->data_size); + if (stream.bad()) { + return Error("Failed to write serialized fabricated overlay."); + } + + return Unit{}; +} + +using FabContainer = FabricatedOverlayContainer; +FabContainer::FabricatedOverlayContainer(FabricatedOverlay&& overlay, std::string&& path) + : overlay_(std::forward(overlay)), path_(std::forward(path)) { +} + +FabContainer::~FabricatedOverlayContainer() = default; + +Result> FabContainer::FromPath(std::string path) { + std::fstream fin(path); + auto overlay = FabricatedOverlay::FromBinaryStream(fin); + if (!overlay) { + return overlay.GetError(); + } + return std::unique_ptr( + new FabricatedOverlayContainer(std::move(*overlay), std::move(path))); +} + +std::unique_ptr FabContainer::FromOverlay(FabricatedOverlay&& overlay) { + return std::unique_ptr( + new FabricatedOverlayContainer(std::move(overlay), {} /* path */)); +} + +Result FabContainer::FindOverlayInfo(const std::string& name) const { + const pb::FabricatedOverlay& overlay_pb = overlay_.overlay_pb_; + if (name != overlay_pb.name()) { + return Error("Failed to find name '%s' in fabricated overlay", name.c_str()); + } + + return OverlayManifestInfo{ + .name = overlay_pb.name(), + .target_package = overlay_pb.target_package_name(), + .target_name = overlay_pb.target_overlayable(), + }; +} + +Result FabContainer::GetOverlayData(const OverlayManifestInfo& info) const { + const pb::FabricatedOverlay& overlay_pb = overlay_.overlay_pb_; + if (info.name != overlay_pb.name()) { + return Error("Failed to find name '%s' in fabricated overlay", info.name.c_str()); + } + + OverlayData result{}; + for (const auto& package : overlay_pb.packages()) { + for (const auto& type : package.types()) { + for (const auto& entry : type.entries()) { + auto name = base::StringPrintf("%s:%s/%s", package.name().c_str(), type.name().c_str(), + entry.name().c_str()); + const auto& res_value = entry.res_value(); + result.pairs.emplace_back(OverlayData::Value{ + name, TargetValue{.data_type = static_cast(res_value.data_type()), + .data_value = res_value.data_value()}}); + } + } + } + return result; +} + +Result FabContainer::GetCrc() const { + return overlay_.GetCrc(); +} + +const std::string& FabContainer::GetPath() const { + return path_; +} + +Result FabContainer::GetResourceName(ResourceId /* id */) const { + return Error("Fabricated overlay does not contain resources."); +} + +} // namespace android::idmap2 \ No newline at end of file diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index a0cc407fc948..6515d5516d83 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -20,23 +20,16 @@ #include #include #include -#include #include -#include #include #include -#include #include "android-base/macros.h" -#include "android-base/stringprintf.h" #include "androidfw/AssetManager2.h" #include "idmap2/ResourceMapping.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" #include "idmap2/SysTrace.h" -#include "idmap2/ZipFile.h" -#include "utils/String16.h" -#include "utils/String8.h" namespace android::idmap2 { @@ -93,22 +86,13 @@ bool WARN_UNUSED ReadString(std::istream& stream, std::string* out) { } // namespace -Result GetPackageCrc(const ZipFile& zip) { - const Result a = zip.Crc("resources.arsc"); - const Result b = zip.Crc("AndroidManifest.xml"); - return a && b - ? Result(*a ^ *b) - : Error("failed to get CRC for \"%s\"", a ? "AndroidManifest.xml" : "resources.arsc"); -} - std::unique_ptr IdmapHeader::FromBinaryStream(std::istream& stream) { std::unique_ptr idmap_header(new IdmapHeader()); if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_)) { return nullptr; } - if (idmap_header->magic_ != kIdmapMagic || - idmap_header->version_ != kIdmapCurrentVersion) { + if (idmap_header->magic_ != kIdmapMagic || idmap_header->version_ != kIdmapCurrentVersion) { // Do not continue parsing if the file is not a current version idmap. return nullptr; } @@ -127,32 +111,22 @@ std::unique_ptr IdmapHeader::FromBinaryStream(std::istream& s return std::move(idmap_header); } -Result IdmapHeader::IsUpToDate(const std::string& target_path, - const std::string& overlay_path, +Result IdmapHeader::IsUpToDate(const TargetResourceContainer& target, + const OverlayResourceContainer& overlay, const std::string& overlay_name, PolicyBitmask fulfilled_policies, bool enforce_overlayable) const { - const std::unique_ptr target_zip = ZipFile::Open(target_path); - if (!target_zip) { - return Error("failed to open target %s", target_path.c_str()); - } - - const Result target_crc = GetPackageCrc(*target_zip); + const Result target_crc = target.GetCrc(); if (!target_crc) { return Error("failed to get target crc"); } - const std::unique_ptr overlay_zip = ZipFile::Open(overlay_path); - if (!overlay_zip) { - return Error("failed to overlay target %s", overlay_path.c_str()); - } - - const Result overlay_crc = GetPackageCrc(*overlay_zip); + const Result overlay_crc = overlay.GetCrc(); if (!overlay_crc) { return Error("failed to get overlay crc"); } - return IsUpToDate(target_path, overlay_path, overlay_name, *target_crc, *overlay_crc, + return IsUpToDate(target.GetPath(), overlay.GetPath(), overlay_name, *target_crc, *overlay_crc, fulfilled_policies, enforce_overlayable); } @@ -209,11 +183,7 @@ Result IdmapHeader::IsUpToDate(const std::string& target_path, std::unique_ptr IdmapData::Header::FromBinaryStream(std::istream& stream) { std::unique_ptr idmap_data_header(new IdmapData::Header()); - - uint8_t padding; - if (!Read8(stream, &idmap_data_header->target_package_id_) || - !Read8(stream, &idmap_data_header->overlay_package_id_) || !Read8(stream, &padding) || - !Read8(stream, &padding) || !Read32(stream, &idmap_data_header->target_entry_count) || + if (!Read32(stream, &idmap_data_header->target_entry_count) || !Read32(stream, &idmap_data_header->target_entry_inline_count) || !Read32(stream, &idmap_data_header->overlay_entry_count) || !Read32(stream, &idmap_data_header->string_pool_index_offset)) { @@ -321,8 +291,6 @@ Result> IdmapData::FromResourceMapping( } std::unique_ptr data_header(new IdmapData::Header()); - data_header->target_package_id_ = resource_mapping.GetTargetPackageId(); - data_header->overlay_package_id_ = resource_mapping.GetOverlayPackageId(); data_header->target_entry_count = static_cast(data->target_entries_.size()); data_header->target_entry_inline_count = static_cast(data->target_inline_entries_.size()); @@ -332,57 +300,46 @@ Result> IdmapData::FromResourceMapping( return {std::move(data)}; } -Result> Idmap::FromApkAssets(const ApkAssets& target_apk_assets, - const ApkAssets& overlay_apk_assets, - const std::string& overlay_name, - const PolicyBitmask& fulfilled_policies, - bool enforce_overlayable) { +Result> Idmap::FromContainers(const TargetResourceContainer& target, + const OverlayResourceContainer& overlay, + const std::string& overlay_name, + const PolicyBitmask& fulfilled_policies, + bool enforce_overlayable) { SYSTRACE << "Idmap::FromApkAssets"; - const std::string& target_apk_path = target_apk_assets.GetPath(); - const std::string& overlay_apk_path = overlay_apk_assets.GetPath(); - - const std::unique_ptr target_zip = ZipFile::Open(target_apk_path); - if (!target_zip) { - return Error("failed to open target as zip"); - } - - const std::unique_ptr overlay_zip = ZipFile::Open(overlay_apk_path); - if (!overlay_zip) { - return Error("failed to open overlay as zip"); - } - std::unique_ptr header(new IdmapHeader()); header->magic_ = kIdmapMagic; header->version_ = kIdmapCurrentVersion; - Result crc = GetPackageCrc(*target_zip); - if (!crc) { - return Error(crc.GetError(), "failed to get zip CRC for target"); + const auto target_crc = target.GetCrc(); + if (!target_crc) { + return Error(target_crc.GetError(), "failed to get zip CRC for '%s'", target.GetPath().data()); } - header->target_crc_ = *crc; + header->target_crc_ = *target_crc; - crc = GetPackageCrc(*overlay_zip); - if (!crc) { - return Error(crc.GetError(), "failed to get zip CRC for overlay"); + const auto overlay_crc = overlay.GetCrc(); + if (!overlay_crc) { + return Error(overlay_crc.GetError(), "failed to get zip CRC for '%s'", + overlay.GetPath().data()); } - header->overlay_crc_ = *crc; + header->overlay_crc_ = *overlay_crc; + header->fulfilled_policies_ = fulfilled_policies; header->enforce_overlayable_ = enforce_overlayable; - header->target_path_ = target_apk_path; - header->overlay_path_ = overlay_apk_path; + header->target_path_ = target.GetPath(); + header->overlay_path_ = overlay.GetPath(); header->overlay_name_ = overlay_name; - auto info = utils::ExtractOverlayManifestInfo(overlay_apk_path, overlay_name); + auto info = overlay.FindOverlayInfo(overlay_name); if (!info) { - return info.GetError(); + return Error(info.GetError(), "failed to get overlay info for '%s'", overlay.GetPath().data()); } LogInfo log_info; - auto resource_mapping = - ResourceMapping::FromApkAssets(target_apk_assets, overlay_apk_assets, *info, - fulfilled_policies, enforce_overlayable, log_info); + auto resource_mapping = ResourceMapping::FromContainers( + target, overlay, *info, fulfilled_policies, enforce_overlayable, log_info); if (!resource_mapping) { - return resource_mapping.GetError(); + return Error(resource_mapping.GetError(), "failed to generate resource map for '%s'", + overlay.GetPath().data()); } auto idmap_data = IdmapData::FromResourceMapping(*resource_mapping); diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp index 7e090a983f95..721612cc567b 100644 --- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp @@ -27,8 +27,6 @@ namespace android::idmap2 { -#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry)) - #define TAB " " void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) { @@ -36,8 +34,8 @@ void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) { void PrettyPrintVisitor::visit(const IdmapHeader& header) { stream_ << "Paths:" << std::endl - << TAB "target apk path : " << header.GetTargetPath() << std::endl - << TAB "overlay apk path : " << header.GetOverlayPath() << std::endl; + << TAB "target path : " << header.GetTargetPath() << std::endl + << TAB "overlay path : " << header.GetOverlayPath() << std::endl; if (!header.GetOverlayName().empty()) { stream_ << "Overlay name: " << header.GetOverlayName() << std::endl; @@ -53,14 +51,11 @@ void PrettyPrintVisitor::visit(const IdmapHeader& header) { } } - if (auto target_apk_ = ApkAssets::Load(header.GetTargetPath())) { - target_am_.SetApkAssets({target_apk_.get()}); - apk_assets_.push_back(std::move(target_apk_)); + if (auto target = TargetResourceContainer::FromPath(header.GetTargetPath())) { + target_ = std::move(*target); } - - if (auto overlay_apk = ApkAssets::Load(header.GetOverlayPath())) { - overlay_am_.SetApkAssets({overlay_apk.get()}); - apk_assets_.push_back(std::move(overlay_apk)); + if (auto overlay = OverlayResourceContainer::FromPath(header.GetOverlayPath())) { + overlay_ = std::move(*overlay); } stream_ << "Mapping:" << std::endl; @@ -72,23 +67,20 @@ void PrettyPrintVisitor::visit(const IdmapData::Header& header ATTRIBUTE_UNUSED) void PrettyPrintVisitor::visit(const IdmapData& data) { static constexpr const char* kUnknownResourceName = "???"; - const bool target_package_loaded = !target_am_.GetApkAssets().empty(); - const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty(); - const ResStringPool string_pool(data.GetStringPoolData().data(), data.GetStringPoolData().size()); const size_t string_pool_offset = data.GetHeader()->GetStringPoolIndexOffset(); for (const auto& target_entry : data.GetTargetEntries()) { std::string target_name = kUnknownResourceName; - if (target_package_loaded) { - if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) { + if (target_ != nullptr) { + if (auto name = target_->GetResourceName(target_entry.target_id)) { target_name = *name; } } std::string overlay_name = kUnknownResourceName; - if (overlay_package_loaded) { - if (auto name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id)) { + if (overlay_ != nullptr) { + if (auto name = overlay_->GetResourceName(target_entry.overlay_id)) { overlay_name = *name; } } @@ -112,8 +104,8 @@ void PrettyPrintVisitor::visit(const IdmapData& data) { } std::string target_name = kUnknownResourceName; - if (target_package_loaded) { - if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) { + if (target_ != nullptr) { + if (auto name = target_->GetResourceName(target_entry.target_id)) { target_name = *name; } } diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp index b517aa3a0c01..a016a36a2e3d 100644 --- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp @@ -18,16 +18,13 @@ #include #include -#include #include "android-base/macros.h" #include "android-base/stringprintf.h" -#include "androidfw/ApkAssets.h" #include "idmap2/PolicyUtils.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" -using android::ApkAssets; using android::idmap2::policy::PoliciesToDebugString; namespace android::idmap2 { @@ -48,27 +45,19 @@ void RawPrintVisitor::visit(const IdmapHeader& header) { print(header.GetOverlayName(), true /* print_value */, "overlay name"); print(header.GetDebugInfo(), false /* print_value */, "debug info"); - auto target_apk_ = ApkAssets::Load(header.GetTargetPath()); - if (target_apk_) { - target_am_.SetApkAssets({target_apk_.get()}); - apk_assets_.push_back(std::move(target_apk_)); + if (auto target = TargetResourceContainer::FromPath(header.GetTargetPath())) { + target_ = std::move(*target); } - - auto overlay_apk_ = ApkAssets::Load(header.GetOverlayPath()); - if (overlay_apk_) { - overlay_am_.SetApkAssets({overlay_apk_.get()}); - apk_assets_.push_back(std::move(overlay_apk_)); + if (auto overlay = OverlayResourceContainer::FromPath(header.GetOverlayPath())) { + overlay_ = std::move(*overlay); } } void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { - const bool target_package_loaded = !target_am_.GetApkAssets().empty(); - const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty(); - for (auto& target_entry : data.GetTargetEntries()) { Result target_name(Error("")); - if (target_package_loaded) { - target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id); + if (target_ != nullptr) { + target_name = target_->GetResourceName(target_entry.target_id); } if (target_name) { print(target_entry.target_id, "target id: %s", target_name->c_str()); @@ -77,8 +66,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { } Result overlay_name(Error("")); - if (overlay_package_loaded) { - overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id); + if (overlay_ != nullptr) { + overlay_name = overlay_->GetResourceName(target_entry.overlay_id); } if (overlay_name) { print(target_entry.overlay_id, "overlay id: %s", overlay_name->c_str()); @@ -89,8 +78,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { for (auto& target_entry : data.GetTargetInlineEntries()) { Result target_name(Error("")); - if (target_package_loaded) { - target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id); + if (target_ != nullptr) { + target_name = target_->GetResourceName(target_entry.target_id); } if (target_name) { print(target_entry.target_id, "target id: %s", target_name->c_str()); @@ -104,10 +93,10 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { utils::DataTypeToString(target_entry.value.data_type).data()); Result overlay_name(Error("")); - if (overlay_package_loaded && + if (overlay_ != nullptr && (target_entry.value.data_value == Res_value::TYPE_REFERENCE || target_entry.value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) { - overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.value.data_value); + overlay_name = overlay_->GetResourceName(target_entry.value.data_value); } if (overlay_name) { @@ -119,8 +108,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { for (auto& overlay_entry : data.GetOverlayEntries()) { Result overlay_name(Error("")); - if (overlay_package_loaded) { - overlay_name = utils::ResToTypeEntryName(overlay_am_, overlay_entry.overlay_id); + if (overlay_ != nullptr) { + overlay_name = overlay_->GetResourceName(overlay_entry.overlay_id); } if (overlay_name) { @@ -130,8 +119,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { } Result target_name(Error("")); - if (target_package_loaded) { - target_name = utils::ResToTypeEntryName(target_am_, overlay_entry.target_id); + if (target_ != nullptr) { + target_name = target_->GetResourceName(overlay_entry.target_id); } if (target_name) { @@ -145,9 +134,6 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { } void RawPrintVisitor::visit(const IdmapData::Header& header) { - print(header.GetTargetPackageId(), "target package id"); - print(header.GetOverlayPackageId(), "overlay package id"); - align(); print(header.GetTargetEntryCount(), "target entry count"); print(header.GetTargetInlineEntryCount(), "target inline entry count"); print(header.GetOverlayEntryCount(), "overlay entry count"); diff --git a/cmds/idmap2/libidmap2/ResourceContainer.cpp b/cmds/idmap2/libidmap2/ResourceContainer.cpp new file mode 100644 index 000000000000..91cb43c216ef --- /dev/null +++ b/cmds/idmap2/libidmap2/ResourceContainer.cpp @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2021 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 "idmap2/ResourceContainer.h" + +#include "androidfw/ApkAssets.h" +#include "androidfw/AssetManager.h" +#include "androidfw/Util.h" +#include "idmap2/FabricatedOverlay.h" +#include "idmap2/XmlParser.h" + +namespace android::idmap2 { +namespace { +#define REWRITE_PACKAGE(resid, package_id) \ + (((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U)) + +#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24) + +constexpr ResourceId kAttrName = 0x01010003; +constexpr ResourceId kAttrResourcesMap = 0x01010609; +constexpr ResourceId kAttrTargetName = 0x0101044d; +constexpr ResourceId kAttrTargetPackage = 0x01010021; + +// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package +// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so +// this assumption tends to work out. That said, the correct thing to do is to scan +// resources.arsc for a package with a given name as read from the package manifest instead of +// relying on a hard-coded index. This however requires storing the package name in the idmap +// header, which in turn requires incrementing the idmap version. Because the initial version of +// idmap2 is compatible with idmap, this will have to wait for now. +const LoadedPackage* GetPackageAtIndex0(const LoadedArsc* loaded_arsc) { + const std::vector>& packages = loaded_arsc->GetPackages(); + if (packages.empty()) { + return nullptr; + } + return loaded_arsc->GetPackageById(packages[0]->GetPackageId()); +} + +Result CalculateCrc(const ZipAssetsProvider* zip_assets) { + constexpr const char* kResourcesArsc = "resources.arsc"; + std::optional res_crc = zip_assets->GetCrc(kResourcesArsc); + if (!res_crc) { + return Error("failed to get CRC for '%s'", kResourcesArsc); + } + + constexpr const char* kManifest = "AndroidManifest.xml"; + std::optional man_crc = zip_assets->GetCrc(kManifest); + if (!man_crc) { + return Error("failed to get CRC for '%s'", kManifest); + } + + return *res_crc ^ *man_crc; +} + +Result OpenXmlParser(const std::string& entry_path, const ZipAssetsProvider* zip) { + auto manifest = zip->Open(entry_path); + if (manifest == nullptr) { + return Error("failed to find %s ", entry_path.c_str()); + } + + auto size = manifest->getLength(); + auto buffer = manifest->getIncFsBuffer(true /* aligned */).convert(); + if (!buffer.verify(size)) { + return Error("failed to read entire %s", entry_path.c_str()); + } + + return XmlParser::Create(buffer.unsafe_ptr(), size, true /* copyData */); +} + +Result OpenXmlParser(ResourceId id, const ZipAssetsProvider* zip, + const AssetManager2* am) { + const auto ref_table = am->GetDynamicRefTableForCookie(0); + if (ref_table == nullptr) { + return Error("failed to find dynamic ref table for cookie 0"); + } + + ref_table->lookupResourceId(&id); + auto value = am->GetResource(id); + if (!value.has_value()) { + return Error("failed to find resource for id 0x%08x", id); + } + + if (value->type != Res_value::TYPE_STRING) { + return Error("resource for is 0x%08x is not a file", id); + } + + auto string_pool = am->GetStringPoolForCookie(value->cookie); + auto file = string_pool->string8ObjectAt(value->data); + if (!file.has_value()) { + return Error("failed to find string for index %d", value->data); + } + + return OpenXmlParser(file->c_str(), zip); +} + +Result ExtractOverlayManifestInfo(const ZipAssetsProvider* zip, + const std::string& name) { + Result xml = OpenXmlParser("AndroidManifest.xml", zip); + if (!xml) { + return xml.GetError(); + } + + auto manifest_it = xml->tree_iterator(); + if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") { + return Error("root element tag is not in AndroidManifest.xml"); + } + + for (auto&& it : manifest_it) { + if (it.event() != XmlParser::Event::START_TAG || it.name() != "overlay") { + continue; + } + + OverlayManifestInfo info{}; + if (auto result_str = it.GetAttributeStringValue(kAttrName, "android:name")) { + if (*result_str != name) { + // A value for android:name was found, but either the name does not match the requested + // name, or an tag with no name was requested. + continue; + } + info.name = *result_str; + } else if (!name.empty()) { + // This tag does not have a value for android:name, but an tag with a specific name + // has been requested. + continue; + } + + if (auto result_str = it.GetAttributeStringValue(kAttrTargetPackage, "android:targetPackage")) { + info.target_package = *result_str; + } else { + return Error("android:targetPackage missing from in AndroidManifest.xml"); + } + + if (auto result_str = it.GetAttributeStringValue(kAttrTargetName, "android:targetName")) { + info.target_name = *result_str; + } + + if (auto result_value = it.GetAttributeValue(kAttrResourcesMap, "android:resourcesMap")) { + if (utils::IsReference((*result_value).dataType)) { + info.resource_mapping = (*result_value).data; + } else { + return Error("android:resourcesMap is not a reference in AndroidManifest.xml"); + } + } + return info; + } + + return Error(" with android:name \"%s\" missing from AndroidManifest.xml", name.c_str()); +} + +Result CreateResourceMapping(ResourceId id, const ZipAssetsProvider* zip, + const AssetManager2* overlay_am, + const LoadedArsc* overlay_arsc, + const LoadedPackage* overlay_package) { + auto parser = OpenXmlParser(id, zip, overlay_am); + if (!parser) { + return parser.GetError(); + } + + OverlayData overlay_data{}; + const uint32_t string_pool_offset = overlay_arsc->GetStringPool()->size(); + const uint8_t package_id = overlay_package->GetPackageId(); + auto root_it = parser->tree_iterator(); + if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") { + return Error("root element is not tag"); + } + + auto overlay_it_end = root_it.end(); + for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) { + if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) { + return Error("failed to parse overlay xml document"); + } + + if (overlay_it->event() != XmlParser::Event::START_TAG) { + continue; + } + + if (overlay_it->name() != "item") { + return Error("unexpected tag <%s> in ", overlay_it->name().c_str()); + } + + Result target_resource = overlay_it->GetAttributeStringValue("target"); + if (!target_resource) { + return Error(R"( tag missing expected attribute "target")"); + } + + Result overlay_resource = overlay_it->GetAttributeValue("value"); + if (!overlay_resource) { + return Error(R"( tag missing expected attribute "value")"); + } + + if (overlay_resource->dataType == Res_value::TYPE_STRING) { + overlay_resource->data += string_pool_offset; + } + + if (utils::IsReference(overlay_resource->dataType)) { + // Only rewrite resources defined within the overlay package to their corresponding target + // resource ids at runtime. + bool rewrite_id = package_id == EXTRACT_PACKAGE(overlay_resource->data); + overlay_data.pairs.emplace_back(OverlayData::Value{ + *target_resource, OverlayData::ResourceIdValue{overlay_resource->data, rewrite_id}}); + } else { + overlay_data.pairs.emplace_back( + OverlayData::Value{*target_resource, TargetValue{.data_type = overlay_resource->dataType, + .data_value = overlay_resource->data}}); + } + } + + const auto& string_pool = parser->get_strings(); + const uint32_t string_pool_data_length = string_pool.bytes(); + overlay_data.string_pool_data = OverlayData::InlineStringPoolData{ + .data = std::unique_ptr(new uint8_t[string_pool_data_length]), + .data_length = string_pool_data_length, + .string_pool_offset = string_pool_offset, + }; + + // Overlays should not be incrementally installed, so calling unsafe_ptr is fine here. + memcpy(overlay_data.string_pool_data->data.get(), string_pool.data().unsafe_ptr(), + string_pool_data_length); + return overlay_data; +} + +OverlayData CreateResourceMappingLegacy(const AssetManager2* overlay_am, + const LoadedPackage* overlay_package) { + OverlayData overlay_data{}; + for (const ResourceId overlay_resid : *overlay_package) { + if (auto name = utils::ResToTypeEntryName(*overlay_am, overlay_resid)) { + // Disable rewriting. Overlays did not support internal references before + // android:resourcesMap. Do not introduce new behavior. + overlay_data.pairs.emplace_back(OverlayData::Value{ + *name, OverlayData::ResourceIdValue{overlay_resid, false /* rewrite_id */}}); + } + } + return overlay_data; +} + +struct ResState { + std::unique_ptr apk_assets; + const LoadedArsc* arsc; + const LoadedPackage* package; + std::unique_ptr am; + ZipAssetsProvider* zip_assets; + + static Result Initialize(std::unique_ptr zip) { + ResState state; + state.zip_assets = zip.get(); + if ((state.apk_assets = ApkAssets::Load(std::move(zip))) == nullptr) { + return Error("failed to load apk asset"); + } + + if ((state.arsc = state.apk_assets->GetLoadedArsc()) == nullptr) { + return Error("failed to retrieve loaded arsc"); + } + + if ((state.package = GetPackageAtIndex0(state.arsc)) == nullptr) { + return Error("failed to retrieve loaded package at index 0"); + } + + state.am = std::make_unique(); + if (!state.am->SetApkAssets({state.apk_assets.get()})) { + return Error("failed to create asset manager"); + } + + return state; + } +}; + +} // namespace + +struct ApkResourceContainer : public TargetResourceContainer, public OverlayResourceContainer { + static Result> FromPath(const std::string& path); + + // inherited from TargetResourceContainer + Result DefinesOverlayable() const override; + Result GetOverlayableInfo(ResourceId id) const override; + Result GetResourceId(const std::string& name) const override; + + // inherited from OverlayResourceContainer + Result GetOverlayData(const OverlayManifestInfo& info) const override; + Result FindOverlayInfo(const std::string& name) const override; + + // inherited from ResourceContainer + Result GetCrc() const override; + Result GetResourceName(ResourceId id) const override; + const std::string& GetPath() const override; + + ~ApkResourceContainer() override = default; + + private: + ApkResourceContainer(std::unique_ptr zip_assets, std::string path); + + Result GetState() const; + ZipAssetsProvider* GetZipAssets() const; + + mutable std::variant, ResState> state_; + std::string path_; +}; + +ApkResourceContainer::ApkResourceContainer(std::unique_ptr zip_assets, + std::string path) + : state_(std::move(zip_assets)), path_(std::move(path)) { +} + +Result> ApkResourceContainer::FromPath( + const std::string& path) { + auto zip_assets = ZipAssetsProvider::Create(path); + if (zip_assets == nullptr) { + return Error("failed to load zip assets"); + } + return std::unique_ptr( + new ApkResourceContainer(std::move(zip_assets), path)); +} + +Result ApkResourceContainer::GetState() const { + if (auto state = std::get_if(&state_); state != nullptr) { + return state; + } + + auto state = + ResState::Initialize(std::move(std::get>(state_))); + if (!state) { + return state.GetError(); + } + + state_ = std::move(*state); + return &std::get(state_); +} + +ZipAssetsProvider* ApkResourceContainer::GetZipAssets() const { + if (auto zip = std::get_if>(&state_); zip != nullptr) { + return zip->get(); + } + return std::get(state_).zip_assets; +} + +Result ApkResourceContainer::DefinesOverlayable() const { + auto state = GetState(); + if (!state) { + return state.GetError(); + } + return (*state)->package->DefinesOverlayable(); +} + +Result ApkResourceContainer::GetOverlayableInfo( + ResourceId id) const { + auto state = GetState(); + if (!state) { + return state.GetError(); + } + return (*state)->package->GetOverlayableInfo(id); +} + +Result ApkResourceContainer::FindOverlayInfo(const std::string& name) const { + return ExtractOverlayManifestInfo(GetZipAssets(), name); +} + +Result ApkResourceContainer::GetOverlayData(const OverlayManifestInfo& info) const { + const auto state = GetState(); + if (!state) { + return state.GetError(); + } + + if (info.resource_mapping != 0) { + return CreateResourceMapping(info.resource_mapping, GetZipAssets(), (*state)->am.get(), + (*state)->arsc, (*state)->package); + } + return CreateResourceMappingLegacy((*state)->am.get(), (*state)->package); +} + +Result ApkResourceContainer::GetCrc() const { + return CalculateCrc(GetZipAssets()); +} + +const std::string& ApkResourceContainer::GetPath() const { + return path_; +} + +Result ApkResourceContainer::GetResourceId(const std::string& name) const { + auto state = GetState(); + if (!state) { + return state.GetError(); + } + auto id = (*state)->am->GetResourceId(name, "", (*state)->package->GetPackageName()); + if (!id.has_value()) { + return Error("failed to find resource '%s'", name.c_str()); + } + + // Retrieve the compile-time resource id of the target resource. + return REWRITE_PACKAGE(*id, (*state)->package->GetPackageId()); +} + +Result ApkResourceContainer::GetResourceName(ResourceId id) const { + auto state = GetState(); + if (!state) { + return state.GetError(); + } + return utils::ResToTypeEntryName(*(*state)->am, id); +} + +Result> TargetResourceContainer::FromPath( + std::string path) { + auto result = ApkResourceContainer::FromPath(path); + if (!result) { + return result.GetError(); + } + return std::unique_ptr(result->release()); +} + +Result> OverlayResourceContainer::FromPath( + std::string path) { + // Load the path as a fabricated overlay if the file magic indicates this is a fabricated overlay. + if (android::IsFabricatedOverlay(path)) { + auto result = FabricatedOverlayContainer::FromPath(path); + if (!result) { + return result.GetError(); + } + return std::unique_ptr(result->release()); + } + + // Fallback to loading the container as an APK. + auto result = ApkResourceContainer::FromPath(path); + if (!result) { + return result.GetError(); + } + return std::unique_ptr(result->release()); +} + +} // namespace android::idmap2 \ No newline at end of file diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp index 46eeb8e6ac80..3bbbf248c87d 100644 --- a/cmds/idmap2/libidmap2/ResourceMapping.cpp +++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp @@ -30,19 +30,12 @@ using android::base::StringPrintf; using android::idmap2::utils::BitmaskToPolicies; -using android::idmap2::utils::IsReference; -using android::idmap2::utils::ResToTypeEntryName; using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask; using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; namespace android::idmap2 { namespace { - -#define REWRITE_PACKAGE(resid, package_id) \ - (((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U)) -#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24) - std::string ConcatPolicies(const std::vector& policies) { std::string message; for (const std::string& policy : policies) { @@ -55,11 +48,11 @@ std::string ConcatPolicies(const std::vector& policies) { return message; } -Result CheckOverlayable(const LoadedPackage& target_package, +Result CheckOverlayable(const TargetResourceContainer& target, const OverlayManifestInfo& overlay_info, const PolicyBitmask& fulfilled_policies, const ResourceId& target_resource) { - static constexpr const PolicyBitmask sDefaultPolicies = + constexpr const PolicyBitmask kDefaultPolicies = PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION | PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE | PolicyFlags::CONFIG_SIGNATURE; @@ -68,8 +61,13 @@ Result CheckOverlayable(const LoadedPackage& target_package, // the overlay is preinstalled, signed with the same signature as the target or signed with the // same signature as reference package defined in SystemConfig under 'overlay-config-signature' // tag. - if (!target_package.DefinesOverlayable()) { - return (sDefaultPolicies & fulfilled_policies) != 0 + const Result defines_overlayable = target.DefinesOverlayable(); + if (!defines_overlayable) { + return Error(defines_overlayable.GetError(), "unable to retrieve overlayable info"); + } + + if (!*defines_overlayable) { + return (kDefaultPolicies & fulfilled_policies) != 0 ? Result({}) : Error( "overlay must be preinstalled, signed with the same signature as the target," @@ -77,317 +75,92 @@ Result CheckOverlayable(const LoadedPackage& target_package, " ."); } - const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource); - if (overlayable_info == nullptr) { + const auto overlayable_info = target.GetOverlayableInfo(target_resource); + if (!overlayable_info) { + return overlayable_info.GetError(); + } + + if (*overlayable_info == nullptr) { // Do not allow non-overlayable resources to be overlaid. return Error("target resource has no overlayable declaration"); } - if (overlay_info.target_name != overlayable_info->name) { + if (overlay_info.target_name != (*overlayable_info)->name) { // If the overlay supplies a target overlayable name, the resource must belong to the // overlayable defined with the specified name to be overlaid. return Error(R"( android:targetName "%s" does not match overlayable name "%s")", - overlay_info.target_name.c_str(), overlayable_info->name.c_str()); + overlay_info.target_name.c_str(), (*overlayable_info)->name.c_str()); } // Enforce policy restrictions if the resource is declared as overlayable. - if ((overlayable_info->policy_flags & fulfilled_policies) == 0) { + if (((*overlayable_info)->policy_flags & fulfilled_policies) == 0) { return Error(R"(overlay with policies "%s" does not fulfill any overlayable policies "%s")", ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(), - ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str()); + ConcatPolicies(BitmaskToPolicies((*overlayable_info)->policy_flags)).c_str()); } return Result({}); } -// TODO(martenkongstad): scan for package name instead of assuming package at index 0 -// -// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package -// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so -// this assumption tends to work out. That said, the correct thing to do is to scan -// resources.arsc for a package with a given name as read from the package manifest instead of -// relying on a hard-coded index. This however requires storing the package name in the idmap -// header, which in turn requires incrementing the idmap version. Because the initial version of -// idmap2 is compatible with idmap, this will have to wait for now. -const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) { - const std::vector>& packages = loaded_arsc.GetPackages(); - if (packages.empty()) { - return nullptr; - } - int id = packages[0]->GetPackageId(); - return loaded_arsc.GetPackageById(id); -} - -Result> OpenNonAssetFromResource(const ResourceId& resource_id, - const AssetManager2& asset_manager) { - auto value = asset_manager.GetResource(resource_id); - if (!value.has_value()) { - return Error("failed to find resource for id 0x%08x", resource_id); - } - - if (value->type != Res_value::TYPE_STRING) { - return Error("resource for is 0x%08x is not a file", resource_id); - } - - auto string_pool = asset_manager.GetStringPoolForCookie(value->cookie); - auto file = string_pool->string8ObjectAt(value->data); - if (!file.has_value()) { - return Error("failed to find string for index %d", value->data); +std::string GetDebugResourceName(const ResourceContainer& container, ResourceId resid) { + auto name = container.GetResourceName(resid); + if (name) { + return *name; } - - // Load the overlay resource mappings from the file specified using android:resourcesMap. - auto asset = asset_manager.OpenNonAsset(file->c_str(), Asset::AccessMode::ACCESS_BUFFER); - if (asset == nullptr) { - return Error("file \"%s\" not found", file->c_str()); - } - - return asset; + return StringPrintf("0x%08x", resid); } - } // namespace -Result ResourceMapping::CreateResourceMapping(const AssetManager2* target_am, - const LoadedPackage* target_package, - const LoadedPackage* overlay_package, - size_t string_pool_offset, - const XmlParser& overlay_parser, - LogInfo& log_info) { - ResourceMapping resource_mapping; - auto root_it = overlay_parser.tree_iterator(); - if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") { - return Error("root element is not tag"); - } - - const uint8_t target_package_id = target_package->GetPackageId(); - const uint8_t overlay_package_id = overlay_package->GetPackageId(); - auto overlay_it_end = root_it.end(); - for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) { - if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) { - return Error("failed to parse overlay xml document"); - } - - if (overlay_it->event() != XmlParser::Event::START_TAG) { - continue; - } - - if (overlay_it->name() != "item") { - return Error("unexpected tag <%s> in ", overlay_it->name().c_str()); - } - - Result target_resource = overlay_it->GetAttributeStringValue("target"); - if (!target_resource) { - return Error(R"( tag missing expected attribute "target")"); - } - - Result overlay_resource = overlay_it->GetAttributeValue("value"); - if (!overlay_resource) { - return Error(R"( tag missing expected attribute "value")"); - } - - auto target_id_result = - target_am->GetResourceId(*target_resource, "", target_package->GetPackageName()); - if (!target_id_result.has_value()) { - log_info.Warning(LogMessage() << "failed to find resource \"" << *target_resource - << "\" in target resources"); +Result ResourceMapping::FromContainers(const TargetResourceContainer& target, + const OverlayResourceContainer& overlay, + const OverlayManifestInfo& overlay_info, + const PolicyBitmask& fulfilled_policies, + bool enforce_overlayable, + LogInfo& log_info) { + auto overlay_data = overlay.GetOverlayData(overlay_info); + if (!overlay_data) { + return overlay_data.GetError(); + } + + ResourceMapping mapping; + for (const auto& overlay_pair : overlay_data->pairs) { + const auto target_resid = target.GetResourceId(overlay_pair.resource_name); + if (!target_resid) { + log_info.Warning(LogMessage() << target_resid.GetErrorMessage()); continue; } - // Retrieve the compile-time resource id of the target resource. - uint32_t target_id = REWRITE_PACKAGE(*target_id_result, target_package_id); - - if (overlay_resource->dataType == Res_value::TYPE_STRING) { - overlay_resource->data += string_pool_offset; - } - - if (IsReference(overlay_resource->dataType)) { - // Only rewrite resources defined within the overlay package to their corresponding target - // resource ids at runtime. - bool rewrite_reference = overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data); - resource_mapping.AddMapping(target_id, overlay_resource->data, rewrite_reference); - } else { - resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data); - } - } - - return resource_mapping; -} - -Result ResourceMapping::CreateResourceMappingLegacy( - const AssetManager2* target_am, const AssetManager2* overlay_am, - const LoadedPackage* target_package, const LoadedPackage* overlay_package, LogInfo& log_info) { - ResourceMapping resource_mapping; - const uint8_t target_package_id = target_package->GetPackageId(); - const auto end = overlay_package->end(); - for (auto iter = overlay_package->begin(); iter != end; ++iter) { - const ResourceId overlay_resid = *iter; - Result name = utils::ResToTypeEntryName(*overlay_am, overlay_resid); - if (!name) { - continue; + if (enforce_overlayable) { + // Filter out resources the overlay is not allowed to override. + auto overlayable = CheckOverlayable(target, overlay_info, fulfilled_policies, *target_resid); + if (!overlayable) { + log_info.Warning(LogMessage() << "overlay '" << overlay.GetPath() + << "' is not allowed to overlay resource '" + << GetDebugResourceName(target, *target_resid) + << "' in target: " << overlayable.GetErrorMessage()); + continue; + } } - // Find the resource with the same type and entry name within the target package. - const std::string full_name = - base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str()); - auto target_resource_result = target_am->GetResourceId(full_name); - if (!target_resource_result.has_value()) { - log_info.Warning(LogMessage() - << "failed to find resource \"" << full_name << "\" in target resources"); - continue; + if (auto result = mapping.AddMapping(*target_resid, overlay_pair.value); !result) { + return Error(result.GetError(), "failed to add mapping for '%s'", + GetDebugResourceName(target, *target_resid).c_str()); } - - // Retrieve the compile-time resource id of the target resource. - ResourceId target_resource = REWRITE_PACKAGE(*target_resource_result, target_package_id); - resource_mapping.AddMapping(target_resource, overlay_resid, - false /* rewrite_overlay_reference */); } - return resource_mapping; -} - -void ResourceMapping::FilterOverlayableResources(const AssetManager2* target_am, - const LoadedPackage* target_package, - const LoadedPackage* overlay_package, - const OverlayManifestInfo& overlay_info, - const PolicyBitmask& fulfilled_policies, - LogInfo& log_info) { - std::set remove_ids; - for (const auto& target_map : target_map_) { - const ResourceId target_resid = target_map.first; - Result success = - CheckOverlayable(*target_package, overlay_info, fulfilled_policies, target_resid); - if (success) { - continue; - } - - // Attempting to overlay a resource that is not allowed to be overlaid is treated as a - // warning. - Result name = utils::ResToTypeEntryName(*target_am, target_resid); - if (!name) { - name = StringPrintf("0x%08x", target_resid); - } - - log_info.Warning(LogMessage() << "overlay \"" << overlay_package->GetPackageName() - << "\" is not allowed to overlay resource \"" << *name - << "\" in target: " << success.GetErrorMessage()); - - remove_ids.insert(target_resid); + auto& string_pool_data = overlay_data->string_pool_data; + if (string_pool_data.has_value()) { + mapping.string_pool_offset_ = string_pool_data->string_pool_offset; + mapping.string_pool_data_ = std::move(string_pool_data->data); + mapping.string_pool_data_length_ = string_pool_data->data_length; } - for (const ResourceId target_resid : remove_ids) { - RemoveMapping(target_resid); - } + return std::move(mapping); } -Result ResourceMapping::FromApkAssets(const ApkAssets& target_apk_assets, - const ApkAssets& overlay_apk_assets, - const OverlayManifestInfo& overlay_info, - const PolicyBitmask& fulfilled_policies, - bool enforce_overlayable, - LogInfo& log_info) { - AssetManager2 target_asset_manager; - if (!target_asset_manager.SetApkAssets({&target_apk_assets})) { - return Error("failed to create target asset manager"); - } - - AssetManager2 overlay_asset_manager; - if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets})) { - return Error("failed to create overlay asset manager"); - } - - const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc(); - if (target_arsc == nullptr) { - return Error("failed to load target resources.arsc"); - } - - const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc(); - if (overlay_arsc == nullptr) { - return Error("failed to load overlay resources.arsc"); - } - - const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc); - if (target_pkg == nullptr) { - return Error("failed to load target package from resources.arsc"); - } - - const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc); - if (overlay_pkg == nullptr) { - return Error("failed to load overlay package from resources.arsc"); - } - - size_t string_pool_data_length = 0U; - size_t string_pool_offset = 0U; - std::unique_ptr string_pool_data; - Result resource_mapping = {{}}; - if (overlay_info.resource_mapping != 0U) { - // Use the dynamic reference table to find the assigned resource id of the map xml. - const auto& ref_table = overlay_asset_manager.GetDynamicRefTableForCookie(0); - uint32_t resource_mapping_id = overlay_info.resource_mapping; - ref_table->lookupResourceId(&resource_mapping_id); - - // Load the overlay resource mappings from the file specified using android:resourcesMap. - auto asset = OpenNonAssetFromResource(resource_mapping_id, overlay_asset_manager); - if (!asset) { - return Error("failed opening xml for android:resourcesMap: %s", - asset.GetErrorMessage().c_str()); - } - - auto parser = - XmlParser::Create((*asset)->getBuffer(true /* wordAligned*/), (*asset)->getLength()); - if (!parser) { - return Error("failed opening ResXMLTree"); - } - - // Copy the xml string pool data before the parse goes out of scope. - auto& string_pool = (*parser)->get_strings(); - string_pool_data_length = string_pool.bytes(); - string_pool_data.reset(new uint8_t[string_pool_data_length]); - - // Overlays should not be incrementally installed, so calling unsafe_ptr is fine here. - memcpy(string_pool_data.get(), string_pool.data().unsafe_ptr(), string_pool_data_length); - - // Offset string indices by the size of the overlay resource table string pool. - string_pool_offset = overlay_arsc->GetStringPool()->size(); - - resource_mapping = CreateResourceMapping(&target_asset_manager, target_pkg, overlay_pkg, - string_pool_offset, *(*parser), log_info); - } else { - // If no file is specified using android:resourcesMap, it is assumed that the overlay only - // defines resources intended to override target resources of the same type and name. - resource_mapping = CreateResourceMappingLegacy(&target_asset_manager, &overlay_asset_manager, - target_pkg, overlay_pkg, log_info); - } - - if (!resource_mapping) { - return resource_mapping.GetError(); - } - - if (enforce_overlayable) { - // Filter out resources the overlay is not allowed to override. - (*resource_mapping) - .FilterOverlayableResources(&target_asset_manager, target_pkg, overlay_pkg, overlay_info, - fulfilled_policies, log_info); - } - - resource_mapping->target_package_id_ = target_pkg->GetPackageId(); - resource_mapping->overlay_package_id_ = overlay_pkg->GetPackageId(); - resource_mapping->string_pool_offset_ = string_pool_offset; - resource_mapping->string_pool_data_ = std::move(string_pool_data); - resource_mapping->string_pool_data_length_ = string_pool_data_length; - return std::move(*resource_mapping); -} - -OverlayResourceMap ResourceMapping::GetOverlayToTargetMap() const { - // An overlay resource can override multiple target resources at once. Rewrite the overlay - // resource as the first target resource it overrides. - OverlayResourceMap map; - for (const auto& mappings : overlay_map_) { - map.insert(std::make_pair(mappings.first, mappings.second)); - } - return map; -} - -Result ResourceMapping::AddMapping(ResourceId target_resource, ResourceId overlay_resource, - bool rewrite_overlay_reference) { +Result ResourceMapping::AddMapping( + ResourceId target_resource, + const std::variant& value) { if (target_map_.find(target_resource) != target_map_.end()) { return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource); } @@ -395,49 +168,19 @@ Result ResourceMapping::AddMapping(ResourceId target_resource, ResourceId // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the // runtime types are not compatible, it could cause runtime crashes when the resource is resolved. - target_map_.insert(std::make_pair(target_resource, overlay_resource)); - - if (rewrite_overlay_reference) { - overlay_map_.insert(std::make_pair(overlay_resource, target_resource)); - } - return Unit{}; -} - -Result ResourceMapping::AddMapping(ResourceId target_resource, - TargetValue::DataType data_type, - TargetValue::DataValue data_value) { - if (target_map_.find(target_resource) != target_map_.end()) { - return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource); + if (auto overlay_resource = std::get_if(&value)) { + target_map_.insert(std::make_pair(target_resource, overlay_resource->overlay_id)); + if (overlay_resource->rewrite_id) { + // An overlay resource can override multiple target resources at once. Rewrite the overlay + // resource as the first target resource it overrides. + overlay_map_.insert(std::make_pair(overlay_resource->overlay_id, target_resource)); + } + } else { + auto overlay_value = std::get(value); + target_map_.insert(std::make_pair(target_resource, overlay_value)); } - // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the - // runtime types are not compatible, it could cause runtime crashes when the resource is resolved. - - target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value})); return Unit{}; } -void ResourceMapping::RemoveMapping(ResourceId target_resource) { - auto target_iter = target_map_.find(target_resource); - if (target_iter == target_map_.end()) { - return; - } - - const auto value = target_iter->second; - target_map_.erase(target_iter); - - const ResourceId* overlay_resource = std::get_if(&value); - if (overlay_resource == nullptr) { - return; - } - - auto overlay_iter = overlay_map_.equal_range(*overlay_resource); - for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) { - if (i->second == target_resource) { - overlay_map_.erase(i); - return; - } - } -} - } // namespace android::idmap2 diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp index 4e85e5751300..e809bf1f4b02 100644 --- a/cmds/idmap2/libidmap2/ResourceUtils.cpp +++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp @@ -17,27 +17,16 @@ #include "idmap2/ResourceUtils.h" #include -#include #include "androidfw/StringPiece.h" #include "androidfw/Util.h" #include "idmap2/Result.h" -#include "idmap2/XmlParser.h" -#include "idmap2/ZipFile.h" using android::StringPiece16; using android::idmap2::Result; -using android::idmap2::XmlParser; -using android::idmap2::ZipFile; using android::util::Utf16ToUtf8; namespace android::idmap2::utils { -namespace { -constexpr ResourceId kAttrName = 0x01010003; -constexpr ResourceId kAttrResourcesMap = 0x01010609; -constexpr ResourceId kAttrTargetName = 0x0101044d; -constexpr ResourceId kAttrTargetPackage = 0x01010021; -} // namespace bool IsReference(uint8_t data_type) { return data_type == Res_value::TYPE_REFERENCE || data_type == Res_value::TYPE_DYNAMIC_REFERENCE; @@ -97,71 +86,4 @@ Result ResToTypeEntryName(const AssetManager2& am, uint32_t resid) return out; } -Result ExtractOverlayManifestInfo(const std::string& path, - const std::string& name) { - std::unique_ptr zip = ZipFile::Open(path); - if (!zip) { - return Error("failed to open %s as a zip file", path.c_str()); - } - - std::unique_ptr entry = zip->Uncompress("AndroidManifest.xml"); - if (!entry) { - return Error("failed to uncompress AndroidManifest.xml from %s", path.c_str()); - } - - Result> xml = XmlParser::Create(entry->buf, entry->size); - if (!xml) { - return Error("failed to parse AndroidManifest.xml from %s", path.c_str()); - } - - auto manifest_it = (*xml)->tree_iterator(); - if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") { - return Error("root element tag is not in AndroidManifest.xml of %s", path.c_str()); - } - - for (auto&& it : manifest_it) { - if (it.event() != XmlParser::Event::START_TAG || it.name() != "overlay") { - continue; - } - - OverlayManifestInfo info{}; - if (auto result_str = it.GetAttributeStringValue(kAttrName, "android:name")) { - if (*result_str != name) { - // A value for android:name was found, but either a the name does not match the requested - // name, or an tag with no name was requested. - continue; - } - info.name = *result_str; - } else if (!name.empty()) { - // This tag does not have a value for android:name, but an tag with a specific name - // has been requested. - continue; - } - - if (auto result_str = it.GetAttributeStringValue(kAttrTargetPackage, "android:targetPackage")) { - info.target_package = *result_str; - } else { - return Error("android:targetPackage missing from of %s: %s", path.c_str(), - result_str.GetErrorMessage().c_str()); - } - - if (auto result_str = it.GetAttributeStringValue(kAttrTargetName, "android:targetName")) { - info.target_name = *result_str; - } - - if (auto result_value = it.GetAttributeValue(kAttrResourcesMap, "android:resourcesMap")) { - if (IsReference((*result_value).dataType)) { - info.resource_mapping = (*result_value).data; - } else { - return Error("android:resourcesMap is not a reference in AndroidManifest.xml of %s", - path.c_str()); - } - } - return info; - } - - return Error(" with android:name \"%s\" missing from AndroidManifest.xml of %s", - name.c_str(), path.c_str()); -} - } // namespace android::idmap2::utils diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp index 00baea46f909..70822c890288 100644 --- a/cmds/idmap2/libidmap2/XmlParser.cpp +++ b/cmds/idmap2/libidmap2/XmlParser.cpp @@ -151,16 +151,18 @@ Result XmlParser::Node::GetAttributeStringValue(const std::string& return value ? GetStringValue(parser_, *value, name) : value.GetError(); } -Result> XmlParser::Create(const void* data, size_t size, - bool copy_data) { - auto parser = std::unique_ptr(new XmlParser()); - if (parser->tree_.setTo(data, size, copy_data) != NO_ERROR) { +XmlParser::XmlParser(std::unique_ptr tree) : tree_(std::move(tree)) { +} + +Result XmlParser::Create(const void* data, size_t size, bool copy_data) { + auto tree = std::make_unique(); + if (tree->setTo(data, size, copy_data) != NO_ERROR) { return Error("Malformed xml block"); } // Find the beginning of the first tag. XmlParser::Event event; - while ((event = parser->tree_.next()) != XmlParser::Event::BAD_DOCUMENT && + while ((event = tree->next()) != XmlParser::Event::BAD_DOCUMENT && event != XmlParser::Event::END_DOCUMENT && event != XmlParser::Event::START_TAG) { } @@ -172,11 +174,7 @@ Result> XmlParser::Create(const void* data, siz return Error("Bad xml document"); } - return parser; -} - -XmlParser::~XmlParser() { - tree_.uninit(); + return XmlParser{std::move(tree)}; } } // namespace android::idmap2 diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp deleted file mode 100644 index 1e1a218163f0..000000000000 --- a/cmds/idmap2/libidmap2/ZipFile.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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. - */ - -#include "idmap2/ZipFile.h" - -#include -#include - -#include "idmap2/Result.h" - -namespace android::idmap2 { - -std::unique_ptr MemoryChunk::Allocate(size_t size) { - void* ptr = ::operator new(sizeof(MemoryChunk) + size); - std::unique_ptr chunk(reinterpret_cast(ptr)); - chunk->size = size; - return chunk; -} - -std::unique_ptr ZipFile::Open(const std::string& path) { - ::ZipArchiveHandle handle; - int32_t status = ::OpenArchive(path.c_str(), &handle); - if (status != 0) { - ::CloseArchive(handle); - return nullptr; - } - return std::unique_ptr(new ZipFile(handle)); -} - -ZipFile::~ZipFile() { - ::CloseArchive(handle_); -} - -std::unique_ptr ZipFile::Uncompress(const std::string& entryPath) const { - ::ZipEntry entry; - int32_t status = ::FindEntry(handle_, entryPath, &entry); - if (status != 0) { - return nullptr; - } - std::unique_ptr chunk = MemoryChunk::Allocate(entry.uncompressed_length); - status = ::ExtractToMemory(handle_, &entry, chunk->buf, chunk->size); - if (status != 0) { - return nullptr; - } - return chunk; -} - -Result ZipFile::Crc(const std::string& entryPath) const { - ::ZipEntry entry; - int32_t status = ::FindEntry(handle_, entryPath, &entry); - if (status != 0) { - return Error("failed to find zip entry %s", entryPath.c_str()); - } - return entry.crc32; -} - -} // namespace android::idmap2 diff --git a/cmds/idmap2/libidmap2/proto/fabricated_v1.proto b/cmds/idmap2/libidmap2/proto/fabricated_v1.proto new file mode 100644 index 000000000000..a392b2b6d856 --- /dev/null +++ b/cmds/idmap2/libidmap2/proto/fabricated_v1.proto @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 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. + */ + +syntax = "proto3"; + +package android.idmap2.pb; + +option optimize_for = LITE_RUNTIME; + +// All changes to the proto messages in this file MUST be backwards compatible. Backwards +// incompatible changes will cause previously fabricated overlays to be considered corrupt by the +// new proto message specification. +message FabricatedOverlay { + repeated ResourcePackage packages = 1; + string name = 2; + string package_name = 3; + string target_package_name = 4; + string target_overlayable = 5; +} + +message ResourcePackage { + string name = 1; + repeated ResourceType types = 2; +} + +message ResourceType { + string name = 1; + repeated ResourceEntry entries = 2; +} + +message ResourceEntry { + string name = 1; + oneof value { + ResourceValue res_value = 2; + } +} + +message ResourceValue { + // Corresponds with android::Res_value::dataType + uint32 data_type = 1; + // Corresponds with android::Res_value::data + uint32 data_value = 2; +} \ No newline at end of file diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp index 524aabcec652..bf6332742787 100644 --- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp +++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp @@ -33,7 +33,7 @@ using ::testing::NotNull; namespace android::idmap2 { TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) { - std::string raw(reinterpret_cast(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast(kIdmapRawData), kIdmapRawDataLen); std::istringstream raw_stream(raw); auto result1 = Idmap::FromBinaryStream(raw_stream); diff --git a/cmds/idmap2/tests/FabricatedOverlayTests.cpp b/cmds/idmap2/tests/FabricatedOverlayTests.cpp new file mode 100644 index 000000000000..79ab2438af74 --- /dev/null +++ b/cmds/idmap2/tests/FabricatedOverlayTests.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +namespace android::idmap2 { + +TEST(FabricatedOverlayTests, OverlayInfo) { + auto overlay = + FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target") + .SetOverlayable("TestResources") + .Build(); + + ASSERT_TRUE(overlay); + auto container = FabricatedOverlayContainer::FromOverlay(std::move(*overlay)); + auto info = container->FindOverlayInfo("SandTheme"); + ASSERT_TRUE(info); + EXPECT_EQ("SandTheme", (*info).name); + EXPECT_EQ("TestResources", (*info).target_name); + + info = container->FindOverlayInfo("OceanTheme"); + ASSERT_FALSE(info); +} + +TEST(FabricatedOverlayTests, SetResourceValue) { + auto overlay = + FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target") + .SetResourceValue("com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U) + .SetResourceValue("com.example.target.split:integer/int2", Res_value::TYPE_INT_DEC, 2U) + .SetResourceValue("string/int3", Res_value::TYPE_REFERENCE, 0x7f010000) + .Build(); + ASSERT_TRUE(overlay); + auto container = FabricatedOverlayContainer::FromOverlay(std::move(*overlay)); + auto info = container->FindOverlayInfo("SandTheme"); + ASSERT_TRUE(info); + ASSERT_TRUE((*info).target_name.empty()); + + auto crc = (*container).GetCrc(); + ASSERT_TRUE(crc) << crc.GetErrorMessage(); + EXPECT_NE(0U, *crc); + + auto pairs = container->GetOverlayData(*info); + ASSERT_TRUE(pairs); + EXPECT_FALSE(pairs->string_pool_data.has_value()); + ASSERT_EQ(3U, pairs->pairs.size()); + + auto& it = pairs->pairs[0]; + ASSERT_EQ("com.example.target:integer/int1", it.resource_name); + auto entry = std::get_if(&it.value); + ASSERT_NE(nullptr, entry); + ASSERT_EQ(1U, entry->data_value); + ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->data_type); + + it = pairs->pairs[1]; + ASSERT_EQ("com.example.target:string/int3", it.resource_name); + entry = std::get_if(&it.value); + ASSERT_NE(nullptr, entry); + ASSERT_EQ(0x7f010000, entry->data_value); + ASSERT_EQ(Res_value::TYPE_REFERENCE, entry->data_type); + + it = pairs->pairs[2]; + ASSERT_EQ("com.example.target.split:integer/int2", it.resource_name); + entry = std::get_if(&it.value); + ASSERT_NE(nullptr, entry); + ASSERT_EQ(2U, entry->data_value); + ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->data_type); +} + +TEST(FabricatedOverlayTests, SetResourceValueBadArgs) { + { + auto builder = + FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target") + .SetResourceValue("int1", Res_value::TYPE_INT_DEC, 1U); + ASSERT_FALSE(builder.Build()); + } + { + auto builder = + FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target") + .SetResourceValue("com.example.target:int2", Res_value::TYPE_INT_DEC, 1U); + ASSERT_FALSE(builder.Build()); + } +} + +TEST(FabricatedOverlayTests, SerializeAndDeserialize) { + auto overlay = + FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target") + .SetOverlayable("TestResources") + .SetResourceValue("com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U) + .Build(); + ASSERT_TRUE(overlay); + TemporaryFile tf; + std::ofstream out(tf.path); + ASSERT_TRUE((*overlay).ToBinaryStream(out)); + out.close(); + + auto container = OverlayResourceContainer::FromPath(tf.path); + ASSERT_TRUE(container) << container.GetErrorMessage(); + EXPECT_EQ(tf.path, (*container)->GetPath()); + + auto crc = (*container)->GetCrc(); + ASSERT_TRUE(crc) << crc.GetErrorMessage(); + EXPECT_NE(0U, *crc); + + auto info = (*container)->FindOverlayInfo("SandTheme"); + ASSERT_TRUE(info) << info.GetErrorMessage(); + EXPECT_EQ("SandTheme", (*info).name); + EXPECT_EQ("TestResources", (*info).target_name); + + auto pairs = (*container)->GetOverlayData(*info); + ASSERT_TRUE(pairs) << pairs.GetErrorMessage(); + EXPECT_EQ(1U, pairs->pairs.size()); + + auto& it = pairs->pairs[0]; + ASSERT_EQ("com.example.target:integer/int1", it.resource_name); + auto entry = std::get_if(&it.value); + ASSERT_NE(nullptr, entry); + EXPECT_EQ(1U, entry->data_value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, entry->data_type); +} + +} // namespace android::idmap2 \ No newline at end of file diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 16b68f01e8f5..9516ff83d718 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include // fclose #include #include @@ -61,12 +63,12 @@ TEST(IdmapTests, TestCanonicalIdmapPathFor) { } TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) { - std::string raw(reinterpret_cast(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast(kIdmapRawData), kIdmapRawDataLen); std::istringstream stream(raw); std::unique_ptr header = IdmapHeader::FromBinaryStream(stream); ASSERT_THAT(header, NotNull()); ASSERT_EQ(header->GetMagic(), 0x504d4449U); - ASSERT_EQ(header->GetVersion(), 0x07U); + ASSERT_EQ(header->GetVersion(), 0x08U); ASSERT_EQ(header->GetTargetCrc(), 0x1234U); ASSERT_EQ(header->GetOverlayCrc(), 0x5678U); ASSERT_EQ(header->GetFulfilledPolicies(), 0x11); @@ -81,7 +83,7 @@ TEST(IdmapTests, IdmapFailParsingDifferentVersion) { std::stringstream stream; stream << android::kIdmapMagic; stream << 0xffffffffU; - stream << std::string(kJunkSize, (char) 0xffU); + stream << std::string(kJunkSize, (char)0xffU); ASSERT_FALSE(Idmap::FromBinaryStream(stream)); } @@ -90,14 +92,13 @@ TEST(IdmapTests, IdmapFailParsingDifferentMagic) { std::stringstream stream; stream << 0xffffffffU; stream << android::kIdmapCurrentVersion; - stream << std::string(kJunkSize, (char) 0xffU); + stream << std::string(kJunkSize, (char)0xffU); ASSERT_FALSE(Idmap::FromBinaryStream(stream)); } TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) { const size_t offset = kIdmapRawDataOffset; - std::string raw(reinterpret_cast(idmap_raw_data + offset), - kIdmapRawDataLen - offset); + std::string raw(reinterpret_cast(kIdmapRawData + offset), kIdmapRawDataLen - offset); std::istringstream stream(raw); std::unique_ptr header = IdmapData::Header::FromBinaryStream(stream); @@ -108,8 +109,7 @@ TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) { TEST(IdmapTests, CreateIdmapDataFromBinaryStream) { const size_t offset = kIdmapRawDataOffset; - std::string raw(reinterpret_cast(idmap_raw_data + offset), - kIdmapRawDataLen - offset); + std::string raw(reinterpret_cast(kIdmapRawData + offset), kIdmapRawDataLen - offset); std::istringstream stream(raw); std::unique_ptr data = IdmapData::FromBinaryStream(stream); @@ -134,7 +134,7 @@ TEST(IdmapTests, CreateIdmapDataFromBinaryStream) { } TEST(IdmapTests, CreateIdmapFromBinaryStream) { - std::string raw(reinterpret_cast(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast(kIdmapRawData), kIdmapRawDataLen); std::istringstream stream(raw); auto result = Idmap::FromBinaryStream(stream); @@ -143,7 +143,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) { ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); - ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x07U); + ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U); ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), kIdmapRawDataPolicies); @@ -177,7 +177,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) { } TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) { - std::string raw(reinterpret_cast(idmap_raw_data), + std::string raw(reinterpret_cast(kIdmapRawData), 10); // data too small std::istringstream stream(raw); @@ -189,14 +189,14 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) { std::string target_apk_path = GetTestDataPath() + "/target/target.apk"; std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk"; - std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); - ASSERT_THAT(target_apk, NotNull()); + auto target = TargetResourceContainer::FromPath(target_apk_path); + ASSERT_TRUE(target); - std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path); - ASSERT_THAT(overlay_apk, NotNull()); + auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + ASSERT_TRUE(overlay); - auto idmap_result = Idmap::FromApkAssets( - *target_apk, *overlay_apk, TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::PUBLIC, + auto idmap_result = Idmap::FromContainers( + **target, **overlay, TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::PUBLIC, /* enforce_overlayable */ true); ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage(); auto& idmap = *idmap_result; @@ -204,7 +204,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) { ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); - ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x07U); + ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC); ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC); @@ -218,15 +218,15 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssets) { std::string target_apk_path = GetTestDataPath() + "/target/target.apk"; std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk"; - std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); - ASSERT_THAT(target_apk, NotNull()); + auto target = TargetResourceContainer::FromPath(target_apk_path); + ASSERT_TRUE(target); - std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path); - ASSERT_THAT(overlay_apk, NotNull()); + auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + ASSERT_TRUE(overlay); - auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, - TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC, - /* enforce_overlayable */ true); + auto idmap_result = Idmap::FromContainers( + **target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC, + /* enforce_overlayable */ true); ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage(); auto& idmap = *idmap_result; ASSERT_THAT(idmap, NotNull()); @@ -255,25 +255,66 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssets) { ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay::string::str4, R::target::string::str4); } +TEST(IdmapTests, FabricatedOverlay) { + std::string target_apk_path = GetTestDataPath() + "/target/target.apk"; + auto target = TargetResourceContainer::FromPath(target_apk_path); + ASSERT_TRUE(target); + + auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target") + .SetOverlayable("TestResources") + .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U) + .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000) + .Build(); + + ASSERT_TRUE(frro); + TemporaryFile tf; + std::ofstream out(tf.path); + ASSERT_TRUE((*frro).ToBinaryStream(out)); + out.close(); + + auto overlay = OverlayResourceContainer::FromPath(tf.path); + ASSERT_TRUE(overlay); + + auto idmap_result = Idmap::FromContainers(**target, **overlay, "SandTheme", PolicyFlags::PUBLIC, + /* enforce_overlayable */ true); + ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage(); + auto& idmap = *idmap_result; + ASSERT_THAT(idmap, NotNull()); + + const std::vector>& dataBlocks = idmap->GetData(); + ASSERT_EQ(dataBlocks.size(), 1U); + + const std::unique_ptr& data = dataBlocks[0]; + ASSERT_THAT(data, NotNull()); + ASSERT_EQ(data->GetTargetEntries().size(), 0U); + ASSERT_EQ(data->GetOverlayEntries().size(), 0U); + + const auto& target_inline_entries = data->GetTargetInlineEntries(); + ASSERT_EQ(target_inline_entries.size(), 2U); + ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1, + Res_value::TYPE_INT_DEC, 2U); + ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1, + Res_value::TYPE_REFERENCE, 0x7f010000); +} + TEST(IdmapTests, FailCreateIdmapInvalidName) { std::string target_apk_path = GetTestDataPath() + "/target/target.apk"; std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk"; - std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); - ASSERT_THAT(target_apk, NotNull()); + auto target = TargetResourceContainer::FromPath(target_apk_path); + ASSERT_TRUE(target); - std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path); - ASSERT_THAT(overlay_apk, NotNull()); + auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + ASSERT_TRUE(overlay); { - auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, "", PolicyFlags::PUBLIC, - /* enforce_overlayable */ true); + auto idmap_result = Idmap::FromContainers(**target, **overlay, "", PolicyFlags::PUBLIC, + /* enforce_overlayable */ true); ASSERT_FALSE(idmap_result); } { - auto idmap_result = - Idmap::FromApkAssets(*target_apk, *overlay_apk, "unknown", PolicyFlags::PUBLIC, - /* enforce_overlayable */ true); + auto idmap_result = Idmap::FromContainers(**target, **overlay, "unknown", PolicyFlags::PUBLIC, + /* enforce_overlayable */ true); ASSERT_FALSE(idmap_result); } } @@ -282,15 +323,15 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) { std::string target_apk_path = GetTestDataPath() + "/target/target.apk"; std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-shared.apk"; - std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); - ASSERT_THAT(target_apk, NotNull()); + auto target = TargetResourceContainer::FromPath(target_apk_path); + ASSERT_TRUE(target); - std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path); - ASSERT_THAT(overlay_apk, NotNull()); + auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + ASSERT_TRUE(overlay); - auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, - TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC, - /* enforce_overlayable */ true); + auto idmap_result = Idmap::FromContainers( + **target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC, + /* enforce_overlayable */ true); ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage(); auto& idmap = *idmap_result; ASSERT_THAT(idmap, NotNull()); @@ -328,30 +369,29 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) { } Result> TestIdmapDataFromApkAssets( - const std::string& local_target_apk_path, const std::string& local_overlay_apk_path, + const std::string& local_target_path, const std::string& local_overlay_path, const std::string& overlay_name, const PolicyBitmask& fulfilled_policies, bool enforce_overlayable) { - auto overlay_info = - utils::ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path, overlay_name); - if (!overlay_info) { - return overlay_info.GetError(); + const std::string target_path(GetTestDataPath() + local_target_path); + auto target = TargetResourceContainer::FromPath(target_path); + if (!target) { + return Error(R"(Failed to load target "%s")", target_path.c_str()); } - const std::string target_apk_path(GetTestDataPath() + local_target_apk_path); - std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); - if (!target_apk) { - return Error(R"(Failed to load target apk "%s")", target_apk_path.data()); + const std::string overlay_path(GetTestDataPath() + local_overlay_path); + auto overlay = OverlayResourceContainer::FromPath(overlay_path); + if (!overlay) { + return Error(R"(Failed to load overlay "%s")", overlay_path.c_str()); } - const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path); - std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path); - if (!overlay_apk) { - return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data()); + auto overlay_info = (*overlay)->FindOverlayInfo(overlay_name); + if (!overlay_info) { + return Error(R"(Failed to find overlay name "%s")", overlay_name.c_str()); } LogInfo log_info; - auto mapping = ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, *overlay_info, - fulfilled_policies, enforce_overlayable, log_info); + auto mapping = ResourceMapping::FromContainers(**target, **overlay, *overlay_info, + fulfilled_policies, enforce_overlayable, log_info); if (!mapping) { return mapping.GetError(); } @@ -360,11 +400,9 @@ Result> TestIdmapDataFromApkAssets( } TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) { - auto idmap_data = - TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", "DifferentPackages", - - PolicyFlags::PUBLIC, - /* enforce_overlayable */ false); + auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", + "DifferentPackages", PolicyFlags::PUBLIC, + /* enforce_overlayable */ false); ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage(); auto& data = *idmap_data; @@ -417,7 +455,7 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { const uint32_t target_crc = kIdmapRawDataTargetCrc; const uint32_t overlay_crc = kIdmapRawOverlayCrc; - std::string raw(reinterpret_cast(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast(kIdmapRawData), kIdmapRawDataLen); std::istringstream raw_stream(raw); auto result = Idmap::FromBinaryStream(raw_stream); @@ -468,8 +506,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { ASSERT_THAT(bad_target_crc_header, NotNull()); ASSERT_NE(header->GetTargetCrc(), bad_target_crc_header->GetTargetCrc()); ASSERT_FALSE(bad_target_crc_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name, - target_crc, overlay_crc, policies, - /* enforce_overlayable */ true)); + target_crc, overlay_crc, policies, + /* enforce_overlayable */ true)); // overlay crc: bytes (0xc, 0xf) std::string bad_overlay_crc_string(stream.str()); @@ -483,8 +521,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { ASSERT_THAT(bad_overlay_crc_header, NotNull()); ASSERT_NE(header->GetOverlayCrc(), bad_overlay_crc_header->GetOverlayCrc()); ASSERT_FALSE(bad_overlay_crc_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name, - target_crc, overlay_crc, policies, - /* enforce_overlayable */ true)); + target_crc, overlay_crc, policies, + /* enforce_overlayable */ true)); // fulfilled policy: bytes (0x10, 0x13) std::string bad_policy_string(stream.str()); @@ -522,8 +560,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { ASSERT_THAT(bad_target_path_header, NotNull()); ASSERT_NE(header->GetTargetPath(), bad_target_path_header->GetTargetPath()); ASSERT_FALSE(bad_target_path_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name, - target_crc, overlay_crc, policies, - /* enforce_overlayable */ true)); + target_crc, overlay_crc, policies, + /* enforce_overlayable */ true)); // overlay path: bytes (0x2c, 0x37) std::string bad_overlay_path_string(stream.str()); @@ -576,7 +614,7 @@ class TestVisitor : public Visitor { }; TEST(IdmapTests, TestVisitor) { - std::string raw(reinterpret_cast(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast(kIdmapRawData), kIdmapRawDataLen); std::istringstream stream(raw); const auto idmap = Idmap::FromBinaryStream(stream); diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp index 87ce0f13d19e..3d3d82a8c7dd 100644 --- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp @@ -27,35 +27,31 @@ #include "idmap2/Idmap.h" #include "idmap2/PrettyPrintVisitor.h" -using android::ApkAssets; using android::base::StringPrintf; -using ::testing::NotNull; -using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask; using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; namespace android::idmap2 { TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) { const std::string target_apk_path(GetTestDataPath() + "/target/target.apk"); - std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); - ASSERT_THAT(target_apk, NotNull()); + auto target = TargetResourceContainer::FromPath(target_apk_path); + ASSERT_TRUE(target); const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk"); - std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path); - ASSERT_THAT(overlay_apk, NotNull()); + auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + ASSERT_TRUE(overlay); - const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, - TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC, - /* enforce_overlayable */ true); + const auto idmap = Idmap::FromContainers(**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, + PolicyFlags::PUBLIC, /* enforce_overlayable */ true); ASSERT_TRUE(idmap); std::stringstream stream; PrettyPrintVisitor visitor(stream); (*idmap)->accept(&visitor); - ASSERT_NE(stream.str().find("target apk path : "), std::string::npos); - ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos); + ASSERT_NE(stream.str().find("target path : "), std::string::npos); + ASSERT_NE(stream.str().find("overlay path : "), std::string::npos); ASSERT_NE(stream.str().find(StringPrintf("0x%08x -> 0x%08x (integer/int1 -> integer/int1)\n", R::target::integer::int1, R::overlay::integer::int1)), std::string::npos); @@ -64,7 +60,7 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) { TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) { fclose(stderr); // silence expected warnings from libandroidfw - std::string raw(reinterpret_cast(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast(kIdmapRawData), kIdmapRawDataLen); std::istringstream raw_stream(raw); const auto idmap = Idmap::FromBinaryStream(raw_stream); @@ -74,8 +70,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) { PrettyPrintVisitor visitor(stream); (*idmap)->accept(&visitor); - ASSERT_NE(stream.str().find("target apk path : "), std::string::npos); - ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos); + ASSERT_NE(stream.str().find("target path : "), std::string::npos); + ASSERT_NE(stream.str().find("overlay path : "), std::string::npos); ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000 (\?\?\? -> \?\?\?)\n"), std::string::npos); } diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index 88f85efb0f84..a6371cb74f2e 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -30,17 +30,16 @@ #include "idmap2/RawPrintVisitor.h" using android::base::StringPrintf; -using ::testing::NotNull; using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; namespace android::idmap2 { -#define ASSERT_CONTAINS_REGEX(pattern, str) \ - do { \ - ASSERT_TRUE(std::regex_search(str, std::regex(pattern))) \ - << "pattern '" << pattern << "' not found in\n--------\n" \ - << str << "--------"; \ +#define ASSERT_CONTAINS_REGEX(pattern, str) \ + do { \ + ASSERT_TRUE(std::regex_search(str, std::regex(pattern))) \ + << "pattern '" << (pattern) << "' not found in\n--------\n" \ + << (str) << "--------"; \ } while (0) #define ADDRESS "[0-9a-f]{8}: " @@ -49,16 +48,15 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { fclose(stderr); // silence expected warnings const std::string target_apk_path(GetTestDataPath() + "/target/target.apk"); - std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); - ASSERT_THAT(target_apk, NotNull()); + auto target = TargetResourceContainer::FromPath(target_apk_path); + ASSERT_TRUE(target); const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk"); - std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path); - ASSERT_THAT(overlay_apk, NotNull()); + auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + ASSERT_TRUE(overlay); - const auto idmap = - Idmap::FromApkAssets(*target_apk, *overlay_apk, TestConstants::OVERLAY_NAME_DEFAULT, - PolicyFlags::PUBLIC, /* enforce_overlayable */ true); + const auto idmap = Idmap::FromContainers(**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, + PolicyFlags::PUBLIC, /* enforce_overlayable */ true); ASSERT_TRUE(idmap); std::stringstream stream; @@ -66,7 +64,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { (*idmap)->accept(&visitor); ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS "00000007 version\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000008 version\n", stream.str()); ASSERT_CONTAINS_REGEX( StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING), stream.str()); @@ -75,8 +73,6 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000001 fulfilled policies: public\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000001 enforce overlayable\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000000 target inline entry count", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count", stream.str()); @@ -104,7 +100,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { fclose(stderr); // silence expected warnings from libandroidfw - std::string raw(reinterpret_cast(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast(kIdmapRawData), kIdmapRawDataLen); std::istringstream raw_stream(raw); const auto idmap = Idmap::FromBinaryStream(raw_stream); @@ -115,7 +111,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { (*idmap)->accept(&visitor); ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS "00000007 version\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000008 version\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str()); @@ -126,8 +122,6 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { ASSERT_CONTAINS_REGEX(ADDRESS "........ overlay path: overlayX.apk\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "0000000b overlay name size\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "........ overlay name: OverlayName\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000003 target entry count\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000001 target inline entry count\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000003 overlay entry count\n", stream.str()); @@ -140,7 +134,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f030002 target id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000004 string pool size\n", stream.str()); - ASSERT_CONTAINS_REGEX("000000a8: ........ string pool\n", stream.str()); + ASSERT_CONTAINS_REGEX("000000a4: ........ string pool\n", stream.str()); } } // namespace android::idmap2 diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp index 0362529c4f3b..5a1d808af06f 100644 --- a/cmds/idmap2/tests/ResourceMappingTests.cpp +++ b/cmds/idmap2/tests/ResourceMappingTests.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +#include +#include +#include + #include // fclose #include #include @@ -22,14 +26,10 @@ #include "R.h" #include "TestConstants.h" #include "TestHelpers.h" -#include "androidfw/ResourceTypes.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" #include "idmap2/LogInfo.h" #include "idmap2/ResourceMapping.h" using android::Res_value; -using android::idmap2::utils::ExtractOverlayManifestInfo; using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; @@ -41,32 +41,36 @@ namespace android::idmap2 { ASSERT_TRUE(result) << result.GetErrorMessage(); \ } while (0) -Result TestGetResourceMapping(const std::string& local_target_apk_path, - const std::string& local_overlay_apk_path, +Result TestGetResourceMapping(const std::string& local_target_path, + const std::string& local_overlay_path, const std::string& overlay_name, const PolicyBitmask& fulfilled_policies, bool enforce_overlayable) { - auto overlay_info = - ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path, overlay_name); - if (!overlay_info) { - return overlay_info.GetError(); + const std::string target_path = (local_target_path[0] == '/') + ? local_target_path + : (GetTestDataPath() + "/" + local_target_path); + auto target = TargetResourceContainer::FromPath(target_path); + if (!target) { + return Error(target.GetError(), R"(Failed to load target "%s")", target_path.c_str()); } - const std::string target_apk_path(GetTestDataPath() + local_target_apk_path); - std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); - if (!target_apk) { - return Error(R"(Failed to load target apk "%s")", target_apk_path.data()); + const std::string overlay_path = (local_overlay_path[0] == '/') + ? local_overlay_path + : (GetTestDataPath() + "/" + local_overlay_path); + auto overlay = OverlayResourceContainer::FromPath(overlay_path); + if (!overlay) { + return Error(overlay.GetError(), R"(Failed to load overlay "%s")", overlay_path.c_str()); } - const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path); - std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path); - if (!overlay_apk) { - return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data()); + auto overlay_info = (*overlay)->FindOverlayInfo(overlay_name); + if (!overlay_info) { + return Error(overlay_info.GetError(), R"(Failed to find overlay name "%s")", + overlay_name.c_str()); } LogInfo log_info; - return ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, *overlay_info, - fulfilled_policies, enforce_overlayable, log_info); + return ResourceMapping::FromContainers(**target, **overlay, *overlay_info, fulfilled_policies, + enforce_overlayable, log_info); } Result MappingExists(const ResourceMapping& mapping, ResourceId target_resource, @@ -128,7 +132,7 @@ Result MappingExists(const ResourceMapping& mapping, const ResourceId& tar } TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-legacy.apk", "", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay-legacy.apk", "", PolicyFlags::PUBLIC, /* enforce_overlayable */ false); ASSERT_TRUE(resources) << resources.GetErrorMessage(); @@ -145,7 +149,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) { } TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", "SwapNames", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", "SwapNames", PolicyFlags::PUBLIC, /* enforce_overlayable */ false); @@ -161,7 +165,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) { } TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", "DifferentPackages", PolicyFlags::PUBLIC, /* enforce_overlayable */ false); @@ -176,7 +180,7 @@ TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) { } TEST(ResourceMappingTests, InlineResources) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", "Inline", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", "Inline", PolicyFlags::PUBLIC, /* enforce_overlayable */ false); constexpr size_t overlay_string_pool_size = 10U; @@ -189,8 +193,32 @@ TEST(ResourceMappingTests, InlineResources) { ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U)); } +TEST(ResourceMappingTests, FabricatedOverlay) { + auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target") + .SetOverlayable("TestResources") + .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U) + .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000) + .Build(); + + ASSERT_TRUE(frro); + TemporaryFile tf; + std::ofstream out(tf.path); + ASSERT_TRUE((*frro).ToBinaryStream(out)); + out.close(); + + auto resources = TestGetResourceMapping("target/target.apk", tf.path, "SandTheme", + PolicyFlags::PUBLIC, /* enforce_overlayable */ false); + + ASSERT_TRUE(resources) << resources.GetErrorMessage(); + auto& res = *resources; + ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U); + ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U); + ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x7f010000)); + ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 2U)); +} + TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC, /* enforce_overlayable */ true); @@ -209,7 +237,7 @@ TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) { // Resources that are not declared as overlayable and resources that a protected by policies the // overlay does not fulfill must not map to overlay resources. TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC, /* enforce_overlayable */ true); @@ -229,7 +257,7 @@ TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) { // overlay does not fulfilled can map to overlay resources when overlayable enforcement is turned // off. TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC, /* enforce_overlayable */ false); @@ -264,7 +292,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnore // Overlays that do not target an tag can overlay any resource if overlayable // enforcement is disabled. TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-legacy.apk", "", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay-legacy.apk", "", PolicyFlags::PUBLIC, /* enforce_overlayable */ false); @@ -284,10 +312,9 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTarget // Overlays that are neither pre-installed nor signed with the same signature as the target cannot // overlay packages that have not defined overlayable resources. TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) { - auto resources = - TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay.apk", - "NoTargetName", PolicyFlags::PUBLIC, - /* enforce_overlayable */ true); + auto resources = TestGetResourceMapping("target/target-no-overlayable.apk", "overlay/overlay.apk", + "NoTargetName", PolicyFlags::PUBLIC, + /* enforce_overlayable */ true); ASSERT_TRUE(resources) << resources.GetErrorMessage(); ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U); @@ -297,9 +324,9 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) { // signed with the same signature as the reference package can overlay packages that have not // defined overlayable resources. TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) { - auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void { + auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) { auto resources = - TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay.apk", + TestGetResourceMapping("target/target-no-overlayable.apk", "overlay/overlay.apk", TestConstants::OVERLAY_NAME_ALL_POLICIES, fulfilled_policies, /* enforce_overlayable */ true); diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp index 1f6bf49f5f0e..69142086765c 100644 --- a/cmds/idmap2/tests/ResourceUtilsTests.cpp +++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp @@ -17,10 +17,12 @@ #include #include +#include "R.h" #include "TestHelpers.h" #include "androidfw/ApkAssets.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "idmap2/ResourceContainer.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" @@ -49,8 +51,8 @@ class ResourceUtilsTests : public Idmap2Tests { }; TEST_F(ResourceUtilsTests, ResToTypeEntryName) { - Result name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000U); - ASSERT_TRUE(name); + Result name = utils::ResToTypeEntryName(GetAssetManager(), R::target::integer::int1); + ASSERT_TRUE(name) << name.GetErrorMessage(); ASSERT_EQ(*name, "integer/int1"); } @@ -60,25 +62,34 @@ TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) { } TEST_F(ResourceUtilsTests, InvalidValidOverlayNameInvalidAttributes) { - auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk", - "InvalidName"); + auto overlay = + OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk"); + ASSERT_TRUE(overlay); + + auto info = (*overlay)->FindOverlayInfo("InvalidName"); ASSERT_FALSE(info); } TEST_F(ResourceUtilsTests, ValidOverlayNameInvalidAttributes) { - auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk", - "ValidName"); + auto overlay = + OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk"); + ASSERT_TRUE(overlay); + + auto info = (*overlay)->FindOverlayInfo("ValidName"); ASSERT_FALSE(info); } TEST_F(ResourceUtilsTests, ValidOverlayNameAndTargetPackageInvalidAttributes) { - auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk", - "ValidNameAndTargetPackage"); + auto overlay = + OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk"); + ASSERT_TRUE(overlay); + + auto info = (*overlay)->FindOverlayInfo("ValidNameAndTargetPackage"); ASSERT_TRUE(info); ASSERT_EQ("ValidNameAndTargetPackage", info->name); ASSERT_EQ("Valid", info->target_package); - ASSERT_EQ("", info->target_name); // Attribute resource id could not be found - ASSERT_EQ(0, info->resource_mapping); // Attribute resource id could not be found + ASSERT_EQ("", info->target_name); // Attribute resource id could not be found + ASSERT_EQ(0, info->resource_mapping); // Attribute resource id could not be found } -}// namespace android::idmap2 +} // namespace android::idmap2 diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h index 842af3dd7b3c..6b5f3a8a98eb 100644 --- a/cmds/idmap2/tests/TestHelpers.h +++ b/cmds/idmap2/tests/TestHelpers.h @@ -24,13 +24,13 @@ namespace android::idmap2 { -const unsigned char idmap_raw_data[] = { +const unsigned char kIdmapRawData[] = { // IDMAP HEADER // 0x0: magic 0x49, 0x44, 0x4d, 0x50, // 0x4: version - 0x07, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, // 0x8: target crc 0x34, 0x12, 0x00, 0x00, @@ -70,81 +70,72 @@ const unsigned char idmap_raw_data[] = { 0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00, // DATA HEADER - // 0x54: target_package_id - 0x7f, - - // 0x55: overlay_package_id - 0x7f, - - // 0x56: padding - 0x00, 0x00, - - // 0x58: target_entry_count + // 0x54: target_entry_count 0x03, 0x00, 0x00, 0x00, - // 0x5c: target_inline_entry_count + // 0x58: target_inline_entry_count 0x01, 0x00, 0x00, 0x00, - // 0x60: overlay_entry_count + // 0x5c: overlay_entry_count 0x03, 0x00, 0x00, 0x00, - // 0x64: string_pool_offset + // 0x60: string_pool_offset 0x00, 0x00, 0x00, 0x00, // TARGET ENTRIES - // 0x68: target id (0x7f020000) + // 0x64: target id (0x7f020000) 0x00, 0x00, 0x02, 0x7f, - // 0x6c: overlay_id (0x7f020000) + // 0x68: overlay_id (0x7f020000) 0x00, 0x00, 0x02, 0x7f, - // 0x70: target id (0x7f030000) + // 0x6c: target id (0x7f030000) 0x00, 0x00, 0x03, 0x7f, - // 0x74: overlay_id (0x7f030000) + // 0x70: overlay_id (0x7f030000) 0x00, 0x00, 0x03, 0x7f, - // 0x78: target id (0x7f030002) + // 0x74: target id (0x7f030002) 0x02, 0x00, 0x03, 0x7f, - // 0x7c: overlay_id (0x7f030001) + // 0x78: overlay_id (0x7f030001) 0x01, 0x00, 0x03, 0x7f, // INLINE TARGET ENTRIES - // 0x80: target_id + // 0x7c: target_id 0x00, 0x00, 0x04, 0x7f, - // 0x84: Res_value::size (value ignored by idmap) + // 0x80: Res_value::size (value ignored by idmap) 0x08, 0x00, - // 0x87: Res_value::res0 (value ignored by idmap) + // 0x82: Res_value::res0 (value ignored by idmap) 0x00, - // 0x88: Res_value::dataType (TYPE_INT_HEX) + // 0x83: Res_value::dataType (TYPE_INT_HEX) 0x11, - // 0x8c: Res_value::data + // 0x84: Res_value::data 0x78, 0x56, 0x34, 0x12, // OVERLAY ENTRIES - // 0x90: 0x7f020000 -> 0x7f020000 + // 0x88: 0x7f020000 -> 0x7f020000 0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f, - // 0x98: 0x7f030000 -> 0x7f030000 + // 0x90: 0x7f030000 -> 0x7f030000 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f, - // 0xa0: 0x7f030001 -> 0x7f030002 + // 0x98: 0x7f030001 -> 0x7f030002 0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f, - // 0xa4: string pool + // 0xa0: string pool // string length, 0x04, 0x00, 0x00, 0x00, - // 0xa8 string contents "test" + // 0xa4 string contents "test" 0x74, 0x65, 0x73, 0x74}; -const unsigned int kIdmapRawDataLen = 0xac; +const unsigned int kIdmapRawDataLen = 0xa8; const unsigned int kIdmapRawDataOffset = 0x54; const unsigned int kIdmapRawDataTargetCrc = 0x1234; const unsigned int kIdmapRawOverlayCrc = 0x5678; diff --git a/cmds/idmap2/tests/XmlParserTests.cpp b/cmds/idmap2/tests/XmlParserTests.cpp index 1a7eaca4d67b..eaf10a7d9282 100644 --- a/cmds/idmap2/tests/XmlParserTests.cpp +++ b/cmds/idmap2/tests/XmlParserTests.cpp @@ -19,25 +19,25 @@ #include #include "TestHelpers.h" -#include "gmock/gmock.h" +#include "androidfw/AssetsProvider.h" #include "gtest/gtest.h" #include "idmap2/XmlParser.h" -#include "idmap2/ZipFile.h" namespace android::idmap2 { -Result> CreateTestParser(const std::string& test_file) { - auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); +Result CreateTestParser(const std::string& test_file) { + auto zip = ZipAssetsProvider::Create(GetTestDataPath() + "/target/target.apk"); if (zip == nullptr) { return Error("Failed to open zip file"); } - auto data = zip->Uncompress(test_file); + auto data = zip->Open(test_file); if (data == nullptr) { return Error("Failed to open xml file"); } - return XmlParser::Create(data->buf, data->size, /* copy_data */ true); + return XmlParser::Create(data->getBuffer(true /* aligned*/), data->getLength(), + /* copy_data */ true); } TEST(XmlParserTests, Create) { @@ -54,7 +54,7 @@ TEST(XmlParserTests, NextChild) { auto xml = CreateTestParser("res/xml/test.xml"); ASSERT_TRUE(xml) << xml.GetErrorMessage(); - auto root_iter = (*xml)->tree_iterator(); + auto root_iter = xml->tree_iterator(); ASSERT_EQ(root_iter->event(), XmlParser::Event::START_TAG); ASSERT_EQ(root_iter->name(), "a"); @@ -85,7 +85,7 @@ TEST(XmlParserTests, AttributeValues) { ASSERT_TRUE(xml) << xml.GetErrorMessage(); // Start at the tag. - auto root_iter = (*xml)->tree_iterator(); + auto root_iter = xml->tree_iterator(); // Start at the tag. auto a_iter = root_iter.begin(); @@ -111,8 +111,8 @@ TEST(XmlParserTests, IteratorEquality) { ASSERT_TRUE(xml) << xml.GetErrorMessage(); // Start at the tag. - auto root_iter_1 = (*xml)->tree_iterator(); - auto root_iter_2 = (*xml)->tree_iterator(); + auto root_iter_1 = xml->tree_iterator(); + auto root_iter_2 = xml->tree_iterator(); ASSERT_EQ(root_iter_1, root_iter_2); ASSERT_EQ(*root_iter_1, *root_iter_2); @@ -146,7 +146,7 @@ TEST(XmlParserTests, Backtracking) { ASSERT_TRUE(xml) << xml.GetErrorMessage(); // Start at the tag. - auto root_iter_1 = (*xml)->tree_iterator(); + auto root_iter_1 = xml->tree_iterator(); // Start at the tag. auto a_iter_1 = root_iter_1.begin(); diff --git a/cmds/idmap2/tests/ZipFileTests.cpp b/cmds/idmap2/tests/ZipFileTests.cpp deleted file mode 100644 index 3fca43621945..000000000000 --- a/cmds/idmap2/tests/ZipFileTests.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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. - */ - -#include // fclose -#include - -#include "TestHelpers.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "idmap2/Result.h" -#include "idmap2/ZipFile.h" - -using ::testing::IsNull; -using ::testing::NotNull; - -namespace android::idmap2 { - -TEST(ZipFileTests, BasicOpen) { - auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); - ASSERT_THAT(zip, NotNull()); - - fclose(stderr); // silence expected warnings from libziparchive - auto fail = ZipFile::Open(GetTestDataPath() + "/does-not-exist"); - ASSERT_THAT(fail, IsNull()); -} - -TEST(ZipFileTests, Crc) { - auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); - ASSERT_THAT(zip, NotNull()); - - Result crc = zip->Crc("AndroidManifest.xml"); - ASSERT_TRUE(crc); - ASSERT_EQ(*crc, 0x762f3d24); - - Result crc2 = zip->Crc("does-not-exist"); - ASSERT_FALSE(crc2); -} - -TEST(ZipFileTests, Uncompress) { - auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); - ASSERT_THAT(zip, NotNull()); - - auto data = zip->Uncompress("assets/lorem-ipsum.txt"); - ASSERT_THAT(data, NotNull()); - const std::string lorem_ipsum("Lorem ipsum dolor sit amet.\n"); - ASSERT_THAT(data->size, lorem_ipsum.size()); - ASSERT_THAT(std::string(reinterpret_cast(data->buf), data->size), lorem_ipsum); - - auto fail = zip->Uncompress("does-not-exist"); - ASSERT_THAT(fail, IsNull()); -} - -} // namespace android::idmap2 diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp index f1998a583a21..af06e2e80cec 100644 --- a/core/jni/android_content_res_ApkAssets.cpp +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -188,7 +188,7 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t forma ATRACE_NAME(base::StringPrintf("LoadApkAssets(%s)", path.c_str()).c_str()); auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider); - std::unique_ptr apk_assets; + std::unique_ptr apk_assets; switch (format) { case FORMAT_APK: { auto assets = MultiAssetsProvider::Create(std::move(loader_assets), diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml index 38e5fa18300c..926b1864d97c 100644 --- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml @@ -25,7 +25,6 @@ - diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index ca5981c0dd5c..9c743cea592a 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -83,8 +83,16 @@ std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap_path, return {}; } + std::unique_ptr overlay_assets; const std::string overlay_path(loaded_idmap->OverlayApkPath()); - auto overlay_assets = ZipAssetsProvider::Create(overlay_path); + if (IsFabricatedOverlay(overlay_path)) { + // Fabricated overlays do not contain resource definitions. All of the overlay resource values + // are defined inline in the idmap. + overlay_assets = EmptyAssetsProvider::Create(); + } else { + // The overlay should be an APK. + overlay_assets = ZipAssetsProvider::Create(overlay_path); + } if (overlay_assets == nullptr) { return {}; } diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 03ab62f48870..36bde5ccf267 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -102,9 +102,8 @@ AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } -bool AssetManager2::SetApkAssets(const std::vector& apk_assets, - bool invalidate_caches) { - apk_assets_ = apk_assets; +bool AssetManager2::SetApkAssets(std::vector apk_assets, bool invalidate_caches) { + apk_assets_ = std::move(apk_assets); BuildDynamicRefTable(); RebuildFilterList(); if (invalidate_caches) { @@ -137,6 +136,36 @@ void AssetManager2::BuildDynamicRefTable() { // 0x01 is reserved for the android package. int next_package_id = 0x02; for (const ApkAssets* apk_assets : sorted_apk_assets) { + std::shared_ptr overlay_ref_table; + if (auto loaded_idmap = apk_assets->GetLoadedIdmap(); loaded_idmap != nullptr) { + // The target package must precede the overlay package in the apk assets paths in order + // to take effect. + auto iter = apk_assets_package_ids.find(std::string(loaded_idmap->TargetApkPath())); + if (iter == apk_assets_package_ids.end()) { + LOG(INFO) << "failed to find target package for overlay " + << loaded_idmap->OverlayApkPath(); + } else { + uint8_t target_package_id = iter->second; + + // Create a special dynamic reference table for the overlay to rewrite references to + // overlay resources as references to the target resources they overlay. + overlay_ref_table = std::make_shared( + loaded_idmap->GetOverlayDynamicRefTable(target_package_id)); + + // Add the overlay resource map to the target package's set of overlays. + const uint8_t target_idx = package_ids_[target_package_id]; + CHECK(target_idx != 0xff) << "overlay target '" << loaded_idmap->TargetApkPath() + << "'added to apk_assets_package_ids but does not have an" + << " assigned package group"; + + PackageGroup& target_package_group = package_groups_[target_idx]; + target_package_group.overlays_.push_back( + ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id, + overlay_ref_table.get()), + apk_assets_cookies[apk_assets]}); + } + } + const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc(); for (const std::unique_ptr& package : loaded_arsc->GetPackages()) { // Get the package ID or assign one if a shared library. @@ -147,50 +176,25 @@ void AssetManager2::BuildDynamicRefTable() { package_id = package->GetPackageId(); } - // Add the mapping for package ID to index if not present. uint8_t idx = package_ids_[package_id]; if (idx == 0xff) { + // Add the mapping for package ID to index if not present. package_ids_[package_id] = idx = static_cast(package_groups_.size()); - package_groups_.push_back({}); - - 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( - std::string(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( - 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()), - apk_assets_cookies[apk_assets]}); - } + PackageGroup& new_group = package_groups_.emplace_back(); + + if (overlay_ref_table != nullptr) { + // If this package is from an overlay, use a dynamic reference table that can rewrite + // overlay resource ids to their corresponding target resource ids. + new_group.dynamic_ref_table = overlay_ref_table; } - DynamicRefTable* ref_table = package_groups_.back().dynamic_ref_table.get(); + DynamicRefTable* ref_table = new_group.dynamic_ref_table.get(); ref_table->mAssignedPackageId = package_id; ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f; } - PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. + PackageGroup* package_group = &package_groups_[idx]; package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); package_group->cookies_.push_back(apk_assets_cookies[apk_assets]); @@ -578,7 +582,7 @@ base::expected AssetManager2::FindEntry( const PackageGroup& package_group = package_groups_[package_idx]; auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config, - stop_at_first_match, ignore_configuration); + stop_at_first_match, ignore_configuration); if (UNLIKELY(!result.has_value())) { return base::unexpected(result.error()); } diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp index 23cacf88a6db..f3c48f7f9fc8 100644 --- a/libs/androidfw/AssetsProvider.cpp +++ b/libs/androidfw/AssetsProvider.cpp @@ -84,7 +84,7 @@ const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const { return value_; } -ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, +ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path, time_t last_mod_time) : zip_handle_(handle, ::CloseArchive), name_(std::forward(path)), @@ -93,7 +93,7 @@ ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, std::unique_ptr ZipAssetsProvider::Create(std::string path) { ZipArchiveHandle handle; if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) { - LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); + LOG(ERROR) << "Failed to open APK '" << path << "': " << ::ErrorCodeString(result); CloseArchive(handle); return {}; } @@ -253,6 +253,14 @@ bool ZipAssetsProvider::ForEachFile(const std::string& root_path, return result == -1; } +std::optional ZipAssetsProvider::GetCrc(std::string_view path) const { + ::ZipEntry entry; + if (FindEntry(zip_handle_.get(), path, &entry) != 0) { + return {}; + } + return entry.crc32; +} + const std::string& ZipAssetsProvider::GetDebugName() const { return name_.GetDebugName(); } diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index f216f55771c2..efd1f6a25786 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -54,12 +54,6 @@ struct Idmap_header { }; struct Idmap_data_header { - uint8_t target_package_id; - uint8_t overlay_package_id; - - // Padding to ensure 4 byte alignment for target_entry_count - uint16_t p0; - uint32_t target_entry_count; uint32_t target_inline_entry_count; uint32_t overlay_entry_count; @@ -158,19 +152,19 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const { 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) << 24U); + // The resource ids encoded within the idmap are build-time resource ids so do not consider the + // package id when determining if the resource in the target package is overlaid. + target_res_id &= 0x00FFFFFFU; // Check if the target resource is mapped to an overlay resource. auto first_entry = entries_; auto end_entry = entries_ + dtohl(data_header_->target_entry_count); auto entry = std::lower_bound(first_entry, end_entry, target_res_id, - [](const Idmap_target_entry &e, const uint32_t target_id) { - return dtohl(e.target_id) < target_id; + [](const Idmap_target_entry& e, const uint32_t target_id) { + return (0x00FFFFFFU & dtohl(e.target_id)) < target_id; }); - if (entry != end_entry && dtohl(entry->target_id) == target_res_id) { + if (entry != end_entry && (0x00FFFFFFU & dtohl(entry->target_id)) == target_res_id) { uint32_t overlay_resource_id = dtohl(entry->overlay_id); // Lookup the resource without rewriting the overlay resource id back to the target resource id // being looked up. @@ -182,12 +176,13 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const { auto first_inline_entry = inline_entries_; auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count); auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id, - [](const Idmap_target_entry_inline &e, + [](const Idmap_target_entry_inline& e, const uint32_t target_id) { - return dtohl(e.target_id) < target_id; + return (0x00FFFFFFU & dtohl(e.target_id)) < target_id; }); - if (inline_entry != end_inline_entry && dtohl(inline_entry->target_id) == target_res_id) { + if (inline_entry != end_inline_entry && + (0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) { return Result(inline_entry->value); } return {}; @@ -235,7 +230,7 @@ std::optional ReadString(const uint8_t** in_out_data_ptr, size } return std::string_view(data, *len); } -} +} // namespace LoadedIdmap::LoadedIdmap(std::string&& idmap_path, const Idmap_header* header, diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 223382731bc0..30500abc39c0 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #ifdef __ANDROID__ #include + #endif #ifndef INT32_MAX @@ -233,6 +235,15 @@ void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs fill9patchOffsets(reinterpret_cast(outData)); } +bool IsFabricatedOverlay(const std::string& path) { + std::ifstream fin(path); + uint32_t magic; + if (fin.read(reinterpret_cast(&magic), sizeof(uint32_t))) { + return magic == kFabricatedOverlayMagic; + } + return false; +} + static bool assertIdmapHeader(const void* idmap, size_t size) { if (reinterpret_cast(idmap) & 0x03) { ALOGE("idmap: header is not word aligned"); diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 6fbd6aa0df7b..2255973f1039 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -101,7 +101,7 @@ class AssetManager2 { // Only pass invalidate_caches=false when it is known that the structure // change in ApkAssets is due to a safe addition of resources with completely // new resource IDs. - bool SetApkAssets(const std::vector& apk_assets, bool invalidate_caches = true); + bool SetApkAssets(std::vector apk_assets, bool invalidate_caches = true); inline const std::vector GetApkAssets() const { return apk_assets_; diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h index 7b06947f45aa..6f16ff453905 100644 --- a/libs/androidfw/include/androidfw/AssetsProvider.h +++ b/libs/androidfw/include/androidfw/AssetsProvider.h @@ -88,6 +88,8 @@ struct ZipAssetsProvider : public AssetsProvider { WARN_UNUSED const std::string& GetDebugName() const override; WARN_UNUSED bool IsUpToDate() const override; + WARN_UNUSED std::optional GetCrc(std::string_view path) const; + ~ZipAssetsProvider() override = default; protected: std::unique_ptr OpenInternal(const std::string& path, Asset::AccessMode mode, diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index 0ded79309bc1..6804472b3d17 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -168,15 +168,14 @@ class LoadedIdmap { } // Returns a mapping from target resource ids to overlay values. - const IdmapResMap GetTargetResourcesMap( - uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const { + const IdmapResMap GetTargetResourcesMap(uint8_t target_assigned_package_id, + const OverlayDynamicRefTable* overlay_ref_table) const { return IdmapResMap(data_header_, target_entries_, target_inline_entries_, target_assigned_package_id, overlay_ref_table); } // Returns a dynamic reference table for a loaded overlay package. - const OverlayDynamicRefTable GetOverlayDynamicRefTable( - uint8_t target_assigned_package_id) const { + const OverlayDynamicRefTable GetOverlayDynamicRefTable(uint8_t target_assigned_package_id) const { return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id); } diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index bfd564c258ee..168a863df2bc 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -43,8 +43,19 @@ namespace android { -constexpr const static uint32_t kIdmapMagic = 0x504D4449u; -constexpr const static uint32_t kIdmapCurrentVersion = 0x00000007u; +constexpr const uint32_t kIdmapMagic = 0x504D4449u; +constexpr const uint32_t kIdmapCurrentVersion = 0x00000008u; + +// This must never change. +constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endian) + +// The version should only be changed when a backwards-incompatible change must be made to the +// fabricated overlay file format. Old fabricated overlays must be migrated to the new file format +// to prevent losing fabricated overlay data. +constexpr const uint32_t kFabricatedOverlayCurrentVersion = 1; + +// Returns whether or not the path represents a fabricated overlay. +bool IsFabricatedOverlay(const std::string& path); /** * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap index 723413c3cea8..88eadccb38cf 100644 Binary files a/libs/androidfw/tests/data/overlay/overlay.idmap and b/libs/androidfw/tests/data/overlay/overlay.idmap differ -- cgit v1.2.3-59-g8ed1b From ef538436300f6424f47368b448c5e9303a52a1b2 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Mon, 1 Mar 2021 14:52:14 -0800 Subject: Don't use ApkAssets::GetPath for equality checks With the introduction of ResourcesProviders, not all ApkAssets have paths on disk. Theme::SetTo and various AssetManager methods use the path to perform equality checking on ApkAssets. This equality check will be performed on the debug string of an ApkAssets if it does not have a path on disk. This causes ApkAssets with the same debug name (like "") to be seen as the same ApkAssets. Rather than using path, the pointer to the ApkAssets should be used for equality checking since ResourcesManager caches and reuses ApkAssets when multiple AssetManagers request the same assets. Bug: 177101983 Test: atest CtsResourcesLoaderTests Change-Id: I11f6a2a3a7cc8febe3f976236792f78e41cf07e6 --- core/java/android/content/res/ApkAssets.java | 13 +++- core/jni/android_content_res_ApkAssets.cpp | 17 ++++- libs/androidfw/ApkAssets.cpp | 6 +- libs/androidfw/AssetManager2.cpp | 86 +++++++++++----------- libs/androidfw/AssetsProvider.cpp | 29 ++++++-- libs/androidfw/include/androidfw/ApkAssets.h | 12 ++- libs/androidfw/include/androidfw/AssetManager2.h | 2 +- libs/androidfw/include/androidfw/AssetsProvider.h | 10 ++- .../navigationbar/NavigationModeController.java | 2 +- 9 files changed, 110 insertions(+), 67 deletions(-) (limited to 'libs/androidfw/AssetsProvider.cpp') diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java index 2d381eb85e90..de48ed75746d 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -22,6 +22,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.om.OverlayableInfo; import android.content.res.loader.AssetsProvider; import android.content.res.loader.ResourcesProvider; +import android.text.TextUtils; import com.android.internal.annotations.GuardedBy; @@ -344,7 +345,14 @@ public final class ApkAssets { @UnsupportedAppUsage public @NonNull String getAssetPath() { synchronized (this) { - return nativeGetAssetPath(mNativePtr); + return TextUtils.emptyIfNull(nativeGetAssetPath(mNativePtr)); + } + } + + /** @hide */ + public @NonNull String getDebugName() { + synchronized (this) { + return nativeGetDebugName(mNativePtr); } } @@ -422,7 +430,7 @@ public final class ApkAssets { @Override public String toString() { - return "ApkAssets{path=" + getAssetPath() + "}"; + return "ApkAssets{path=" + getDebugName() + "}"; } /** @@ -450,6 +458,7 @@ public final class ApkAssets { @NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException; private static native @NonNull String nativeGetAssetPath(long ptr); + private static native @NonNull String nativeGetDebugName(long ptr); private static native long nativeGetStringBlock(long ptr); private static native boolean nativeIsUpToDate(long ptr); private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException; diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp index c7439f1b32d4..b0c575162b56 100644 --- a/core/jni/android_content_res_ApkAssets.cpp +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -83,6 +83,10 @@ class LoaderAssetsProvider : public AssetsProvider { return true; } + std::optional GetPath() const override { + return {}; + } + const std::string& GetDebugName() const override { return debug_name_; } @@ -358,8 +362,16 @@ static jlong NativeGetFinalizer(JNIEnv* /*env*/, jclass /*clazz*/) { } static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { - const ApkAssets* apk_assets = reinterpret_cast(ptr); - return env->NewStringUTF(apk_assets->GetPath().c_str()); + auto apk_assets = reinterpret_cast(ptr); + if (auto path = apk_assets->GetPath()) { + return env->NewStringUTF(path->data()); + } + return nullptr; +} + +static jstring NativeGetDebugName(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + auto apk_assets = reinterpret_cast(ptr); + return env->NewStringUTF(apk_assets->GetDebugName().c_str()); } static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { @@ -467,6 +479,7 @@ static const JNINativeMethod gApkAssetsMethods[] = { (void*)NativeLoadFromFdOffset}, {"nativeGetFinalizer", "()J", (void*)NativeGetFinalizer}, {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath}, + {"nativeGetDebugName", "(J)Ljava/lang/String;", (void*)NativeGetDebugName}, {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock}, {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate}, {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml}, diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 9c743cea592a..76366fce2aea 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -156,7 +156,11 @@ std::unique_ptr ApkAssets::LoadImpl(std::unique_ptr resources_ std::move(loaded_idmap))); } -const std::string& ApkAssets::GetPath() const { +std::optional ApkAssets::GetPath() const { + return assets_provider_->GetPath(); +} + +const std::string& ApkAssets::GetDebugName() const { return assets_provider_->GetDebugName(); } diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 36bde5ccf267..c0ef7be8b673 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -116,8 +116,10 @@ void AssetManager2::BuildDynamicRefTable() { package_groups_.clear(); package_ids_.fill(0xff); - // A mapping from apk assets path to the runtime package id of its first loaded package. - std::unordered_map apk_assets_package_ids; + // A mapping from path of apk assets that could be target packages of overlays to the runtime + // package id of its first loaded package. Overlays currently can only override resources in the + // first package in the target resource table. + std::unordered_map target_assets_package_ids; // 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. @@ -140,8 +142,8 @@ void AssetManager2::BuildDynamicRefTable() { if (auto loaded_idmap = apk_assets->GetLoadedIdmap(); loaded_idmap != nullptr) { // The target package must precede the overlay package in the apk assets paths in order // to take effect. - auto iter = apk_assets_package_ids.find(std::string(loaded_idmap->TargetApkPath())); - if (iter == apk_assets_package_ids.end()) { + auto iter = target_assets_package_ids.find(std::string(loaded_idmap->TargetApkPath())); + if (iter == target_assets_package_ids.end()) { LOG(INFO) << "failed to find target package for overlay " << loaded_idmap->OverlayApkPath(); } else { @@ -205,7 +207,10 @@ void AssetManager2::BuildDynamicRefTable() { package_name, static_cast(entry.package_id)); } - apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id)); + if (auto apk_assets_path = apk_assets->GetPath()) { + // Overlay target ApkAssets must have been created using path based load apis. + target_assets_package_ids.insert(std::make_pair(std::string(*apk_assets_path), package_id)); + } } } @@ -227,7 +232,7 @@ void AssetManager2::DumpToLog() const { std::string list; for (const auto& apk_assets : apk_assets_) { - base::StringAppendF(&list, "%s,", apk_assets->GetPath().c_str()); + base::StringAppendF(&list, "%s,", apk_assets->GetDebugName().c_str()); } LOG(INFO) << "ApkAssets: " << list; @@ -383,8 +388,8 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { } } -std::set AssetManager2::GetNonSystemOverlayPaths() const { - std::set non_system_overlays; +std::set AssetManager2::GetNonSystemOverlays() const { + std::set non_system_overlays; for (const PackageGroup& package_group : package_groups_) { bool found_system_package = false; for (const ConfiguredPackage& package : package_group.packages_) { @@ -396,7 +401,7 @@ std::set AssetManager2::GetNonSystemOverlayPaths() const { if (!found_system_package) { for (const ConfiguredOverlay& overlay : package_group.overlays_) { - non_system_overlays.insert(apk_assets_[overlay.cookie]->GetPath()); + non_system_overlays.insert(apk_assets_[overlay.cookie]); } } } @@ -408,7 +413,7 @@ base::expected, IOError> AssetManager2::GetResourceCon bool exclude_system, bool exclude_mipmap) const { ATRACE_NAME("AssetManager::GetResourceConfigurations"); const auto non_system_overlays = - (exclude_system) ? GetNonSystemOverlayPaths() : std::set(); + (exclude_system) ? GetNonSystemOverlays() : std::set(); std::set configurations; for (const PackageGroup& package_group : package_groups_) { @@ -419,8 +424,8 @@ base::expected, IOError> AssetManager2::GetResourceCon } 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()) { + if (exclude_system && apk_assets->IsOverlay() && + non_system_overlays.find(apk_assets) == non_system_overlays.end()) { // Exclude overlays that target system resources. continue; } @@ -439,7 +444,7 @@ std::set AssetManager2::GetResourceLocales(bool exclude_system, ATRACE_NAME("AssetManager::GetResourceLocales"); std::set locales; const auto non_system_overlays = - (exclude_system) ? GetNonSystemOverlayPaths() : std::set(); + (exclude_system) ? GetNonSystemOverlays() : std::set(); for (const PackageGroup& package_group : package_groups_) { for (size_t i = 0; i < package_group.packages_.size(); i++) { @@ -449,8 +454,8 @@ std::set AssetManager2::GetResourceLocales(bool exclude_system, } 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()) { + if (exclude_system && apk_assets->IsOverlay() && + non_system_overlays.find(apk_assets) == non_system_overlays.end()) { // Exclude overlays that target system resources. continue; } @@ -491,7 +496,7 @@ std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) con AssetDir::FileInfo info; info.setFileName(String8(name.data(), name.size())); info.setFileType(type); - info.setSourceName(String8(apk_assets->GetPath().c_str())); + info.setSourceName(String8(apk_assets->GetDebugName().c_str())); files->add(info); }; @@ -846,7 +851,7 @@ std::string AssetManager2::GetLastResourceResolution() const { } log_stream << "\n\t" << prefix->second << ": " << *step.package_name << " (" - << apk_assets_[step.cookie]->GetPath() << ")"; + << apk_assets_[step.cookie]->GetDebugName() << ")"; if (!step.config_name.isEmpty()) { log_stream << " -" << step.config_name; } @@ -1556,41 +1561,32 @@ base::expected Theme::SetTo(const Theme& o) { std::map src_asset_cookie_id_map; // Determine which ApkAssets are loaded in both theme AssetManagers. - std::vector src_assets = o.asset_manager_->GetApkAssets(); + const auto src_assets = o.asset_manager_->GetApkAssets(); for (size_t i = 0; i < src_assets.size(); i++) { const ApkAssets* src_asset = src_assets[i]; - std::vector dest_assets = asset_manager_->GetApkAssets(); + const auto dest_assets = asset_manager_->GetApkAssets(); for (size_t j = 0; j < dest_assets.size(); j++) { const ApkAssets* dest_asset = dest_assets[j]; + if (src_asset != dest_asset) { + // ResourcesManager caches and reuses ApkAssets when the same apk must be present in + // multiple AssetManagers. Two ApkAssets point to the same version of the same resources + // if they are the same instance. + continue; + } - // Map the runtime package of the source apk asset to the destination apk asset. - if (src_asset->GetPath() == dest_asset->GetPath()) { - const auto& src_packages = src_asset->GetLoadedArsc()->GetPackages(); - const auto& dest_packages = dest_asset->GetLoadedArsc()->GetPackages(); - - SourceToDestinationRuntimePackageMap package_map; - - // The source and destination package should have the same number of packages loaded in - // the same order. - const size_t N = src_packages.size(); - CHECK(N == dest_packages.size()) - << " LoadedArsc " << src_asset->GetPath() << " differs number of packages."; - for (size_t p = 0; p < N; p++) { - auto& src_package = src_packages[p]; - auto& dest_package = dest_packages[p]; - CHECK(src_package->GetPackageName() == dest_package->GetPackageName()) - << " Package " << src_package->GetPackageName() << " differs in load order."; - - int src_package_id = o.asset_manager_->GetAssignedPackageId(src_package.get()); - int dest_package_id = asset_manager_->GetAssignedPackageId(dest_package.get()); - package_map[src_package_id] = dest_package_id; - } - - src_to_dest_asset_cookies.insert(std::make_pair(i, j)); - src_asset_cookie_id_map.insert(std::make_pair(i, package_map)); - break; + // Map the package ids of the asset in the source AssetManager to the package ids of the + // asset in th destination AssetManager. + SourceToDestinationRuntimePackageMap package_map; + for (const auto& loaded_package : src_asset->GetLoadedArsc()->GetPackages()) { + const int src_package_id = o.asset_manager_->GetAssignedPackageId(loaded_package.get()); + const int dest_package_id = asset_manager_->GetAssignedPackageId(loaded_package.get()); + package_map[src_package_id] = dest_package_id; } + + src_to_dest_asset_cookies.insert(std::make_pair(i, j)); + src_asset_cookie_id_map.insert(std::make_pair(i, package_map)); + break; } } diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp index f3c48f7f9fc8..0aaf0b359b60 100644 --- a/libs/androidfw/AssetsProvider.cpp +++ b/libs/androidfw/AssetsProvider.cpp @@ -261,6 +261,13 @@ std::optional ZipAssetsProvider::GetCrc(std::string_view path) const { return entry.crc32; } +std::optional ZipAssetsProvider::GetPath() const { + if (name_.GetPath() != nullptr) { + return *name_.GetPath(); + } + return {}; +} + const std::string& ZipAssetsProvider::GetDebugName() const { return name_.GetDebugName(); } @@ -318,6 +325,10 @@ bool DirectoryAssetsProvider::ForEachFile( return true; } +std::optional DirectoryAssetsProvider::GetPath() const { + return dir_; +} + const std::string& DirectoryAssetsProvider::GetDebugName() const { return dir_; } @@ -336,13 +347,9 @@ MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr&& prima std::unique_ptr&& secondary) : primary_(std::forward>(primary)), secondary_(std::forward>(secondary)) { - if (primary_->GetDebugName() == kEmptyDebugString) { - debug_name_ = secondary_->GetDebugName(); - } else if (secondary_->GetDebugName() == kEmptyDebugString) { - debug_name_ = primary_->GetDebugName(); - } else { - debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName(); - } + debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName(); + path_ = (primary_->GetDebugName() != kEmptyDebugString) ? primary_->GetPath() + : secondary_->GetPath(); } std::unique_ptr MultiAssetsProvider::Create( @@ -367,6 +374,10 @@ bool MultiAssetsProvider::ForEachFile(const std::string& root_path, return primary_->ForEachFile(root_path, f) && secondary_->ForEachFile(root_path, f); } +std::optional MultiAssetsProvider::GetPath() const { + return path_; +} + const std::string& MultiAssetsProvider::GetDebugName() const { return debug_name_; } @@ -394,6 +405,10 @@ bool EmptyAssetsProvider::ForEachFile( return true; } +std::optional EmptyAssetsProvider::GetPath() const { + return {}; +} + const std::string& EmptyAssetsProvider::GetDebugName() const { const static std::string kEmpty = kEmptyDebugString; return kEmpty; diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index d0019ed6be30..6f88f41976cd 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -34,7 +34,6 @@ namespace android { // Holds an APK. class ApkAssets { public: - // Creates an ApkAssets from a path on device. static std::unique_ptr Load(const std::string& path, package_property_t flags = 0U); @@ -61,12 +60,11 @@ class ApkAssets { static std::unique_ptr LoadOverlay(const std::string& idmap_path, package_property_t flags = 0U); - // TODO(177101983): Remove all uses of GetPath for checking whether two ApkAssets are the same. - // With the introduction of ResourcesProviders, not all ApkAssets have paths. This could cause - // bugs when path is used for comparison because multiple ApkAssets could have the same "firendly - // name". Use pointer equality instead. ResourceManager caches and reuses ApkAssets so the - // same asset should have the same pointer. - const std::string& GetPath() const; + // Path to the contents of the ApkAssets on disk. The path could represent an APk, a directory, + // or some other file type. + std::optional GetPath() const; + + const std::string& GetDebugName() const; const AssetsProvider* GetAssetsProvider() const { return assets_provider_.get(); diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 2255973f1039..119f531b8634 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -412,7 +412,7 @@ class AssetManager2 { void RebuildFilterList(); // Retrieves the APK paths of overlays that overlay non-system packages. - std::set GetNonSystemOverlayPaths() const; + std::set GetNonSystemOverlays() const; // AssetManager2::GetBag(resid) wraps this function to track which resource ids have already // been seen while traversing bag parents. diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h index 6f16ff453905..63bbdcc9698d 100644 --- a/libs/androidfw/include/androidfw/AssetsProvider.h +++ b/libs/androidfw/include/androidfw/AssetsProvider.h @@ -48,6 +48,10 @@ struct AssetsProvider { virtual bool ForEachFile(const std::string& path, const std::function& f) const = 0; + // Retrieves the path to the contents of the AssetsProvider on disk. The path could represent an + // APk, a directory, or some other file type. + WARN_UNUSED virtual std::optional GetPath() const = 0; + // Retrieves a name that represents the interface. This may or may not be the path of the // interface source. WARN_UNUSED virtual const std::string& GetDebugName() const = 0; @@ -85,9 +89,9 @@ struct ZipAssetsProvider : public AssetsProvider { bool ForEachFile(const std::string& root_path, const std::function& f) const override; + WARN_UNUSED std::optional GetPath() const override; WARN_UNUSED const std::string& GetDebugName() const override; WARN_UNUSED bool IsUpToDate() const override; - WARN_UNUSED std::optional GetCrc(std::string_view path) const; ~ZipAssetsProvider() override = default; @@ -125,6 +129,7 @@ struct DirectoryAssetsProvider : public AssetsProvider { bool ForEachFile(const std::string& path, const std::function& f) const override; + WARN_UNUSED std::optional GetPath() const override; WARN_UNUSED const std::string& GetDebugName() const override; WARN_UNUSED bool IsUpToDate() const override; @@ -149,6 +154,7 @@ struct MultiAssetsProvider : public AssetsProvider { bool ForEachFile(const std::string& root_path, const std::function& f) const override; + WARN_UNUSED std::optional GetPath() const override; WARN_UNUSED const std::string& GetDebugName() const override; WARN_UNUSED bool IsUpToDate() const override; @@ -163,6 +169,7 @@ struct MultiAssetsProvider : public AssetsProvider { std::unique_ptr primary_; std::unique_ptr secondary_; + std::optional path_; std::string debug_name_; }; @@ -173,6 +180,7 @@ struct EmptyAssetsProvider : public AssetsProvider { bool ForEachFile(const std::string& path, const std::function& f) const override; + WARN_UNUSED std::optional GetPath() const override; WARN_UNUSED const std::string& GetDebugName() const override; WARN_UNUSED bool IsUpToDate() const override; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java index 65d4e23f7734..4b9a4310be5f 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java @@ -200,7 +200,7 @@ public class NavigationModeController implements Dumpable { Log.d(TAG, " assetPaths="); ApkAssets[] assets = context.getResources().getAssets().getApkAssets(); for (ApkAssets a : assets) { - Log.d(TAG, " " + a.getAssetPath()); + Log.d(TAG, " " + a.getDebugName()); } } } -- cgit v1.2.3-59-g8ed1b From bdc0ae12471f1a4a4cae8adbbf83f33a0f3e4ea9 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Mon, 1 Mar 2021 15:18:15 -0800 Subject: Set ApkAssets path for fabricated rros Fabricated RROs do not provide assets but are stored on disk. Ensure that the path to the frro is returned when querying for the ApkAssets path (which is mostly for debug purposes). Bug: 181338216 Test: enable frro, use cmd overlay lookup to see path in resolution Change-Id: Ibf9b1bf0a995325affbf084c71b1e87c5682e734 --- libs/androidfw/ApkAssets.cpp | 2 +- libs/androidfw/AssetManager2.cpp | 32 +++++++++++------------ libs/androidfw/AssetsProvider.cpp | 15 ++++++++++- libs/androidfw/include/androidfw/AssetManager2.h | 5 +--- libs/androidfw/include/androidfw/AssetsProvider.h | 5 ++++ libs/androidfw/tests/AssetManager2_test.cpp | 9 ++++--- 6 files changed, 42 insertions(+), 26 deletions(-) (limited to 'libs/androidfw/AssetsProvider.cpp') diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 76366fce2aea..26d836328b54 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -88,7 +88,7 @@ std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap_path, if (IsFabricatedOverlay(overlay_path)) { // Fabricated overlays do not contain resource definitions. All of the overlay resource values // are defined inline in the idmap. - overlay_assets = EmptyAssetsProvider::Create(); + overlay_assets = EmptyAssetsProvider::Create(overlay_path); } else { // The overlay should be an APK. overlay_assets = ZipAssetsProvider::Create(overlay_path); diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 7e45f952d389..237effeed889 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -602,6 +602,11 @@ base::expected AssetManager2::FindEntry( result->entry = overlay_entry.GetInlineValue(); result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); result->cookie = id_map.cookie; + + if (UNLIKELY(logging_enabled)) { + last_resolution_.steps.push_back( + Resolution::Step{Resolution::Step::Type::OVERLAID_INLINE, String8(), result->cookie}); + } continue; } @@ -630,7 +635,6 @@ base::expected AssetManager2::FindEntry( if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back( Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->config.toString(), - overlay_result->package_name, overlay_result->cookie}); } } @@ -713,7 +717,6 @@ base::expected AssetManager2::FindEntryInternal( if (UNLIKELY(logging_enabled)) { resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::SKIPPED, this_config.toString(), - &loaded_package->GetPackageName(), cookie}); } continue; @@ -731,7 +734,6 @@ base::expected AssetManager2::FindEntryInternal( if (UNLIKELY(logging_enabled)) { resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY, this_config.toString(), - &loaded_package->GetPackageName(), cookie}); } continue; @@ -746,7 +748,6 @@ base::expected AssetManager2::FindEntryInternal( if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back(Resolution::Step{resolution_type, this_config.toString(), - &loaded_package->GetPackageName(), cookie}); } @@ -829,18 +830,18 @@ std::string AssetManager2::GetLastResourceResolution() const { } std::stringstream log_stream; - log_stream << base::StringPrintf("Resolution for 0x%08x ", resid) - << resource_name_string - << "\n\tFor config -" - << configuration_.toString(); + log_stream << base::StringPrintf("Resolution for 0x%08x %s\n" + "\tFor config - %s", resid, resource_name_string.c_str(), + configuration_.toString().c_str()); for (const Resolution::Step& step : last_resolution_.steps) { const static std::unordered_map kStepStrings = { - {Resolution::Step::Type::INITIAL, "Found initial"}, - {Resolution::Step::Type::BETTER_MATCH, "Found better"}, - {Resolution::Step::Type::OVERLAID, "Overlaid"}, - {Resolution::Step::Type::SKIPPED, "Skipped"}, - {Resolution::Step::Type::NO_ENTRY, "No entry"} + {Resolution::Step::Type::INITIAL, "Found initial"}, + {Resolution::Step::Type::BETTER_MATCH, "Found better"}, + {Resolution::Step::Type::OVERLAID, "Overlaid"}, + {Resolution::Step::Type::OVERLAID_INLINE, "Overlaid inline"}, + {Resolution::Step::Type::SKIPPED, "Skipped"}, + {Resolution::Step::Type::NO_ENTRY, "No entry"} }; const auto prefix = kStepStrings.find(step.type); @@ -848,10 +849,9 @@ std::string AssetManager2::GetLastResourceResolution() const { continue; } - log_stream << "\n\t" << prefix->second << ": " << *step.package_name << " (" - << apk_assets_[step.cookie]->GetDebugName() << ")"; + log_stream << "\n\t" << prefix->second << ": " << apk_assets_[step.cookie]->GetDebugName(); if (!step.config_name.isEmpty()) { - log_stream << " -" << step.config_name; + log_stream << " - " << step.config_name; } } diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp index 0aaf0b359b60..6c7a25307247 100644 --- a/libs/androidfw/AssetsProvider.cpp +++ b/libs/androidfw/AssetsProvider.cpp @@ -386,8 +386,15 @@ bool MultiAssetsProvider::IsUpToDate() const { return primary_->IsUpToDate() && secondary_->IsUpToDate(); } +EmptyAssetsProvider::EmptyAssetsProvider(std::optional&& path) : + path_(std::move(path)) {} + std::unique_ptr EmptyAssetsProvider::Create() { - return std::make_unique(); + return std::unique_ptr(new EmptyAssetsProvider({})); +} + +std::unique_ptr EmptyAssetsProvider::Create(const std::string& path) { + return std::unique_ptr(new EmptyAssetsProvider(path)); } std::unique_ptr EmptyAssetsProvider::OpenInternal(const std::string& /* path */, @@ -406,10 +413,16 @@ bool EmptyAssetsProvider::ForEachFile( } std::optional EmptyAssetsProvider::GetPath() const { + if (path_.has_value()) { + return *path_; + } return {}; } const std::string& EmptyAssetsProvider::GetDebugName() const { + if (path_.has_value()) { + return *path_; + } const static std::string kEmpty = kEmptyDebugString; return kEmpty; } diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 10666adfdceb..df3abf63323a 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -458,6 +458,7 @@ class AssetManager2 { INITIAL, BETTER_MATCH, OVERLAID, + OVERLAID_INLINE, SKIPPED, NO_ENTRY, }; @@ -468,10 +469,6 @@ class AssetManager2 { // Built name of configuration for this step. String8 config_name; - // Marks the package name of the better resource found in this step. - const std::string* package_name; - - // ApkAssetsCookie cookie = kInvalidCookie; }; diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h index 63bbdcc9698d..ec51c65053bf 100644 --- a/libs/androidfw/include/androidfw/AssetsProvider.h +++ b/libs/androidfw/include/androidfw/AssetsProvider.h @@ -176,6 +176,7 @@ struct MultiAssetsProvider : public AssetsProvider { // Does not provide any assets. struct EmptyAssetsProvider : public AssetsProvider { static std::unique_ptr Create(); + static std::unique_ptr Create(const std::string& path); bool ForEachFile(const std::string& path, const std::function& f) const override; @@ -188,6 +189,10 @@ struct EmptyAssetsProvider : public AssetsProvider { protected: std::unique_ptr OpenInternal(const std::string& path, Asset::AccessMode mode, bool* file_exists) const override; + + private: + explicit EmptyAssetsProvider(std::optional&& path); + std::optional path_; }; } // namespace android diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index e1c0fab77884..3c4ee4e63a76 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -765,7 +765,8 @@ TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) { auto result = assetmanager.GetLastResourceResolution(); EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n" - "\tFor config -de\n\tFound initial: com.android.basic (basic/basic.apk)", result); + "\tFor config - de\n" + "\tFound initial: basic/basic.apk", result); } TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) { @@ -784,9 +785,9 @@ TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) { auto result = assetmanager.GetLastResourceResolution(); EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n" - "\tFor config -de\n" - "\tFound initial: com.android.basic (basic/basic.apk)\n" - "\tFound better: com.android.basic (basic/basic_de_fr.apk) -de", result); + "\tFor config - de\n" + "\tFound initial: basic/basic.apk\n" + "\tFound better: basic/basic_de_fr.apk - de", result); } TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { -- cgit v1.2.3-59-g8ed1b From c0416698dbaaeba7b706c9eca59e2ba0cab45377 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Tue, 11 May 2021 12:21:29 -0700 Subject: Disable incremental hardening on own resources When an application is incrementally installed, and a resources operation fails due to the resources not being fully present, the app should crash instead of swallowing the error and returning default values to not alter the experience of using the application. Disable IncFsFileMap protections on ApkAssets that are a part of the application that is running (base and splits). Bug: 187220960 Test: atest ResourcesHardeningTest Change-Id: Ibc67aca688720f983c7c656f404593285a54999b --- cmds/idmap2/libidmap2/ResourceContainer.cpp | 2 +- cmds/idmap2/tests/XmlParserTests.cpp | 2 +- core/java/android/app/LoadedApk.java | 4 ++ core/java/android/app/ResourcesManager.java | 51 ++++++++++++++++++++--- core/java/android/content/res/ApkAssets.java | 6 +++ core/jni/android_content_res_ApkAssets.cpp | 37 +++++++++------- libs/androidfw/ApkAssets.cpp | 4 +- libs/androidfw/AssetsProvider.cpp | 16 ++++--- libs/androidfw/TEST_MAPPING | 3 ++ libs/androidfw/include/androidfw/AssetsProvider.h | 9 +++- libs/androidfw/include/androidfw/LoadedArsc.h | 4 ++ 11 files changed, 106 insertions(+), 32 deletions(-) (limited to 'libs/androidfw/AssetsProvider.cpp') diff --git a/cmds/idmap2/libidmap2/ResourceContainer.cpp b/cmds/idmap2/libidmap2/ResourceContainer.cpp index 9147ccaaa17a..c147f6a6024b 100644 --- a/cmds/idmap2/libidmap2/ResourceContainer.cpp +++ b/cmds/idmap2/libidmap2/ResourceContainer.cpp @@ -323,7 +323,7 @@ ApkResourceContainer::ApkResourceContainer(std::unique_ptr zi Result> ApkResourceContainer::FromPath( const std::string& path) { - auto zip_assets = ZipAssetsProvider::Create(path); + auto zip_assets = ZipAssetsProvider::Create(path, 0 /* flags */); if (zip_assets == nullptr) { return Error("failed to load zip assets"); } diff --git a/cmds/idmap2/tests/XmlParserTests.cpp b/cmds/idmap2/tests/XmlParserTests.cpp index eaf10a7d9282..016c5c3a01a5 100644 --- a/cmds/idmap2/tests/XmlParserTests.cpp +++ b/cmds/idmap2/tests/XmlParserTests.cpp @@ -26,7 +26,7 @@ namespace android::idmap2 { Result CreateTestParser(const std::string& test_file) { - auto zip = ZipAssetsProvider::Create(GetTestDataPath() + "/target/target.apk"); + auto zip = ZipAssetsProvider::Create(GetTestDataPath() + "/target/target.apk", 0 /* flags */); if (zip == nullptr) { return Error("Failed to open zip file"); } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 0733a3e7c51c..90a6f32b2344 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -1289,6 +1289,10 @@ public final class LoadedApk { throw new AssertionError("null split not found"); } + if (Process.myUid() == mApplicationInfo.uid) { + ResourcesManager.getInstance().initializeApplicationPaths(mResDir, splitPaths); + } + mResources = ResourcesManager.getInstance().getResources(null, mResDir, splitPaths, mLegacyOverlayDirs, mOverlayPaths, mApplicationInfo.sharedLibraryFiles, null, null, getCompatibilityInfo(), diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 792336d9be9e..f28c760d54d9 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -39,6 +39,7 @@ import android.os.IBinder; import android.os.Process; import android.os.Trace; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Log; import android.util.Pair; @@ -260,6 +261,12 @@ public class ResourcesManager { */ private final UpdateHandler mUpdateCallbacks = new UpdateHandler(); + /** + * The set of APK paths belonging to this process. This is used to disable incremental + * installation crash protections on these APKs so the app either behaves as expects or crashes. + */ + private final ArraySet mApplicationOwnedApks = new ArraySet<>(); + @UnsupportedAppUsage public ResourcesManager() { } @@ -424,6 +431,32 @@ public class ResourcesManager { } } + /** + * Initializes the set of APKs owned by the application running in this process. + */ + public void initializeApplicationPaths(@NonNull String sourceDir, + @Nullable String[] splitDirs) { + synchronized (mLock) { + if (mApplicationOwnedApks.isEmpty()) { + addApplicationPathsLocked(sourceDir, splitDirs); + } + } + } + + /** + * Updates the set of APKs owned by the application running in this process. + * + * This method only appends to the set of APKs owned by this process because the previous APKs + * paths still belong to the application running in this process. + */ + private void addApplicationPathsLocked(@NonNull String sourceDir, + @Nullable String[] splitDirs) { + mApplicationOwnedApks.add(sourceDir); + if (splitDirs != null) { + mApplicationOwnedApks.addAll(Arrays.asList(splitDirs)); + } + } + private static String overlayPathToIdmapPath(String path) { return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap"; } @@ -445,13 +478,17 @@ public class ResourcesManager { } } - // We must load this from disk. + int flags = 0; + if (key.sharedLib) { + flags |= ApkAssets.PROPERTY_DYNAMIC; + } + if (mApplicationOwnedApks.contains(key.path)) { + flags |= ApkAssets.PROPERTY_DISABLE_INCREMENTAL_HARDENING; + } if (key.overlay) { - apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(key.path), - 0 /*flags*/); + apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(key.path), flags); } else { - apkAssets = ApkAssets.loadFromPath(key.path, - key.sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0); + apkAssets = ApkAssets.loadFromPath(key.path, flags); } synchronized (mLock) { @@ -1437,6 +1474,10 @@ public class ResourcesManager { String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs, appInfo.overlayPaths); + if (appInfo.uid == myUid) { + addApplicationPathsLocked(baseCodePath, copiedSplitDirs); + } + final ArrayMap updatedResourceKeys = new ArrayMap<>(); final int implCount = mResourceImpls.size(); for (int i = 0; i < implCount; i++) { diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java index 224ff14c5df4..6fd2d05ad135 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -69,6 +69,12 @@ public final class ApkAssets { */ private static final int PROPERTY_OVERLAY = 1 << 3; + /** + * The apk assets is owned by the application running in this process and incremental crash + * protections for this APK must be disabled. + */ + public static final int PROPERTY_DISABLE_INCREMENTAL_HARDENING = 1 << 4; + /** Flags that change the behavior of loaded apk assets. */ @IntDef(prefix = { "PROPERTY_" }, value = { PROPERTY_SYSTEM, diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp index 9c8daec7919a..1be84282f6db 100644 --- a/core/jni/android_content_res_ApkAssets.cpp +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -212,10 +212,11 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t forma std::unique_ptr apk_assets; switch (format) { case FORMAT_APK: { - auto assets = MultiAssetsProvider::Create(std::move(loader_assets), - ZipAssetsProvider::Create(path.c_str())); - apk_assets = ApkAssets::Load(std::move(assets), property_flags); - break; + auto assets = MultiAssetsProvider::Create(std::move(loader_assets), + ZipAssetsProvider::Create(path.c_str(), + property_flags)); + apk_assets = ApkAssets::Load(std::move(assets), property_flags); + break; } case FORMAT_IDMAP: apk_assets = ApkAssets::LoadOverlay(path.c_str(), property_flags); @@ -271,11 +272,13 @@ static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t std::unique_ptr apk_assets; switch (format) { case FORMAT_APK: { - auto assets = MultiAssetsProvider::Create( - std::move(loader_assets), - ZipAssetsProvider::Create(std::move(dup_fd), friendly_name_utf8.c_str())); - apk_assets = ApkAssets::Load(std::move(assets), property_flags); - break; + auto assets = + MultiAssetsProvider::Create(std::move(loader_assets), + ZipAssetsProvider::Create(std::move(dup_fd), + friendly_name_utf8.c_str(), + property_flags)); + apk_assets = ApkAssets::Load(std::move(assets), property_flags); + break; } case FORMAT_ARSC: apk_assets = ApkAssets::LoadTable( @@ -336,12 +339,16 @@ static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_ std::unique_ptr apk_assets; switch (format) { case FORMAT_APK: { - auto assets = MultiAssetsProvider::Create( - std::move(loader_assets), - ZipAssetsProvider::Create(std::move(dup_fd), friendly_name_utf8.c_str(), - static_cast(offset), static_cast(length))); - apk_assets = ApkAssets::Load(std::move(assets), property_flags); - break; + auto assets = + MultiAssetsProvider::Create(std::move(loader_assets), + ZipAssetsProvider::Create(std::move(dup_fd), + friendly_name_utf8.c_str(), + property_flags, + static_cast(offset), + static_cast( + length))); + apk_assets = ApkAssets::Load(std::move(assets), property_flags); + break; } case FORMAT_ARSC: apk_assets = ApkAssets::LoadTable( diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 26d836328b54..2beb33abe782 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -40,7 +40,7 @@ ApkAssets::ApkAssets(std::unique_ptr resources_asset, loaded_idmap_(std::move(loaded_idmap)) {} std::unique_ptr ApkAssets::Load(const std::string& path, package_property_t flags) { - return Load(ZipAssetsProvider::Create(path), flags); + return Load(ZipAssetsProvider::Create(path, flags), flags); } std::unique_ptr ApkAssets::LoadFromFd(base::unique_fd fd, @@ -91,7 +91,7 @@ std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap_path, overlay_assets = EmptyAssetsProvider::Create(overlay_path); } else { // The overlay should be an APK. - overlay_assets = ZipAssetsProvider::Create(overlay_path); + overlay_assets = ZipAssetsProvider::Create(overlay_path, flags); } if (overlay_assets == nullptr) { return {}; diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp index 6c7a25307247..bce34d37c90b 100644 --- a/libs/androidfw/AssetsProvider.cpp +++ b/libs/androidfw/AssetsProvider.cpp @@ -85,12 +85,14 @@ const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const { } ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path, - time_t last_mod_time) + package_property_t flags, time_t last_mod_time) : zip_handle_(handle, ::CloseArchive), name_(std::forward(path)), + flags_(flags), last_mod_time_(last_mod_time) {} -std::unique_ptr ZipAssetsProvider::Create(std::string path) { +std::unique_ptr ZipAssetsProvider::Create(std::string path, + package_property_t flags) { ZipArchiveHandle handle; if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) { LOG(ERROR) << "Failed to open APK '" << path << "': " << ::ErrorCodeString(result); @@ -109,11 +111,12 @@ std::unique_ptr ZipAssetsProvider::Create(std::string path) { return std::unique_ptr( new ZipAssetsProvider(handle, PathOrDebugName{std::move(path), - true /* is_path */}, sb.st_mtime)); + true /* is_path */}, flags, sb.st_mtime)); } std::unique_ptr ZipAssetsProvider::Create(base::unique_fd fd, std::string friendly_name, + package_property_t flags, off64_t offset, off64_t len) { ZipArchiveHandle handle; @@ -140,7 +143,7 @@ std::unique_ptr ZipAssetsProvider::Create(base::unique_fd fd, return std::unique_ptr( new ZipAssetsProvider(handle, PathOrDebugName{std::move(friendly_name), - false /* is_path */}, sb.st_mtime)); + false /* is_path */}, flags, sb.st_mtime)); } std::unique_ptr ZipAssetsProvider::OpenInternal(const std::string& path, @@ -161,10 +164,11 @@ std::unique_ptr ZipAssetsProvider::OpenInternal(const std::string& path, const int fd = GetFileDescriptor(zip_handle_.get()); const off64_t fd_offset = GetFileDescriptorOffset(zip_handle_.get()); + const bool incremental_hardening = (flags_ & PROPERTY_DISABLE_INCREMENTAL_HARDENING) == 0U; incfs::IncFsFileMap asset_map; if (entry.method == kCompressDeflated) { if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, - name_.GetDebugName().c_str())) { + name_.GetDebugName().c_str(), incremental_hardening)) { LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'"; return {}; @@ -181,7 +185,7 @@ std::unique_ptr ZipAssetsProvider::OpenInternal(const std::string& path, } if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, - name_.GetDebugName().c_str())) { + name_.GetDebugName().c_str(), incremental_hardening)) { LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'"; return {}; } diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING index 766714cd7694..8a5c06d1eef9 100644 --- a/libs/androidfw/TEST_MAPPING +++ b/libs/androidfw/TEST_MAPPING @@ -2,6 +2,9 @@ "presubmit": [ { "name": "CtsResourcesLoaderTests" + }, + { + "name": "ResourcesHardeningTest" } ] } \ No newline at end of file diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h index ec51c65053bf..966ec74c1786 100644 --- a/libs/androidfw/include/androidfw/AssetsProvider.h +++ b/libs/androidfw/include/androidfw/AssetsProvider.h @@ -80,9 +80,12 @@ struct AssetsProvider { // Supplies assets from a zip archive. struct ZipAssetsProvider : public AssetsProvider { - static std::unique_ptr Create(std::string path); + static std::unique_ptr Create(std::string path, + package_property_t flags); + static std::unique_ptr Create(base::unique_fd fd, std::string friendly_name, + package_property_t flags, off64_t offset = 0, off64_t len = kUnknownLength); @@ -101,7 +104,8 @@ struct ZipAssetsProvider : public AssetsProvider { private: struct PathOrDebugName; - ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, time_t last_mod_time); + ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, package_property_t flags, + time_t last_mod_time); struct PathOrDebugName { PathOrDebugName(std::string&& value, bool is_path); @@ -119,6 +123,7 @@ struct ZipAssetsProvider : public AssetsProvider { std::unique_ptr zip_handle_; PathOrDebugName name_; + package_property_t flags_; time_t last_mod_time_; }; diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 9bbdede56293..b3d6a4dcb955 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -92,6 +92,10 @@ enum : package_property_t { // The package is a RRO. PROPERTY_OVERLAY = 1U << 3U, + + // The apk assets is owned by the application running in this process and incremental crash + // protections for this APK must be disabled. + PROPERTY_DISABLE_INCREMENTAL_HARDENING = 1U << 4U, }; struct OverlayableInfo { -- cgit v1.2.3-59-g8ed1b