From f267a40400259483305a431250a4bd49e7312cb7 Mon Sep 17 00:00:00 2001 From: Pierre Lecesne Date: Fri, 1 Dec 2017 11:39:01 +0000 Subject: AAPT2: Use manifest parsing to determine format of APK. This makes it possible to load APKs that don't have a resource table. Bug: 69355482 Test: Manual Change-Id: I8471dbe068e836b4beea9e6934d18dd16b70ef02 --- tools/aapt2/LoadedApk.cpp | 52 +++++++++++++++++++++++++++++++++++++++++------ tools/aapt2/LoadedApk.h | 8 ++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp index 5981401195a4..33b5a8b2686d 100644 --- a/tools/aapt2/LoadedApk.cpp +++ b/tools/aapt2/LoadedApk.cpp @@ -43,13 +43,16 @@ std::unique_ptr LoadedApk::LoadApkFromPath(const StringPiece& path, I return {}; } - if (apk->FindFile("resources.arsc") != nullptr) { - return LoadBinaryApkFromFileCollection(source, std::move(apk), diag); - } else if (apk->FindFile("resources.pb") != nullptr) { - return LoadProtoApkFromFileCollection(source, std::move(apk), diag); + ApkFormat apkFormat = DetermineApkFormat(apk.get()); + switch (apkFormat) { + case ApkFormat::kBinary: + return LoadBinaryApkFromFileCollection(source, std::move(apk), diag); + case ApkFormat::kProto: + return LoadProtoApkFromFileCollection(source, std::move(apk), diag); + default: + diag->Error(DiagMessage(path) << "could not identify format of APK"); + return {}; } - diag->Error(DiagMessage(path) << "no resource table found"); - return {}; } std::unique_ptr LoadedApk::LoadProtoApkFromFileCollection( @@ -243,4 +246,41 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table return true; } +ApkFormat LoadedApk::DetermineApkFormat(io::IFileCollection* apk) { + if (apk->FindFile("resources.arsc") != nullptr) { + return ApkFormat::kBinary; + } else if (apk->FindFile("resources.pb") != nullptr) { + return ApkFormat::kProto; + } else { + // If the resource table is not present, attempt to read the manifest. + io::IFile* manifest_file = apk->FindFile(kAndroidManifestPath); + if (manifest_file == nullptr) { + return ApkFormat::kUnknown; + } + + // First try in proto format. + std::unique_ptr manifest_in = manifest_file->OpenInputStream(); + if (manifest_in != nullptr) { + pb::XmlNode pb_node; + io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get()); + if (pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) { + return ApkFormat::kProto; + } + } + + // If it didn't work, try in binary format. + std::unique_ptr manifest_data = manifest_file->OpenAsData(); + if (manifest_data != nullptr) { + std::string error; + std::unique_ptr manifest = + xml::Inflate(manifest_data->data(), manifest_data->size(), &error); + if (manifest != nullptr) { + return ApkFormat::kBinary; + } + } + + return ApkFormat::kUnknown; + } +} + } // namespace aapt diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h index ef97de301ad5..6d2257f2e17a 100644 --- a/tools/aapt2/LoadedApk.h +++ b/tools/aapt2/LoadedApk.h @@ -33,6 +33,12 @@ constexpr static const char kApkResourceTablePath[] = "resources.arsc"; constexpr static const char kProtoResourceTablePath[] = "resources.pb"; constexpr static const char kAndroidManifestPath[] = "AndroidManifest.xml"; +enum ApkFormat { + kUnknown, + kBinary, + kProto, +}; + // Info about an APK loaded in memory. class LoadedApk { public: @@ -104,6 +110,8 @@ class LoadedApk { std::unique_ptr apk_; std::unique_ptr table_; std::unique_ptr manifest_; + + static ApkFormat DetermineApkFormat(io::IFileCollection* apk); }; } // namespace aapt -- cgit v1.2.3-59-g8ed1b