summaryrefslogtreecommitdiff
path: root/tools/aapt2/LoadedApk.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/aapt2/LoadedApk.cpp')
-rw-r--r--tools/aapt2/LoadedApk.cpp202
1 files changed, 177 insertions, 25 deletions
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 7e5efa15f61b..20a9f417228c 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -18,54 +18,156 @@
#include "ResourceValues.h"
#include "ValueVisitor.h"
-#include "flatten/Archive.h"
-#include "flatten/TableFlattener.h"
-#include "io/BigBufferInputStream.h"
+#include "format/Archive.h"
+#include "format/binary/TableFlattener.h"
+#include "format/binary/XmlFlattener.h"
+#include "format/proto/ProtoDeserialize.h"
+#include "io/BigBufferStream.h"
#include "io/Util.h"
+#include "xml/XmlDom.h"
+
+using ::aapt::io::IFile;
+using ::aapt::io::IFileCollection;
+using ::aapt::xml::XmlResource;
+using ::android::StringPiece;
+using ::std::unique_ptr;
namespace aapt {
-std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context,
- const android::StringPiece& path) {
+std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(const StringPiece& path, IDiagnostics* diag) {
Source source(path);
std::string error;
std::unique_ptr<io::ZipFileCollection> apk = io::ZipFileCollection::Create(path, &error);
- if (!apk) {
- context->GetDiagnostics()->Error(DiagMessage(source) << error);
+ if (apk == nullptr) {
+ diag->Error(DiagMessage(path) << "failed opening zip: " << error);
+ return {};
+ }
+
+ 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 {};
+ }
+}
+
+std::unique_ptr<LoadedApk> LoadedApk::LoadProtoApkFromFileCollection(
+ const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) {
+ std::unique_ptr<ResourceTable> table;
+
+ io::IFile* table_file = collection->FindFile(kProtoResourceTablePath);
+ if (table_file != nullptr) {
+ pb::ResourceTable pb_table;
+ std::unique_ptr<io::InputStream> in = table_file->OpenInputStream();
+ if (in == nullptr) {
+ diag->Error(DiagMessage(source) << "failed to open " << kProtoResourceTablePath);
+ return {};
+ }
+
+ io::ZeroCopyInputAdaptor adaptor(in.get());
+ if (!pb_table.ParseFromZeroCopyStream(&adaptor)) {
+ diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath);
+ return {};
+ }
+
+ std::string error;
+ table = util::make_unique<ResourceTable>();
+ if (!DeserializeTableFromPb(pb_table, collection.get(), table.get(), &error)) {
+ diag->Error(DiagMessage(source)
+ << "failed to deserialize " << kProtoResourceTablePath << ": " << error);
+ return {};
+ }
+ }
+
+ io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath);
+ if (manifest_file == nullptr) {
+ diag->Error(DiagMessage(source) << "failed to find " << kAndroidManifestPath);
return {};
}
- io::IFile* file = apk->FindFile("resources.arsc");
- if (!file) {
- context->GetDiagnostics()->Error(DiagMessage(source) << "no resources.arsc found");
+ std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
+ if (manifest_in == nullptr) {
+ diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath);
+ return {};
+ }
+
+ pb::XmlNode pb_node;
+ io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get());
+ if (!pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) {
+ diag->Error(DiagMessage(source) << "failed to read proto " << kAndroidManifestPath);
+ return {};
+ }
+
+ std::string error;
+ std::unique_ptr<xml::XmlResource> manifest = DeserializeXmlResourceFromPb(pb_node, &error);
+ if (manifest == nullptr) {
+ diag->Error(DiagMessage(source)
+ << "failed to deserialize proto " << kAndroidManifestPath << ": " << error);
+ return {};
+ }
+ return util::make_unique<LoadedApk>(source, std::move(collection), std::move(table),
+ std::move(manifest));
+}
+
+std::unique_ptr<LoadedApk> LoadedApk::LoadBinaryApkFromFileCollection(
+ const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) {
+ std::unique_ptr<ResourceTable> table;
+
+ io::IFile* table_file = collection->FindFile(kApkResourceTablePath);
+ if (table_file != nullptr) {
+ table = util::make_unique<ResourceTable>();
+ std::unique_ptr<io::IData> data = table_file->OpenAsData();
+ if (data == nullptr) {
+ diag->Error(DiagMessage(source) << "failed to open " << kApkResourceTablePath);
+ return {};
+ }
+ BinaryResourceParser parser(diag, table.get(), source, data->data(), data->size(),
+ collection.get());
+ if (!parser.Parse()) {
+ return {};
+ }
+ }
+
+ io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath);
+ if (manifest_file == nullptr) {
+ diag->Error(DiagMessage(source) << "failed to find " << kAndroidManifestPath);
return {};
}
- std::unique_ptr<io::IData> data = file->OpenAsData();
- if (!data) {
- context->GetDiagnostics()->Error(DiagMessage(source) << "could not open resources.arsc");
+ std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
+ if (manifest_data == nullptr) {
+ diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath);
return {};
}
- std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
- BinaryResourceParser parser(context, table.get(), source, data->data(), data->size(), apk.get());
- if (!parser.Parse()) {
+ std::string error;
+ std::unique_ptr<xml::XmlResource> manifest =
+ xml::Inflate(manifest_data->data(), manifest_data->size(), &error);
+ if (manifest == nullptr) {
+ diag->Error(DiagMessage(source)
+ << "failed to parse binary " << kAndroidManifestPath << ": " << error);
return {};
}
- return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
+ return util::make_unique<LoadedApk>(source, std::move(collection), std::move(table),
+ std::move(manifest));
}
bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
IArchiveWriter* writer) {
FilterChain empty;
- return WriteToArchive(context, options, &empty, writer);
+ return WriteToArchive(context, table_.get(), options, &empty, writer);
}
-bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
- FilterChain* filters, IArchiveWriter* writer) {
+bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table,
+ const TableFlattenerOptions& options, FilterChain* filters,
+ IArchiveWriter* writer, XmlResource* manifest) {
std::set<std::string> referenced_resources;
// List the files being referenced in the resource table.
- for (auto& pkg : table_->packages) {
+ for (auto& pkg : split_table->packages) {
for (auto& type : pkg->types) {
for (auto& entry : type->entries) {
for (auto& config_value : entry->values) {
@@ -84,7 +186,7 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOption
std::string path = file->GetSource().path;
// The name of the path has the format "<zip-file-name>@<path-to-file>".
- path = path.substr(path.find("@") + 1);
+ path = path.substr(path.find('@') + 1);
// Skip resources that are not referenced if requested.
if (path.find("res/") == 0 && referenced_resources.find(path) == referenced_resources.end()) {
@@ -108,7 +210,7 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOption
// TODO(adamlesinski): How to determine if there were sparse entries (and if to encode
// with sparse entries) b/35389232.
TableFlattener flattener(options, &buffer);
- if (!flattener.Consume(context, table_.get())) {
+ if (!flattener.Consume(context, split_table)) {
return false;
}
@@ -118,9 +220,22 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOption
return false;
}
- } else {
+ } else if (manifest != nullptr && path == "AndroidManifest.xml") {
+ BigBuffer buffer(8192);
+ XmlFlattener xml_flattener(&buffer, {});
+ if (!xml_flattener.Consume(context, manifest)) {
+ context->GetDiagnostics()->Error(DiagMessage(path) << "flattening failed");
+ return false;
+ }
+
uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
- if (!io::CopyFileToArchive(context, file, path, compression_flags, writer)) {
+ io::BigBufferInputStream manifest_buffer_in(&buffer);
+ if (!io::CopyInputStreamToArchive(context, &manifest_buffer_in, path, compression_flags,
+ writer)) {
+ return false;
+ }
+ } else {
+ if (!io::CopyFileToArchivePreserveCompression(context, file, path, writer)) {
return false;
}
}
@@ -128,4 +243,41 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOption
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