AAPT2: Add convert command
This command allows a developer to convert their proto APK
(generated from the link phase using --proto-format) into
a binary APK suitable for use on device.
aapt2 convert -o output.apk input.apk
Test: manual + make aapt2_tests
Change-Id: I10a7c33bb4b57006d01fe00a8bf92f78e04e7e50
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index ae32ee9..921d853 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -21,43 +21,139 @@
#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 {
-using xml::XmlResource;
-
-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 {};
}
- io::IFile* file = apk->FindFile("resources.arsc");
- if (!file) {
- context->GetDiagnostics()->Error(DiagMessage(source) << "no resources.arsc found");
+ 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);
+ }
+ diag->Error(DiagMessage(path) << "no resource table found");
+ return {};
+}
+
+std::unique_ptr<LoadedApk> LoadedApk::LoadProtoApkFromFileCollection(
+ const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) {
+ io::IFile* table_file = collection->FindFile(kProtoResourceTablePath);
+ if (table_file == nullptr) {
+ diag->Error(DiagMessage(source) << "failed to find " << kProtoResourceTablePath);
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::InputStream> in = table_file->OpenInputStream();
+ if (in == nullptr) {
+ diag->Error(DiagMessage(source) << "failed to open " << kProtoResourceTablePath);
+ return {};
+ }
+
+ pb::ResourceTable pb_table;
+ io::ZeroCopyInputAdaptor adaptor(in.get());
+ if (!pb_table.ParseFromZeroCopyStream(&adaptor)) {
+ diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath);
+ return {};
+ }
+
+ std::string error;
+ std::unique_ptr<ResourceTable> 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 {};
+ }
+
+ 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::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) {
+ io::IFile* table_file = collection->FindFile(kApkResourceTablePath);
+ if (table_file == nullptr) {
+ diag->Error(DiagMessage(source) << "failed to find " << kApkResourceTablePath);
+
+ return {};
+ }
+
+ std::unique_ptr<io::IData> data = table_file->OpenAsData();
+ if (data == nullptr) {
+ diag->Error(DiagMessage(source) << "failed to open " << kApkResourceTablePath);
return {};
}
std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
- BinaryResourceParser parser(context, table.get(), source, data->data(), data->size(), apk.get());
+ BinaryResourceParser parser(diag, table.get(), source, data->data(), data->size(),
+ collection.get());
if (!parser.Parse()) {
return {};
}
- return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
+ 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> manifest_data = manifest_file->OpenAsData();
+ if (manifest_data == nullptr) {
+ diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath);
+ return {};
+ }
+
+ 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(collection), std::move(table),
+ std::move(manifest));
}
bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
@@ -148,26 +244,4 @@
return true;
}
-std::unique_ptr<xml::XmlResource> LoadedApk::InflateManifest(IAaptContext* context) {
- IDiagnostics* diag = context->GetDiagnostics();
-
- io::IFile* manifest_file = GetFileCollection()->FindFile("AndroidManifest.xml");
- if (manifest_file == nullptr) {
- diag->Error(DiagMessage(source_) << "no AndroidManifest.xml found");
- return {};
- }
-
- std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
- if (manifest_data == nullptr) {
- diag->Error(DiagMessage(manifest_file->GetSource()) << "could not open AndroidManifest.xml");
- return {};
- }
-
- std::unique_ptr<xml::XmlResource> manifest =
- xml::Inflate(manifest_data->data(), manifest_data->size(), diag, manifest_file->GetSource());
- if (manifest == nullptr) {
- diag->Error(DiagMessage() << "failed to read binary AndroidManifest.xml");
- }
- return manifest;
-}
} // namespace aapt