diff options
| author | 2017-12-01 11:39:01 +0000 | |
|---|---|---|
| committer | 2017-12-01 11:48:03 +0000 | |
| commit | f267a40400259483305a431250a4bd49e7312cb7 (patch) | |
| tree | 50a66bca3644a31f6255ae3ecad041d42d9dafc3 | |
| parent | 031a2f1aafbc4e39ab5601567862d498e8949538 (diff) | |
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
| -rw-r--r-- | tools/aapt2/LoadedApk.cpp | 52 | ||||
| -rw-r--r-- | 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> 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> 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<io::InputStream> 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<io::IData> manifest_data = manifest_file->OpenAsData(); + if (manifest_data != nullptr) { + std::string error; + std::unique_ptr<xml::XmlResource> 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<io::IFileCollection> apk_; std::unique_ptr<ResourceTable> table_; std::unique_ptr<xml::XmlResource> manifest_; + + static ApkFormat DetermineApkFormat(io::IFileCollection* apk); }; } // namespace aapt |