diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index 50f400122fe1b3d01d4e89e64a2c2aab11a5d2ae..34d38d2b1b04cadeca4e2267c22c9011e8896da0 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 09867f3a9c202fd89b81fd21899109f6390e7eab..bf30a76d581ae73984c15c00c146f7ea7fed2e87 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<Unit> Verify(const std::string& idmap_path, const std::string& target_path, @@ -39,11 +41,20 @@ Result<Unit> 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 c93c717a15d29886d0e9f43868694f1458145a59..977a0bbadafb86ce11ec99d2d461cf458d687703 100644 --- a/cmds/idmap2/idmap2/Create.cpp +++ b/cmds/idmap2/idmap2/Create.cpp @@ -20,7 +20,6 @@ #include <fstream> #include <memory> #include <ostream> -#include <string> #include <vector> #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<Unit> Create(const std::vector<std::string>& args) { fulfilled_policies |= PolicyFlags::PUBLIC; } - const std::unique_ptr<const ApkAssets> 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<const ApkAssets> 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<Unit> Create(const std::vector<std::string>& 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 5db391caac309989cb01f6dc31e0905f5c6fbad7..953d99f8dcfb15e2ba5bb7d11126ccef7b0350ea 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<Unit> CreateMultiple(const std::vector<std::string>& args) { fulfilled_policies |= PolicyFlags::PUBLIC; } - const std::unique_ptr<const ApkAssets> 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<std::string> idmap_paths; @@ -108,14 +109,14 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& 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<const ApkAssets> 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 43a1951a5ba96add505c82de5d77954e1ad52141..f41e57cc66d603f41fadf314a3580e55e1c40253 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<Unit> Lookup(const std::vector<std::string>& 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 93537d32299b71d4993383e7bf327b183b5e9437..f62630e702d41188ac3f56c80eb824c7171b1096 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<PolicyBitmask>(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<const IdmapHeader> 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<bool>(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<std::string>* _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<const ApkAssets> 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<const ApkAssets> 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::TargetResourceContainerPtr> 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 0127e874b444a3a030730ace837f29b26d97113c..869a4d981f424ea4d50a1c5a29d1d047a9fbe382 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.h +++ b/cmds/idmap2/idmap2d/Idmap2Service.h @@ -18,12 +18,13 @@ #define IDMAP2_IDMAP2D_IDMAP2SERVICE_H_ #include <android-base/unique_fd.h> +#include <android/os/BnIdmap2.h> #include <binder/BinderService.h> +#include <idmap2/ResourceContainer.h> +#include <idmap2/Result.h> #include <string> -#include "android/os/BnIdmap2.h" - namespace android::os { class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 { @@ -32,27 +33,44 @@ class Idmap2Service : public BinderService<Idmap2Service>, 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<std::string>* _aidl_return) override; private: - // Cache the crc of the android framework package since the crc cannot change without a reboot. - std::optional<uint32_t> 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<idmap2::TargetResourceContainer> framework_apk_cache_; + + template <typename T> + using MaybeUniquePtr = std::variant<std::unique_ptr<T>, T*>; + + using TargetResourceContainerPtr = MaybeUniquePtr<idmap2::TargetResourceContainer>; + idmap2::Result<TargetResourceContainerPtr> GetTargetContainer(const std::string& target_path); + + template <typename T> + WARN_UNUSED static const T* GetPointer(const MaybeUniquePtr<T>& ptr); }; +template <typename T> +const T* Idmap2Service::GetPointer(const MaybeUniquePtr<T>& ptr) { + auto u = std::get_if<T*>(&ptr); + if (u != nullptr) { + return *u; + } + return std::get<std::unique_ptr<T>>(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 0000000000000000000000000000000000000000..d82315723f71cdaab831b9b2e1af31eab587ac1e --- /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 <libidmap2/proto/fabricated_v1.pb.h> + +#include <iostream> +#include <map> +#include <memory> +#include <unordered_map> + +#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<FabricatedOverlay> 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<Entry> entries_; + }; + + Result<Unit> ToBinaryStream(std::ostream& stream); + static Result<FabricatedOverlay> FromBinaryStream(std::istream& stream); + + private: + struct SerializedData { + std::unique_ptr<uint8_t[]> data; + size_t data_size; + uint32_t crc; + }; + + Result<SerializedData*> InitializeData() const; + Result<uint32_t> GetCrc() const; + + FabricatedOverlay(pb::FabricatedOverlay&& overlay, std::optional<uint32_t> crc_from_disk = {}); + + pb::FabricatedOverlay overlay_pb_; + std::optional<uint32_t> crc_from_disk_; + mutable std::optional<SerializedData> data_; + + friend struct FabricatedOverlayContainer; +}; + +struct FabricatedOverlayContainer : public OverlayResourceContainer { + static Result<std::unique_ptr<FabricatedOverlayContainer>> FromPath(std::string path); + static std::unique_ptr<FabricatedOverlayContainer> FromOverlay(FabricatedOverlay&& overlay); + + // inherited from OverlayResourceContainer + WARN_UNUSED Result<OverlayManifestInfo> FindOverlayInfo(const std::string& name) const override; + WARN_UNUSED Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const override; + + // inherited from ResourceContainer + WARN_UNUSED Result<uint32_t> GetCrc() const override; + WARN_UNUSED const std::string& GetPath() const override; + WARN_UNUSED Result<std::string> 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 1b815c1197cad334ae003b1d2a08c5061a5daeeb..58aff42b1e98754f6db39ed98e9042958a891538 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 <vector> #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<uint32_t> GetPackageCrc(const ZipFile& zip_info); - class IdmapHeader { public: static std::unique_ptr<const IdmapHeader> 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<Unit> IsUpToDate(const std::string& target_path, const std::string& overlay_path, - const std::string& overlay_name, PolicyBitmask fulfilled_policies, - bool enforce_overlayable) const; + Result<Unit> IsUpToDate(const TargetResourceContainer& target, + const OverlayResourceContainer& overlay, const std::string& overlay_name, + PolicyBitmask fulfilled_policies, bool enforce_overlayable) const; Result<Unit> 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<const Header> 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<std::unique_ptr<const Idmap>> 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<std::unique_ptr<const Idmap>> FromContainers( + const TargetResourceContainer& target, const OverlayResourceContainer& overlay, + const std::string& overlay_name, const PolicyBitmask& fulfilled_policies, + bool enforce_overlayable); const std::unique_ptr<const IdmapHeader>& GetHeader() const { return header_; diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h index 2b4c761241754c166ca1f804b08564244414748c..4464201a1f2e838e4dd3d6a227aac5a82e9ccfb6 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<std::unique_ptr<const ApkAssets>> apk_assets_; + std::unique_ptr<TargetResourceContainer> target_; + std::unique_ptr<OverlayResourceContainer> overlay_; }; } // namespace idmap2 diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h index 45835164ef8e8a25d5f04fcdbfc46e8a82afbf61..ebd0d1eb2fbcbd8eb29eae39b63ac26e2323b09e 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<std::unique_ptr<const ApkAssets>> apk_assets_; - AssetManager2 target_am_; - AssetManager2 overlay_am_; size_t offset_; + std::unique_ptr<TargetResourceContainer> target_; + std::unique_ptr<OverlayResourceContainer> overlay_; }; } // namespace idmap2 diff --git a/cmds/idmap2/include/idmap2/ResourceContainer.h b/cmds/idmap2/include/idmap2/ResourceContainer.h new file mode 100644 index 0000000000000000000000000000000000000000..7a22dc668e629c7c6197b4dbf9e6e79a72e16361 --- /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 <string> +#include <variant> +#include <vector> + +#include "idmap2/Policies.h" +#include "idmap2/ResourceUtils.h" + +namespace android::idmap2 { + +struct ResourceContainer { + WARN_UNUSED virtual Result<uint32_t> GetCrc() const = 0; + WARN_UNUSED virtual const std::string& GetPath() const = 0; + WARN_UNUSED virtual Result<std::string> GetResourceName(ResourceId id) const = 0; + + virtual ~ResourceContainer() = default; +}; + +struct TargetResourceContainer : public ResourceContainer { + static Result<std::unique_ptr<TargetResourceContainer>> FromPath(std::string path); + + WARN_UNUSED virtual Result<bool> DefinesOverlayable() const = 0; + WARN_UNUSED virtual Result<const android::OverlayableInfo*> GetOverlayableInfo( + ResourceId id) const = 0; + WARN_UNUSED virtual Result<ResourceId> 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<ResourceIdValue, TargetValue> value; + }; + + struct InlineStringPoolData { + // The binary data of the android::ResStringPool string pool. + std::unique_ptr<uint8_t[]> 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<Value> 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<InlineStringPoolData> string_pool_data; +}; + +struct OverlayResourceContainer : public ResourceContainer { + static Result<std::unique_ptr<OverlayResourceContainer>> FromPath(std::string path); + + WARN_UNUSED virtual Result<OverlayManifestInfo> FindOverlayInfo( + const std::string& name) const = 0; + WARN_UNUSED virtual Result<OverlayData> 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 f66916ccb78c109aa57cc57f8f2eee1327169e80..5a0a384f75a31f66ad4405e4f276d5167a1866a1 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 <androidfw/ApkAssets.h> + #include <map> #include <memory> #include <utility> -#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<ResourceId, std::variant<ResourceId, TargetValue>>; using OverlayResourceMap = std::map<ResourceId, ResourceId>; @@ -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<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); + static Result<ResourceMapping> 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<const char*>(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<Unit> 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<Unit> 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<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); - - // 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<ResourceMapping> 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<Unit> AddMapping(ResourceId target_resource, + const std::variant<OverlayData::ResourceIdValue, TargetValue>& value); TargetResourceMap target_map_; - std::multimap<ResourceId, ResourceId> 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<uint8_t[]> 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<const char*>(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 cd14d3e7254ca76421dc4d13a1785270fa0e821f..a0202dfee473f57ea2bf5a1d661e4dfc773b5668 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<OverlayManifestInfo> 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<std::string> 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 1c74ab3bb691bb12464d82dd535feb2d3ea57a8a..c968a5e6c04fd16693262d0abcfc2421b265dbab 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<std::unique_ptr<const XmlParser>> Create(const void* data, size_t size, - bool copy_data = false); - ~XmlParser(); + static Result<XmlParser> 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<ResXMLTree> tree); + mutable std::unique_ptr<ResXMLTree> 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 8f50e36256f6366730af0105c5731ee8c9600da0..0000000000000000000000000000000000000000 --- 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 <memory> -#include <string> - -#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<MemoryChunk> Allocate(size_t size); - - private: - MemoryChunk() { - } -}; - -class ZipFile { - public: - static std::unique_ptr<const ZipFile> Open(const std::string& path); - - std::unique_ptr<const MemoryChunk> Uncompress(const std::string& entryPath) const; - Result<uint32_t> 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 c16310792d128a9c867422dd0e2586e64971b97e..3bbe9d91deb60b2e2f4d73aa05dfadd3b95fa288 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 0000000000000000000000000000000000000000..4a348ac232bc3144ed7bf4117809dc7b0978c27f --- /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 <androidfw/ResourceUtils.h> +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/io/zero_copy_stream_impl_lite.h> +#include <utils/ByteOrder.h> +#include <zlib.h> + +#include <fstream> + +namespace android::idmap2 { + +namespace { +bool Read32(std::istream& stream, uint32_t* out) { + uint32_t value; + if (stream.read(reinterpret_cast<char*>(&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<char*>(&x), sizeof(uint32_t)); +} +} // namespace + +FabricatedOverlay::FabricatedOverlay(pb::FabricatedOverlay&& overlay, + std::optional<uint32_t> crc_from_disk) + : overlay_pb_(std::forward<pb::FabricatedOverlay>(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> FabricatedOverlay::Builder::Build() { + std::map<std::string, std::map<std::string, std::map<std::string, TargetValue>>> 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<std::string, std::map<std::string, TargetValue>>())) + .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<std::string, TargetValue>())) + .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> 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<uint32_t>(crc) + : std::nullopt); +} + +Result<FabricatedOverlay::SerializedData*> FabricatedOverlay::InitializeData() const { + if (!data_.has_value()) { + auto size = overlay_pb_.ByteSizeLong(); + auto data = std::unique_ptr<uint8_t[]>(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<const uint8_t*>(&kFabricatedOverlayCurrentVersion), + sizeof(uint32_t)); + crc = crc32(crc, data.get(), size); + data_ = SerializedData{std::move(data), size, crc}; + } + return &(*data_); +} +Result<uint32_t> 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<Unit> 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<const char*>((*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<FabricatedOverlay>(overlay)), path_(std::forward<std::string>(path)) { +} + +FabContainer::~FabricatedOverlayContainer() = default; + +Result<std::unique_ptr<FabContainer>> FabContainer::FromPath(std::string path) { + std::fstream fin(path); + auto overlay = FabricatedOverlay::FromBinaryStream(fin); + if (!overlay) { + return overlay.GetError(); + } + return std::unique_ptr<FabContainer>( + new FabricatedOverlayContainer(std::move(*overlay), std::move(path))); +} + +std::unique_ptr<FabricatedOverlayContainer> FabContainer::FromOverlay(FabricatedOverlay&& overlay) { + return std::unique_ptr<FabContainer>( + new FabricatedOverlayContainer(std::move(overlay), {} /* path */)); +} + +Result<OverlayManifestInfo> 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<OverlayData> 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<uint8_t>(res_value.data_type()), + .data_value = res_value.data_value()}}); + } + } + } + return result; +} + +Result<uint32_t> FabContainer::GetCrc() const { + return overlay_.GetCrc(); +} + +const std::string& FabContainer::GetPath() const { + return path_; +} + +Result<std::string> 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 a0cc407fc9484337d0bd326c69a49841422c76b2..6515d5516d83b8397c8f3072f0c795f9a030adc9 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -20,23 +20,16 @@ #include <iostream> #include <iterator> #include <limits> -#include <map> #include <memory> -#include <set> #include <string> #include <utility> -#include <vector> #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<uint32_t> GetPackageCrc(const ZipFile& zip) { - const Result<uint32_t> a = zip.Crc("resources.arsc"); - const Result<uint32_t> b = zip.Crc("AndroidManifest.xml"); - return a && b - ? Result<uint32_t>(*a ^ *b) - : Error("failed to get CRC for \"%s\"", a ? "AndroidManifest.xml" : "resources.arsc"); -} - std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) { std::unique_ptr<IdmapHeader> 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<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& s return std::move(idmap_header); } -Result<Unit> IdmapHeader::IsUpToDate(const std::string& target_path, - const std::string& overlay_path, +Result<Unit> IdmapHeader::IsUpToDate(const TargetResourceContainer& target, + const OverlayResourceContainer& overlay, const std::string& overlay_name, PolicyBitmask fulfilled_policies, bool enforce_overlayable) const { - const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path); - if (!target_zip) { - return Error("failed to open target %s", target_path.c_str()); - } - - const Result<uint32_t> target_crc = GetPackageCrc(*target_zip); + const Result<uint32_t> target_crc = target.GetCrc(); if (!target_crc) { return Error("failed to get target crc"); } - const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_path); - if (!overlay_zip) { - return Error("failed to overlay target %s", overlay_path.c_str()); - } - - const Result<uint32_t> overlay_crc = GetPackageCrc(*overlay_zip); + const Result<uint32_t> 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<Unit> IdmapHeader::IsUpToDate(const std::string& target_path, std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) { std::unique_ptr<IdmapData::Header> 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<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping( } std::unique_ptr<IdmapData::Header> 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<uint32_t>(data->target_entries_.size()); data_header->target_entry_inline_count = static_cast<uint32_t>(data->target_inline_entries_.size()); @@ -332,57 +300,46 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping( return {std::move(data)}; } -Result<std::unique_ptr<const Idmap>> 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<std::unique_ptr<const Idmap>> 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<const ZipFile> target_zip = ZipFile::Open(target_apk_path); - if (!target_zip) { - return Error("failed to open target as zip"); - } - - const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_apk_path); - if (!overlay_zip) { - return Error("failed to open overlay as zip"); - } - std::unique_ptr<IdmapHeader> header(new IdmapHeader()); header->magic_ = kIdmapMagic; header->version_ = kIdmapCurrentVersion; - Result<uint32_t> 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 7e090a983f95988e00a5b941fc2f64f62772965d..721612cc567b117205b52a72edcd1e166e7a4be8 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 b517aa3a0c01b31f5bed394918366366318c985a..a016a36a2e3df5418324abb81051d71acd76e9d1 100644 --- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp @@ -18,16 +18,13 @@ #include <algorithm> #include <cstdarg> -#include <string> #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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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 0000000000000000000000000000000000000000..91cb43c216ef738eaeee9874420ad8ef77d6abee --- /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<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages(); + if (packages.empty()) { + return nullptr; + } + return loaded_arsc->GetPackageById(packages[0]->GetPackageId()); +} + +Result<uint32_t> CalculateCrc(const ZipAssetsProvider* zip_assets) { + constexpr const char* kResourcesArsc = "resources.arsc"; + std::optional<uint32_t> 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<uint32_t> man_crc = zip_assets->GetCrc(kManifest); + if (!man_crc) { + return Error("failed to get CRC for '%s'", kManifest); + } + + return *res_crc ^ *man_crc; +} + +Result<XmlParser> 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<uint8_t>(); + 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<XmlParser> 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<OverlayManifestInfo> ExtractOverlayManifestInfo(const ZipAssetsProvider* zip, + const std::string& name) { + Result<XmlParser> 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 <manifest> 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 <overlay> 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 <overlay> 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 <overlay> 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("<overlay> with android:name \"%s\" missing from AndroidManifest.xml", name.c_str()); +} + +Result<OverlayData> 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 <overlay> 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>", overlay_it->name().c_str()); + } + + Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target"); + if (!target_resource) { + return Error(R"(<item> tag missing expected attribute "target")"); + } + + Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value"); + if (!overlay_resource) { + return Error(R"(<item> 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<uint8_t[]>(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<ApkAssets> apk_assets; + const LoadedArsc* arsc; + const LoadedPackage* package; + std::unique_ptr<AssetManager2> am; + ZipAssetsProvider* zip_assets; + + static Result<ResState> Initialize(std::unique_ptr<ZipAssetsProvider> 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<AssetManager2>(); + 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<std::unique_ptr<ApkResourceContainer>> FromPath(const std::string& path); + + // inherited from TargetResourceContainer + Result<bool> DefinesOverlayable() const override; + Result<const android::OverlayableInfo*> GetOverlayableInfo(ResourceId id) const override; + Result<ResourceId> GetResourceId(const std::string& name) const override; + + // inherited from OverlayResourceContainer + Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const override; + Result<OverlayManifestInfo> FindOverlayInfo(const std::string& name) const override; + + // inherited from ResourceContainer + Result<uint32_t> GetCrc() const override; + Result<std::string> GetResourceName(ResourceId id) const override; + const std::string& GetPath() const override; + + ~ApkResourceContainer() override = default; + + private: + ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zip_assets, std::string path); + + Result<const ResState*> GetState() const; + ZipAssetsProvider* GetZipAssets() const; + + mutable std::variant<std::unique_ptr<ZipAssetsProvider>, ResState> state_; + std::string path_; +}; + +ApkResourceContainer::ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zip_assets, + std::string path) + : state_(std::move(zip_assets)), path_(std::move(path)) { +} + +Result<std::unique_ptr<ApkResourceContainer>> 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<ApkResourceContainer>( + new ApkResourceContainer(std::move(zip_assets), path)); +} + +Result<const ResState*> ApkResourceContainer::GetState() const { + if (auto state = std::get_if<ResState>(&state_); state != nullptr) { + return state; + } + + auto state = + ResState::Initialize(std::move(std::get<std::unique_ptr<ZipAssetsProvider>>(state_))); + if (!state) { + return state.GetError(); + } + + state_ = std::move(*state); + return &std::get<ResState>(state_); +} + +ZipAssetsProvider* ApkResourceContainer::GetZipAssets() const { + if (auto zip = std::get_if<std::unique_ptr<ZipAssetsProvider>>(&state_); zip != nullptr) { + return zip->get(); + } + return std::get<ResState>(state_).zip_assets; +} + +Result<bool> ApkResourceContainer::DefinesOverlayable() const { + auto state = GetState(); + if (!state) { + return state.GetError(); + } + return (*state)->package->DefinesOverlayable(); +} + +Result<const android::OverlayableInfo*> ApkResourceContainer::GetOverlayableInfo( + ResourceId id) const { + auto state = GetState(); + if (!state) { + return state.GetError(); + } + return (*state)->package->GetOverlayableInfo(id); +} + +Result<OverlayManifestInfo> ApkResourceContainer::FindOverlayInfo(const std::string& name) const { + return ExtractOverlayManifestInfo(GetZipAssets(), name); +} + +Result<OverlayData> 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<uint32_t> ApkResourceContainer::GetCrc() const { + return CalculateCrc(GetZipAssets()); +} + +const std::string& ApkResourceContainer::GetPath() const { + return path_; +} + +Result<ResourceId> 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<std::string> ApkResourceContainer::GetResourceName(ResourceId id) const { + auto state = GetState(); + if (!state) { + return state.GetError(); + } + return utils::ResToTypeEntryName(*(*state)->am, id); +} + +Result<std::unique_ptr<TargetResourceContainer>> TargetResourceContainer::FromPath( + std::string path) { + auto result = ApkResourceContainer::FromPath(path); + if (!result) { + return result.GetError(); + } + return std::unique_ptr<TargetResourceContainer>(result->release()); +} + +Result<std::unique_ptr<OverlayResourceContainer>> 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<OverlayResourceContainer>(result->release()); + } + + // Fallback to loading the container as an APK. + auto result = ApkResourceContainer::FromPath(path); + if (!result) { + return result.GetError(); + } + return std::unique_ptr<OverlayResourceContainer>(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 46eeb8e6ac80b61e512dd326131389675bbc778f..3bbbf248c87d6d5832f3a0fee80ddb92d8a82395 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<std::string>& policies) { std::string message; for (const std::string& policy : policies) { @@ -55,11 +48,11 @@ std::string ConcatPolicies(const std::vector<std::string>& policies) { return message; } -Result<Unit> CheckOverlayable(const LoadedPackage& target_package, +Result<Unit> 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<Unit> 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<bool> 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<Unit>({}) : Error( "overlay must be preinstalled, signed with the same signature as the target," @@ -77,317 +75,92 @@ Result<Unit> CheckOverlayable(const LoadedPackage& target_package, " <overlay-config-signature>."); } - 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"(<overlay> 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<Unit>({}); } -// 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<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages(); - if (packages.empty()) { - return nullptr; - } - int id = packages[0]->GetPackageId(); - return loaded_arsc.GetPackageById(id); -} - -Result<std::unique_ptr<Asset>> 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> 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 <overlay> 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>", overlay_it->name().c_str()); - } - - Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target"); - if (!target_resource) { - return Error(R"(<item> tag missing expected attribute "target")"); - } - - Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value"); - if (!overlay_resource) { - return Error(R"(<item> 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> 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> 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<std::string> 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<ResourceId> remove_ids; - for (const auto& target_map : target_map_) { - const ResourceId target_resid = target_map.first; - Result<Unit> 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<std::string> 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> 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<uint8_t[]> string_pool_data; - Result<ResourceMapping> 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<Unit> ResourceMapping::AddMapping(ResourceId target_resource, ResourceId overlay_resource, - bool rewrite_overlay_reference) { +Result<Unit> ResourceMapping::AddMapping( + ResourceId target_resource, + const std::variant<OverlayData::ResourceIdValue, TargetValue>& 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<Unit> 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<Unit> 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<OverlayData::ResourceIdValue>(&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<TargetValue>(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<ResourceId>(&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 4e85e57513003514caf62e83ec0a0dfa411aaf74..e809bf1f4b02ebb2068fca6f651dcc1a676254c2 100644 --- a/cmds/idmap2/libidmap2/ResourceUtils.cpp +++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp @@ -17,27 +17,16 @@ #include "idmap2/ResourceUtils.h" #include <memory> -#include <string> #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<std::string> ResToTypeEntryName(const AssetManager2& am, uint32_t resid) return out; } -Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path, - const std::string& name) { - std::unique_ptr<const ZipFile> zip = ZipFile::Open(path); - if (!zip) { - return Error("failed to open %s as a zip file", path.c_str()); - } - - std::unique_ptr<const MemoryChunk> entry = zip->Uncompress("AndroidManifest.xml"); - if (!entry) { - return Error("failed to uncompress AndroidManifest.xml from %s", path.c_str()); - } - - Result<std::unique_ptr<const XmlParser>> 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 <manifest> 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 <overlay> 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 <overlay> 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 <overlay> 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("<overlay> 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 00baea46f90931b5271ce75690c08304992e3714..70822c8902881f1e6af9a43636a7cab38263b78c 100644 --- a/cmds/idmap2/libidmap2/XmlParser.cpp +++ b/cmds/idmap2/libidmap2/XmlParser.cpp @@ -151,16 +151,18 @@ Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& return value ? GetStringValue(parser_, *value, name) : value.GetError(); } -Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, size_t size, - bool copy_data) { - auto parser = std::unique_ptr<const XmlParser>(new XmlParser()); - if (parser->tree_.setTo(data, size, copy_data) != NO_ERROR) { +XmlParser::XmlParser(std::unique_ptr<ResXMLTree> tree) : tree_(std::move(tree)) { +} + +Result<XmlParser> XmlParser::Create(const void* data, size_t size, bool copy_data) { + auto tree = std::make_unique<ResXMLTree>(); + 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<std::unique_ptr<const XmlParser>> 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 1e1a218163f042221088a612dbd216506607f519..0000000000000000000000000000000000000000 --- 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 <memory> -#include <string> - -#include "idmap2/Result.h" - -namespace android::idmap2 { - -std::unique_ptr<MemoryChunk> MemoryChunk::Allocate(size_t size) { - void* ptr = ::operator new(sizeof(MemoryChunk) + size); - std::unique_ptr<MemoryChunk> chunk(reinterpret_cast<MemoryChunk*>(ptr)); - chunk->size = size; - return chunk; -} - -std::unique_ptr<const ZipFile> 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<ZipFile>(new ZipFile(handle)); -} - -ZipFile::~ZipFile() { - ::CloseArchive(handle_); -} - -std::unique_ptr<const MemoryChunk> ZipFile::Uncompress(const std::string& entryPath) const { - ::ZipEntry entry; - int32_t status = ::FindEntry(handle_, entryPath, &entry); - if (status != 0) { - return nullptr; - } - std::unique_ptr<MemoryChunk> chunk = MemoryChunk::Allocate(entry.uncompressed_length); - status = ::ExtractToMemory(handle_, &entry, chunk->buf, chunk->size); - if (status != 0) { - return nullptr; - } - return chunk; -} - -Result<uint32_t> 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 0000000000000000000000000000000000000000..a392b2b6d8566bdd56685f554930b466e705329f --- /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 524aabcec6521a35aa90223d5977cf52279579d3..bf63327427871210205ff0fa7098731e41bf676c 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<const char*>(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast<const char*>(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 0000000000000000000000000000000000000000..79ab2438af74336cd55fa2e8b1f03d36d8be2361 --- /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 <android-base/file.h> +#include <gtest/gtest.h> +#include <idmap2/FabricatedOverlay.h> + +#include <fstream> + +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<TargetValue>(&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<TargetValue>(&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<TargetValue>(&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<TargetValue>(&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 16b68f01e8f5adfa17c6024d2d66676c4cc36529..9516ff83d7184f6113fd2efd6cd4725be0e3164b 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <android-base/file.h> + #include <cstdio> // fclose #include <fstream> #include <memory> @@ -61,12 +63,12 @@ TEST(IdmapTests, TestCanonicalIdmapPathFor) { } TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) { - std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen); std::istringstream stream(raw); std::unique_ptr<const IdmapHeader> 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<const char*>(idmap_raw_data + offset), - kIdmapRawDataLen - offset); + std::string raw(reinterpret_cast<const char*>(kIdmapRawData + offset), kIdmapRawDataLen - offset); std::istringstream stream(raw); std::unique_ptr<const IdmapData::Header> 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<const char*>(idmap_raw_data + offset), - kIdmapRawDataLen - offset); + std::string raw(reinterpret_cast<const char*>(kIdmapRawData + offset), kIdmapRawDataLen - offset); std::istringstream stream(raw); std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream); @@ -134,7 +134,7 @@ TEST(IdmapTests, CreateIdmapDataFromBinaryStream) { } TEST(IdmapTests, CreateIdmapFromBinaryStream) { - std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast<const char*>(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<const char*>(idmap_raw_data), + std::string raw(reinterpret_cast<const char*>(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<const ApkAssets> 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<const ApkAssets> 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<const ApkAssets> 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<const ApkAssets> 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<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); + ASSERT_EQ(dataBlocks.size(), 1U); + + const std::unique_ptr<const IdmapData>& 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<const ApkAssets> 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<const ApkAssets> 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<const ApkAssets> 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<const ApkAssets> 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<std::unique_ptr<const IdmapData>> 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<const ApkAssets> 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<const ApkAssets> 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<std::unique_ptr<const IdmapData>> 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<const char*>(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast<const char*>(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<const char*>(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast<const char*>(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 87ce0f13d19ed914cbe449df64bade40c9eec493..3d3d82a8c7dd667d752db233ddfc72a5c7591a44 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<const ApkAssets> 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<const ApkAssets> 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<const char*>(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast<const char*>(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 88f85efb0f84b8bddd6839b5a47ea79088c96c16..a6371cb74f2e7dc3bd34198639f7732f6e6a7892 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<const ApkAssets> 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<const ApkAssets> 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<const char*>(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast<const char*>(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 0362529c4f3b508c32ed9521807cc08730aa7595..5a1d808af06f28f0cf362610ae8ea3f139eb635c 100644 --- a/cmds/idmap2/tests/ResourceMappingTests.cpp +++ b/cmds/idmap2/tests/ResourceMappingTests.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +#include <android-base/file.h> +#include <androidfw/ResourceTypes.h> +#include <gtest/gtest.h> + #include <cstdio> // fclose #include <fstream> #include <memory> @@ -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<ResourceMapping> TestGetResourceMapping(const std::string& local_target_apk_path, - const std::string& local_overlay_apk_path, +Result<ResourceMapping> 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<const ApkAssets> 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<const ApkAssets> 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<Unit> MappingExists(const ResourceMapping& mapping, ResourceId target_resource, @@ -128,7 +132,7 @@ Result<Unit> 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 <overlayable> 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 1f6bf49f5f0e1bdaafb752a861d6abbc8e939d55..69142086765c07b75a3814c06e7b8f1514c8b09b 100644 --- a/cmds/idmap2/tests/ResourceUtilsTests.cpp +++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp @@ -17,10 +17,12 @@ #include <memory> #include <string> +#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<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000U); - ASSERT_TRUE(name); + Result<std::string> 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 842af3dd7b3c0fa6914ffa153c0e748a8f0a7cc1..6b5f3a8a98eb9f5950d2e4f206efc0abb3e9fe16 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 1a7eaca4d67b977e5fa939fd5c1fc4390b65dfab..eaf10a7d9282f310a78e81dd09af7854a5babbf9 100644 --- a/cmds/idmap2/tests/XmlParserTests.cpp +++ b/cmds/idmap2/tests/XmlParserTests.cpp @@ -19,25 +19,25 @@ #include <string> #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<std::unique_ptr<const XmlParser>> CreateTestParser(const std::string& test_file) { - auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); +Result<XmlParser> 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 <a> tag. - auto root_iter = (*xml)->tree_iterator(); + auto root_iter = xml->tree_iterator(); // Start at the <b> tag. auto a_iter = root_iter.begin(); @@ -111,8 +111,8 @@ TEST(XmlParserTests, IteratorEquality) { ASSERT_TRUE(xml) << xml.GetErrorMessage(); // Start at the <a> 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 <a> tag. - auto root_iter_1 = (*xml)->tree_iterator(); + auto root_iter_1 = xml->tree_iterator(); // Start at the <b> 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 3fca43621945ee67b68411ce2c4f519b1c749963..0000000000000000000000000000000000000000 --- 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 <cstdio> // fclose -#include <string> - -#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<uint32_t> crc = zip->Crc("AndroidManifest.xml"); - ASSERT_TRUE(crc); - ASSERT_EQ(*crc, 0x762f3d24); - - Result<uint32_t> 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<const char*>(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 f1998a583a21a820fd37b8cd0ee55fe02a90811b..af06e2e80cec5086f0025e04e5e9593413a0f700 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<const ApkAssets> apk_assets; + std::unique_ptr<ApkAssets> 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 38e5fa18300cc8de27eb871f773bbc40cfebf72f..926b1864d97c6db3e89e37d99abdbae478e64e88 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 @@ <item target="integer/matrix_100100" value="@integer/matrix_100100"/> <item target="integer/matrix_100101" value="@integer/matrix_100101"/> <item target="integer/matrix_100110" value="@integer/matrix_100110"/> - <item target="integer/matrix_100110" value="@integer/matrix_100110"/> <item target="integer/matrix_100111" value="@integer/matrix_100111"/> <item target="integer/matrix_101000" value="@integer/matrix_101000"/> <item target="integer/matrix_101001" value="@integer/matrix_101001"/> diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index ca5981c0dd5cb83076123ca821ef80706cfbeb06..9c743cea592a6c03d6df5980af826973d71b0e64 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -83,8 +83,16 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path, return {}; } + std::unique_ptr<AssetsProvider> 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 03ab62f48870b9e0dd8ce083d9583942506fd4a4..36bde5ccf267b84ea633e931b6bd66f47c07c4f3 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<const ApkAssets*>& apk_assets, - bool invalidate_caches) { - apk_assets_ = apk_assets; +bool AssetManager2::SetApkAssets(std::vector<const ApkAssets*> 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<OverlayDynamicRefTable> 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<OverlayDynamicRefTable>( + 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<const LoadedPackage>& 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<uint8_t>(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<OverlayDynamicRefTable>( - loaded_idmap->GetOverlayDynamicRefTable(target_package_id)); - package_groups_.back().dynamic_ref_table = overlay_table; - - // Add the overlay resource map to the target package's set of overlays. - target_package_group.overlays_.push_back( - ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id, - overlay_table.get()), - 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<FindEntryResult, NullOrIOError> 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 23cacf88a6db56ddf5f637b4d4624cb135e667f0..f3c48f7f9fc84a69c4d080065084e7e52263350c 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<PathOrDebugName>(path)), @@ -93,7 +93,7 @@ ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, std::unique_ptr<ZipAssetsProvider> 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<uint32_t> 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 f216f55771c21fde1958f9496a8748e138d56f03..efd1f6a25786a31579babdae9a3afa29b44d0537 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<std::string_view> 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 223382731bc017c20a2f02ec56fd87f08bb40c37..30500abc39c028cac74ea1dc3c859da61dad9801 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -25,6 +25,7 @@ #include <string.h> #include <algorithm> +#include <fstream> #include <limits> #include <map> #include <memory> @@ -44,6 +45,7 @@ #ifdef __ANDROID__ #include <binder/TextOutput.h> + #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<Res_png_9patch*>(outData)); } +bool IsFabricatedOverlay(const std::string& path) { + std::ifstream fin(path); + uint32_t magic; + if (fin.read(reinterpret_cast<char*>(&magic), sizeof(uint32_t))) { + return magic == kFabricatedOverlayMagic; + } + return false; +} + static bool assertIdmapHeader(const void* idmap, size_t size) { if (reinterpret_cast<uintptr_t>(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 6fbd6aa0df7b47d4e1e855432f24da499e346eb5..2255973f10391f65463e06a6747e520458699a70 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<const ApkAssets*>& apk_assets, bool invalidate_caches = true); + bool SetApkAssets(std::vector<const ApkAssets*> apk_assets, bool invalidate_caches = true); inline const std::vector<const ApkAssets*> GetApkAssets() const { return apk_assets_; diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h index 7b06947f45aace68a785bcceae197d053ddfe436..6f16ff453905fc5c73eea912cd584a4a005f7167 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<uint32_t> GetCrc(std::string_view path) const; + ~ZipAssetsProvider() override = default; protected: std::unique_ptr<Asset> 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 0ded79309bc1010677f69b8f0c567c8c3d696f96..6804472b3d173a9881279a5ae4ef632d6d11987a 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 bfd564c258ee7601385f00fd1d10a7e4c8bb200b..168a863df2bc1198716638f81dc4d420d8bedf61 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 723413c3cea87aa8306952de0638db9eba854a95..88eadccb38cf63c58de22232f152901695bbfea3 100644 Binary files a/libs/androidfw/tests/data/overlay/overlay.idmap and b/libs/androidfw/tests/data/overlay/overlay.idmap differ