summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tools/aapt2/Android.bp7
-rw-r--r--tools/aapt2/Debug.cpp34
-rw-r--r--tools/aapt2/Debug.h6
-rw-r--r--tools/aapt2/LoadedApk.cpp2
-rw-r--r--tools/aapt2/Resource.h10
-rw-r--r--tools/aapt2/ResourceParser_test.cpp2
-rw-r--r--tools/aapt2/ResourceUtils.cpp11
-rw-r--r--tools/aapt2/ResourceValues.cpp1
-rw-r--r--tools/aapt2/ResourceValues.h4
-rw-r--r--tools/aapt2/Resources.proto11
-rw-r--r--tools/aapt2/ResourcesInternal.proto10
-rw-r--r--tools/aapt2/cmd/Compile.cpp209
-rw-r--r--tools/aapt2/cmd/Dump.cpp140
-rw-r--r--tools/aapt2/cmd/Link.cpp365
-rw-r--r--tools/aapt2/cmd/Optimize.cpp2
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.cpp2
-rw-r--r--tools/aapt2/format/Container.cpp353
-rw-r--r--tools/aapt2/format/Container.h106
-rw-r--r--tools/aapt2/format/Container_test.cpp90
-rw-r--r--tools/aapt2/format/binary/XmlFlattener.cpp21
-rw-r--r--tools/aapt2/format/binary/XmlFlattener.h6
-rw-r--r--tools/aapt2/format/proto/ProtoDeserialize.cpp114
-rw-r--r--tools/aapt2/format/proto/ProtoDeserialize.h17
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize.cpp63
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize.h27
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize_test.cpp110
-rw-r--r--tools/aapt2/formats.md44
-rw-r--r--tools/aapt2/io/BigBufferOutputStream.h48
-rw-r--r--tools/aapt2/io/BigBufferStream.cpp (renamed from tools/aapt2/io/BigBufferStreams.cpp)31
-rw-r--r--tools/aapt2/io/BigBufferStream.h (renamed from tools/aapt2/io/BigBufferInputStream.h)33
-rw-r--r--tools/aapt2/io/Data.h6
-rw-r--r--tools/aapt2/io/File.cpp4
-rw-r--r--tools/aapt2/io/File.h7
-rw-r--r--tools/aapt2/io/FileStream.cpp (renamed from tools/aapt2/io/FileInputStream.cpp)82
-rw-r--r--tools/aapt2/io/FileStream.h (renamed from tools/aapt2/io/FileInputStream.h)43
-rw-r--r--tools/aapt2/io/FileStream_test.cpp (renamed from tools/aapt2/io/FileInputStream_test.cpp)46
-rw-r--r--tools/aapt2/io/FileSystem.cpp15
-rw-r--r--tools/aapt2/io/FileSystem.h22
-rw-r--r--tools/aapt2/io/Io.h6
-rw-r--r--tools/aapt2/io/StringStream.cpp (renamed from tools/aapt2/io/StringInputStream.cpp)56
-rw-r--r--tools/aapt2/io/StringStream.h (renamed from tools/aapt2/io/StringInputStream.h)45
-rw-r--r--tools/aapt2/io/StringStream_test.cpp (renamed from tools/aapt2/io/StringInputStream_test.cpp)15
-rw-r--r--tools/aapt2/io/Util.cpp7
-rw-r--r--tools/aapt2/io/Util.h75
-rw-r--r--tools/aapt2/io/ZipArchive.cpp14
-rw-r--r--tools/aapt2/io/ZipArchive.h19
-rw-r--r--tools/aapt2/link/TableMerger.cpp31
-rw-r--r--tools/aapt2/link/TableMerger.h19
-rw-r--r--tools/aapt2/link/TableMerger_test.cpp42
-rw-r--r--tools/aapt2/test/Builders.cpp2
-rw-r--r--tools/aapt2/test/Common.h4
-rw-r--r--tools/aapt2/xml/XmlDom.cpp4
-rw-r--r--tools/aapt2/xml/XmlDom_test.cpp2
-rw-r--r--tools/aapt2/xml/XmlPullParser_test.cpp38
-rw-r--r--tools/aapt2/xml/XmlUtil.cpp44
-rw-r--r--tools/aapt2/xml/XmlUtil.h6
56 files changed, 1649 insertions, 884 deletions
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index f964dfed1710..058504d7520d 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -84,17 +84,18 @@ cc_library_host_static {
"filter/AbiFilter.cpp",
"filter/ConfigFilter.cpp",
"format/Archive.cpp",
+ "format/Container.cpp",
"format/binary/BinaryResourceParser.cpp",
"format/binary/ResChunkPullParser.cpp",
"format/binary/TableFlattener.cpp",
"format/binary/XmlFlattener.cpp",
"format/proto/ProtoDeserialize.cpp",
"format/proto/ProtoSerialize.cpp",
- "io/BigBufferStreams.cpp",
+ "io/BigBufferStream.cpp",
"io/File.cpp",
- "io/FileInputStream.cpp",
+ "io/FileStream.cpp",
"io/FileSystem.cpp",
- "io/StringInputStream.cpp",
+ "io/StringStream.cpp",
"io/Util.cpp",
"io/ZipArchive.cpp",
"link/AutoVersioner.cpp",
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 1555d6126493..61c304b2c048 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -35,11 +35,11 @@ namespace aapt {
namespace {
-class PrintVisitor : public DescendingValueVisitor {
+class PrintVisitor : public ConstValueVisitor {
public:
- using DescendingValueVisitor::Visit;
+ using ConstValueVisitor::Visit;
- void Visit(Attribute* attr) override {
+ void Visit(const Attribute* attr) override {
std::cout << "(attr) type=";
attr->PrintMask(&std::cout);
static constexpr uint32_t kMask =
@@ -55,7 +55,7 @@ class PrintVisitor : public DescendingValueVisitor {
}
}
- void Visit(Style* style) override {
+ void Visit(const Style* style) override {
std::cout << "(style)";
if (style->parent) {
const Reference& parent_ref = style->parent.value();
@@ -90,15 +90,15 @@ class PrintVisitor : public DescendingValueVisitor {
}
}
- void Visit(Array* array) override {
+ void Visit(const Array* array) override {
array->Print(&std::cout);
}
- void Visit(Plural* plural) override {
+ void Visit(const Plural* plural) override {
plural->Print(&std::cout);
}
- void Visit(Styleable* styleable) override {
+ void Visit(const Styleable* styleable) override {
std::cout << "(styleable)";
for (const auto& attr : styleable->entries) {
std::cout << "\n ";
@@ -116,17 +116,17 @@ class PrintVisitor : public DescendingValueVisitor {
}
}
- void VisitItem(Item* item) override {
+ void VisitItem(const Item* item) override {
item->Print(&std::cout);
}
};
} // namespace
-void Debug::PrintTable(ResourceTable* table, const DebugPrintTableOptions& options) {
+void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& options) {
PrintVisitor visitor;
- for (auto& package : table->packages) {
+ for (const auto& package : table.packages) {
std::cout << "Package name=" << package->name;
if (package->id) {
std::cout << " id=" << std::hex << (int)package->id.value() << std::dec;
@@ -261,11 +261,11 @@ void Debug::DumpHex(const void* data, size_t len) {
namespace {
-class XmlPrinter : public xml::Visitor {
+class XmlPrinter : public xml::ConstVisitor {
public:
- using xml::Visitor::Visit;
+ using xml::ConstVisitor::Visit;
- void Visit(xml::Element* el) override {
+ void Visit(const xml::Element* el) override {
const size_t previous_size = prefix_.size();
for (const xml::NamespaceDecl& decl : el->namespace_decls) {
@@ -301,11 +301,11 @@ class XmlPrinter : public xml::Visitor {
}
prefix_ += " ";
- xml::Visitor::Visit(el);
+ xml::ConstVisitor::Visit(el);
prefix_.resize(previous_size);
}
- void Visit(xml::Text* text) override {
+ void Visit(const xml::Text* text) override {
std::cerr << prefix_ << "T: '" << text->text << "'\n";
}
@@ -315,9 +315,9 @@ class XmlPrinter : public xml::Visitor {
} // namespace
-void Debug::DumpXml(xml::XmlResource* doc) {
+void Debug::DumpXml(const xml::XmlResource& doc) {
XmlPrinter printer;
- doc->root->Accept(&printer);
+ doc.root->Accept(&printer);
}
} // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index e2456c7f98b2..296d04bd49bd 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -31,13 +31,11 @@ struct DebugPrintTableOptions {
};
struct Debug {
- static void PrintTable(ResourceTable* table,
- const DebugPrintTableOptions& options = {});
+ static void PrintTable(const ResourceTable& table, const DebugPrintTableOptions& options = {});
static void PrintStyleGraph(ResourceTable* table,
const ResourceName& target_style);
static void DumpHex(const void* data, size_t len);
- static void DumpXml(xml::XmlResource* doc);
- static std::string ToString(xml::XmlResource* doc);
+ static void DumpXml(const xml::XmlResource& doc);
};
} // namespace aapt
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index c1815c82b5b5..ae32ee965b48 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -21,7 +21,7 @@
#include "format/Archive.h"
#include "format/binary/TableFlattener.h"
#include "format/binary/XmlFlattener.h"
-#include "io/BigBufferInputStream.h"
+#include "io/BigBufferStream.h"
#include "io/Util.h"
#include "xml/XmlDom.h"
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index cbcc8fb805aa..87b986784961 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -157,12 +157,22 @@ struct SourcedResourceName {
};
struct ResourceFile {
+ enum class Type {
+ kUnknown,
+ kPng,
+ kBinaryXml,
+ kProtoXml,
+ };
+
// Name
ResourceName name;
// Configuration
ConfigDescription config;
+ // Type
+ Type type;
+
// Source
Source source;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index f08b03e7b8af..9a5cd3edb47f 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -22,7 +22,7 @@
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
-#include "io/StringInputStream.h"
+#include "io/StringStream.h"
#include "test/Test.h"
#include "xml/XmlPullParser.h"
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 6fac6e9dfefe..24187d96fec5 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -704,8 +704,15 @@ std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const Config
} else {
if (type != ResourceType::kString && util::StartsWith(str, "res/")) {
// This must be a FileReference.
- return util::make_unique<FileReference>(dst_pool->MakeRef(
- str, StringPool::Context(StringPool::Context::kHighPriority, config)));
+ std::unique_ptr<FileReference> file_ref =
+ util::make_unique<FileReference>(dst_pool->MakeRef(
+ str, StringPool::Context(StringPool::Context::kHighPriority, config)));
+ if (util::EndsWith(*file_ref->path, ".xml")) {
+ file_ref->type = ResourceFile::Type::kBinaryXml;
+ } else if (util::EndsWith(*file_ref->path, ".png")) {
+ file_ref->type = ResourceFile::Type::kPng;
+ }
+ return std::move(file_ref);
}
// There are no styles associated with this string, so treat it as a simple string.
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 608316083f41..082fd86604da 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -292,6 +292,7 @@ bool FileReference::Flatten(android::Res_value* out_value) const {
FileReference* FileReference::Clone(StringPool* new_pool) const {
FileReference* fr = new FileReference(new_pool->MakeRef(path));
fr->file = file;
+ fr->type = type;
fr->comment_ = comment_;
fr->source_ = source_;
return fr;
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 742765d166da..fd242a109006 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -249,6 +249,10 @@ struct FileReference : public BaseItem<FileReference> {
// This field is NOT persisted in any format. It is transient.
io::IFile* file = nullptr;
+ // FileType of the file pointed to by `file`. This is used to know how to inflate the file,
+ // or if to inflate at all (just copy).
+ ResourceFile::Type type = ResourceFile::Type::kUnknown;
+
FileReference() = default;
explicit FileReference(const StringPool::Ref& path);
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 174b7f6b7c8c..7e7c86d53d24 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -269,8 +269,19 @@ message StyledString {
// A value that is a reference to an external entity, like an XML file or a PNG.
message FileReference {
+ enum Type {
+ UNKNOWN = 0;
+ PNG = 1;
+ BINARY_XML = 2;
+ PROTO_XML = 3;
+ }
+
// Path to a file within the APK (typically res/type-config/entry.ext).
string path = 1;
+
+ // The type of file this path points to. For UAM bundle, this cannot be
+ // BINARY_XML.
+ Type type = 2;
}
// A value that represents a primitive data type (float, int, boolean, etc.).
diff --git a/tools/aapt2/ResourcesInternal.proto b/tools/aapt2/ResourcesInternal.proto
index 0b0a252a3452..520b242ee509 100644
--- a/tools/aapt2/ResourcesInternal.proto
+++ b/tools/aapt2/ResourcesInternal.proto
@@ -41,13 +41,13 @@ message CompiledFile {
// The configuration for which the resource is defined.
aapt.pb.Configuration config = 2;
+ // The type of the file.
+ aapt.pb.FileReference.Type type = 3;
+
// The filesystem path to where the source file originated.
// Mainly used to display helpful error messages.
- string source_path = 3;
+ string source_path = 4;
// Any symbols this file auto-generates/exports (eg. @+id/foo in an XML file).
- repeated Symbol exported_symbol = 4;
-
- // If this is a compiled XML file, this is the root node.
- aapt.pb.XmlNode xml_root = 5;
+ repeated Symbol exported_symbol = 5;
}
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index a5e6aefd1e0f..53910afff593 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -36,10 +36,11 @@
#include "compile/PseudolocaleGenerator.h"
#include "compile/XmlIdCollector.h"
#include "format/Archive.h"
-#include "format/binary/XmlFlattener.h"
+#include "format/Container.h"
#include "format/proto/ProtoSerialize.h"
-#include "io/BigBufferOutputStream.h"
-#include "io/FileInputStream.h"
+#include "io/BigBufferStream.h"
+#include "io/FileStream.h"
+#include "io/StringStream.h"
#include "io/Util.h"
#include "util/Files.h"
#include "util/Maybe.h"
@@ -49,6 +50,7 @@
using ::aapt::io::FileInputStream;
using ::android::StringPiece;
+using ::android::base::SystemErrorCodeToString;
using ::google::protobuf::io::CopyingOutputStreamAdaptor;
namespace aapt {
@@ -116,7 +118,7 @@ struct CompileOptions {
bool verbose = false;
};
-static std::string BuildIntermediateFilename(const ResourcePathData& data) {
+static std::string BuildIntermediateContainerFilename(const ResourcePathData& data) {
std::stringstream name;
name << data.resource_dir;
if (!data.config_str.empty()) {
@@ -141,7 +143,7 @@ static bool LoadInputFilesFromDir(IAaptContext* context, const CompileOptions& o
std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
if (!d) {
context->GetDiagnostics()->Error(DiagMessage(root_dir) << "failed to open directory: "
- << android::base::SystemErrorCodeToString(errno));
+ << SystemErrorCodeToString(errno));
return false;
}
@@ -160,7 +162,7 @@ static bool LoadInputFilesFromDir(IAaptContext* context, const CompileOptions& o
std::unique_ptr<DIR, decltype(closedir)*> subdir(opendir(prefix_path.data()), closedir);
if (!subdir) {
context->GetDiagnostics()->Error(DiagMessage(prefix_path) << "failed to open directory: "
- << android::base::SystemErrorCodeToString(errno));
+ << SystemErrorCodeToString(errno));
return false;
}
@@ -241,16 +243,15 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options,
return false;
}
- // Make sure CopyingOutputStreamAdaptor is deleted before we call
- // writer->FinishEntry().
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->FinishEntry().
{
- // Wrap our IArchiveWriter with an adaptor that implements the
- // ZeroCopyOutputStream interface.
+ // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface.
CopyingOutputStreamAdaptor copying_adaptor(writer);
+ ContainerWriter container_writer(&copying_adaptor, 1u);
pb::ResourceTable pb_table;
SerializeTableToPb(table, &pb_table);
- if (!pb_table.SerializeToZeroCopyStream(&copying_adaptor)) {
+ if (!container_writer.AddResTableEntry(pb_table)) {
context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write");
return false;
}
@@ -263,46 +264,8 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options,
return true;
}
-static bool WriteHeaderAndBufferToWriter(const StringPiece& output_path, const ResourceFile& file,
- const BigBuffer& buffer, IArchiveWriter* writer,
- IDiagnostics* diag) {
- // Start the entry so we can write the header.
- if (!writer->StartEntry(output_path, 0)) {
- diag->Error(DiagMessage(output_path) << "failed to open file");
- return false;
- }
-
- // Make sure CopyingOutputStreamAdaptor is deleted before we call
- // writer->FinishEntry().
- {
- // Wrap our IArchiveWriter with an adaptor that implements the
- // ZeroCopyOutputStream interface.
- CopyingOutputStreamAdaptor copying_adaptor(writer);
- CompiledFileOutputStream output_stream(&copying_adaptor);
-
- // Number of CompiledFiles.
- output_stream.WriteLittleEndian32(1);
-
- pb::internal::CompiledFile pb_compiled_file;
- SerializeCompiledFileToPb(file, &pb_compiled_file);
- output_stream.WriteCompiledFile(pb_compiled_file);
- output_stream.WriteData(buffer);
-
- if (output_stream.HadError()) {
- diag->Error(DiagMessage(output_path) << "failed to write data");
- return false;
- }
- }
-
- if (!writer->FinishEntry()) {
- diag->Error(DiagMessage(output_path) << "failed to finish writing data");
- return false;
- }
- return true;
-}
-
-static bool WriteHeaderAndMmapToWriter(const StringPiece& output_path, const ResourceFile& file,
- const android::FileMap& map, IArchiveWriter* writer,
+static bool WriteHeaderAndDataToWriter(const StringPiece& output_path, const ResourceFile& file,
+ io::KnownSizeInputStream* in, IArchiveWriter* writer,
IDiagnostics* diag) {
// Start the entry so we can write the header.
if (!writer->StartEntry(output_path, 0)) {
@@ -310,24 +273,17 @@ static bool WriteHeaderAndMmapToWriter(const StringPiece& output_path, const Res
return false;
}
- // Make sure CopyingOutputStreamAdaptor is deleted before we call
- // writer->FinishEntry().
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->FinishEntry().
{
- // Wrap our IArchiveWriter with an adaptor that implements the
- // ZeroCopyOutputStream interface.
+ // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface.
CopyingOutputStreamAdaptor copying_adaptor(writer);
- CompiledFileOutputStream output_stream(&copying_adaptor);
-
- // Number of CompiledFiles.
- output_stream.WriteLittleEndian32(1);
+ ContainerWriter container_writer(&copying_adaptor, 1u);
pb::internal::CompiledFile pb_compiled_file;
SerializeCompiledFileToPb(file, &pb_compiled_file);
- output_stream.WriteCompiledFile(pb_compiled_file);
- output_stream.WriteData(map.getDataPtr(), map.getDataLength());
- if (output_stream.HadError()) {
- diag->Error(DiagMessage(output_path) << "failed to write data");
+ if (!container_writer.AddResFileEntry(pb_compiled_file, in)) {
+ diag->Error(DiagMessage(output_path) << "failed to write entry data");
return false;
}
}
@@ -339,23 +295,19 @@ static bool WriteHeaderAndMmapToWriter(const StringPiece& output_path, const Res
return true;
}
-static bool FlattenXmlToOutStream(IAaptContext* context, const StringPiece& output_path,
- xml::XmlResource* xmlres, CompiledFileOutputStream* out) {
- BigBuffer buffer(1024);
- XmlFlattenerOptions xml_flattener_options;
- xml_flattener_options.keep_raw_values = true;
- XmlFlattener flattener(&buffer, xml_flattener_options);
- if (!flattener.Consume(context, xmlres)) {
- return false;
- }
-
+static bool FlattenXmlToOutStream(const StringPiece& output_path, const xml::XmlResource& xmlres,
+ ContainerWriter* container_writer, IDiagnostics* diag) {
pb::internal::CompiledFile pb_compiled_file;
- SerializeCompiledFileToPb(xmlres->file, &pb_compiled_file);
- out->WriteCompiledFile(pb_compiled_file);
- out->WriteData(buffer);
+ SerializeCompiledFileToPb(xmlres.file, &pb_compiled_file);
- if (out->HadError()) {
- context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write XML data");
+ pb::XmlNode pb_xml_node;
+ SerializeXmlToPb(*xmlres.root, &pb_xml_node);
+
+ std::string serialized_xml = pb_xml_node.SerializeAsString();
+ io::StringInputStream serialized_in(serialized_xml);
+
+ if (!container_writer->AddResFileEntry(pb_compiled_file, &serialized_in)) {
+ diag->Error(DiagMessage(output_path) << "failed to write entry data");
return false;
}
return true;
@@ -404,6 +356,7 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options,
xmlres->file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
xmlres->file.config = path_data.config;
xmlres->file.source = path_data.source;
+ xmlres->file.type = ResourceFile::Type::kProtoXml;
// Collect IDs that are defined here.
XmlIdCollector collector;
@@ -423,24 +376,23 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options,
return false;
}
+ std::vector<std::unique_ptr<xml::XmlResource>>& inline_documents =
+ inline_xml_format_parser.GetExtractedInlineXmlDocuments();
+
// Make sure CopyingOutputStreamAdaptor is deleted before we call writer->FinishEntry().
{
// Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface.
CopyingOutputStreamAdaptor copying_adaptor(writer);
- CompiledFileOutputStream output_stream(&copying_adaptor);
+ ContainerWriter container_writer(&copying_adaptor, 1u + inline_documents.size());
- std::vector<std::unique_ptr<xml::XmlResource>>& inline_documents =
- inline_xml_format_parser.GetExtractedInlineXmlDocuments();
-
- // Number of CompiledFiles.
- output_stream.WriteLittleEndian32(1 + inline_documents.size());
-
- if (!FlattenXmlToOutStream(context, output_path, xmlres.get(), &output_stream)) {
+ if (!FlattenXmlToOutStream(output_path, *xmlres, &container_writer,
+ context->GetDiagnostics())) {
return false;
}
- for (auto& inline_xml_doc : inline_documents) {
- if (!FlattenXmlToOutStream(context, output_path, inline_xml_doc.get(), &output_stream)) {
+ for (const std::unique_ptr<xml::XmlResource>& inline_xml_doc : inline_documents) {
+ if (!FlattenXmlToOutStream(output_path, *inline_xml_doc, &container_writer,
+ context->GetDiagnostics())) {
return false;
}
}
@@ -465,6 +417,7 @@ static bool CompilePng(IAaptContext* context, const CompileOptions& options,
res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
res_file.config = path_data.config;
res_file.source = path_data.source;
+ res_file.type = ResourceFile::Type::kPng;
{
std::string content;
@@ -472,7 +425,7 @@ static bool CompilePng(IAaptContext* context, const CompileOptions& options,
true /*follow_symlinks*/)) {
context->GetDiagnostics()->Error(DiagMessage(path_data.source)
<< "failed to open file: "
- << android::base::SystemErrorCodeToString(errno));
+ << SystemErrorCodeToString(errno));
return false;
}
@@ -556,8 +509,9 @@ static bool CompilePng(IAaptContext* context, const CompileOptions& options,
}
}
- if (!WriteHeaderAndBufferToWriter(output_path, res_file, buffer, writer,
- context->GetDiagnostics())) {
+ io::BigBufferInputStream buffer_in(&buffer);
+ if (!WriteHeaderAndDataToWriter(output_path, res_file, &buffer_in, writer,
+ context->GetDiagnostics())) {
return false;
}
return true;
@@ -575,6 +529,7 @@ static bool CompileFile(IAaptContext* context, const CompileOptions& options,
res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
res_file.config = path_data.config;
res_file.source = path_data.source;
+ res_file.type = ResourceFile::Type::kUnknown;
std::string error_str;
Maybe<android::FileMap> f = file::MmapPath(path_data.source.path, &error_str);
@@ -584,7 +539,8 @@ static bool CompileFile(IAaptContext* context, const CompileOptions& options,
return false;
}
- if (!WriteHeaderAndMmapToWriter(output_path, res_file, f.value(), writer,
+ io::MmappedData mmapped_in(std::move(f.value()));
+ if (!WriteHeaderAndDataToWriter(output_path, res_file, &mmapped_in, writer,
context->GetDiagnostics())) {
return false;
}
@@ -614,7 +570,7 @@ class CompileContext : public IAaptContext {
}
NameMangler* GetNameMangler() override {
- abort();
+ UNIMPLEMENTED(FATAL) << "No name mangling should be needed in compile phase";
return nullptr;
}
@@ -628,7 +584,7 @@ class CompileContext : public IAaptContext {
}
SymbolTable* GetExternalSymbols() override {
- abort();
+ UNIMPLEMENTED(FATAL) << "No symbols should be needed in compile phase";
return nullptr;
}
@@ -637,14 +593,13 @@ class CompileContext : public IAaptContext {
}
private:
+ DISALLOW_COPY_AND_ASSIGN(CompileContext);
+
IDiagnostics* diagnostics_;
bool verbose_ = false;
};
-/**
- * Entry point for compilation phase. Parses arguments and dispatches to the
- * correct steps.
- */
+// Entry point for compilation phase. Parses arguments and dispatches to the correct steps.
int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
CompileContext context(diagnostics);
CompileOptions options;
@@ -717,50 +672,34 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
continue;
}
+ // Determine how to compile the file based on its type.
+ auto compile_func = &CompileFile;
if (path_data.resource_dir == "values") {
- // Overwrite the extension.
+ compile_func = &CompileTable;
+ // We use a different extension (not necessary anymore, but avoids altering the existing
+ // build system logic).
path_data.extension = "arsc";
-
- const std::string output_filename = BuildIntermediateFilename(path_data);
- if (!CompileTable(&context, options, path_data, archive_writer.get(), output_filename)) {
- error = true;
- }
-
- } else {
- const std::string output_filename = BuildIntermediateFilename(path_data);
- if (const ResourceType* type = ParseResourceType(path_data.resource_dir)) {
- if (*type != ResourceType::kRaw) {
- if (path_data.extension == "xml") {
- if (!CompileXml(&context, options, path_data, archive_writer.get(), output_filename)) {
- error = true;
- }
- } else if (!options.no_png_crunch &&
- (path_data.extension == "png" || path_data.extension == "9.png")) {
- if (!CompilePng(&context, options, path_data, archive_writer.get(), output_filename)) {
- error = true;
- }
- } else {
- if (!CompileFile(&context, options, path_data, archive_writer.get(), output_filename)) {
- error = true;
- }
- }
- } else {
- if (!CompileFile(&context, options, path_data, archive_writer.get(), output_filename)) {
- error = true;
- }
+ } else if (const ResourceType* type = ParseResourceType(path_data.resource_dir)) {
+ if (*type != ResourceType::kRaw) {
+ if (path_data.extension == "xml") {
+ compile_func = &CompileXml;
+ } else if (!options.no_png_crunch &&
+ (path_data.extension == "png" || path_data.extension == "9.png")) {
+ compile_func = &CompilePng;
}
- } else {
- context.GetDiagnostics()->Error(DiagMessage() << "invalid file path '" << path_data.source
- << "'");
- error = true;
}
+ } else {
+ context.GetDiagnostics()->Error(DiagMessage()
+ << "invalid file path '" << path_data.source << "'");
+ error = true;
+ continue;
}
- }
- if (error) {
- return 1;
+ // Compile the file.
+ const std::string out_path = BuildIntermediateContainerFilename(path_data);
+ error |= !compile_func(&context, options, path_data, archive_writer.get(), out_path);
}
- return 0;
+ return error ? 1 : 0;
}
} // namespace aapt
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 44032f6a730d..090c3fbc5731 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -21,8 +21,10 @@
#include "Debug.h"
#include "Diagnostics.h"
#include "Flags.h"
+#include "format/Container.h"
#include "format/binary/BinaryResourceParser.h"
#include "format/proto/ProtoDeserialize.h"
+#include "io/FileStream.h"
#include "io/ZipArchive.h"
#include "process/IResourceTableConsumer.h"
#include "util/Files.h"
@@ -31,42 +33,51 @@ using ::android::StringPiece;
namespace aapt {
-bool DumpCompiledFile(const pb::internal::CompiledFile& pb_file, const void* data, size_t len,
- const Source& source, IAaptContext* context) {
- ResourceFile file;
- std::string error;
- if (!DeserializeCompiledFileFromPb(pb_file, &file, &error)) {
- context->GetDiagnostics()->Warn(DiagMessage(source)
- << "failed to read compiled file: " << error);
- return false;
- }
+static const char* ResourceFileTypeToString(const ResourceFile::Type& type) {
+ switch (type) {
+ case ResourceFile::Type::kPng:
+ return "PNG";
+ case ResourceFile::Type::kBinaryXml:
+ return "BINARY_XML";
+ case ResourceFile::Type::kProtoXml:
+ return "PROTO_XML";
+ default:
+ break;
+ }
+ return "UNKNOWN";
+}
+static void DumpCompiledFile(const ResourceFile& file, const Source& source, off64_t offset,
+ size_t len) {
std::cout << "Resource: " << file.name << "\n"
<< "Config: " << file.config << "\n"
- << "Source: " << file.source << "\n";
- return true;
+ << "Source: " << file.source << "\n"
+ << "Type: " << ResourceFileTypeToString(file.type) << "\n"
+ << "DataOff: " << offset << "\n"
+ << "DataLen: " << len << "\n";
}
-bool TryDumpFile(IAaptContext* context, const std::string& file_path) {
+static bool TryDumpFile(IAaptContext* context, const std::string& file_path) {
+ DebugPrintTableOptions print_options;
+ print_options.show_sources = true;
+
std::string err;
std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err);
if (zip) {
ResourceTable table;
- if (io::IFile* file = zip->FindFile("resources.arsc.flat")) {
+ if (io::IFile* file = zip->FindFile("resources.pb")) {
std::unique_ptr<io::IData> data = file->OpenAsData();
if (data == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage(file_path)
- << "failed to open resources.arsc.flat");
+ context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.pb");
return false;
}
pb::ResourceTable pb_table;
if (!pb_table.ParseFromArray(data->data(), data->size())) {
- context->GetDiagnostics()->Error(DiagMessage(file_path) << "invalid resources.arsc.flat");
+ context->GetDiagnostics()->Error(DiagMessage(file_path) << "invalid resources.pb");
return false;
}
- ResourceTable table;
if (!DeserializeTableFromPb(pb_table, &table, &err)) {
context->GetDiagnostics()->Error(DiagMessage(file_path)
<< "failed to parse table: " << err);
@@ -85,62 +96,72 @@ bool TryDumpFile(IAaptContext* context, const std::string& file_path) {
}
}
- DebugPrintTableOptions options;
- options.show_sources = true;
- Debug::PrintTable(&table, options);
+ Debug::PrintTable(table, print_options);
return true;
}
err.clear();
- Maybe<android::FileMap> file = file::MmapPath(file_path, &err);
- if (!file) {
- context->GetDiagnostics()->Error(DiagMessage(file_path) << err);
+ io::FileInputStream input(file_path);
+ if (input.HadError()) {
+ context->GetDiagnostics()->Error(DiagMessage(file_path)
+ << "failed to open file: " << input.GetError());
return false;
}
- android::FileMap* file_map = &file.value();
-
- // Check to see if this is a loose ResourceTable.
- pb::ResourceTable pb_table;
- if (pb_table.ParseFromArray(file_map->getDataPtr(), file_map->getDataLength())) {
- ResourceTable table;
- if (DeserializeTableFromPb(pb_table, &table, &err)) {
- DebugPrintTableOptions options;
- options.show_sources = true;
- Debug::PrintTable(&table, options);
- return true;
- }
- }
-
// Try as a compiled file.
- CompiledFileInputStream input(file_map->getDataPtr(), file_map->getDataLength());
- uint32_t num_files = 0;
- if (!input.ReadLittleEndian32(&num_files)) {
+ ContainerReader reader(&input);
+ if (reader.HadError()) {
+ context->GetDiagnostics()->Error(DiagMessage(file_path)
+ << "failed to read container: " << reader.GetError());
return false;
}
- for (uint32_t i = 0; i < num_files; i++) {
- pb::internal::CompiledFile compiled_file;
- if (!input.ReadCompiledFile(&compiled_file)) {
- context->GetDiagnostics()->Warn(DiagMessage() << "failed to read compiled file");
- return false;
- }
+ ContainerReaderEntry* entry;
+ while ((entry = reader.Next()) != nullptr) {
+ if (entry->Type() == ContainerEntryType::kResTable) {
+ pb::ResourceTable pb_table;
+ if (!entry->GetResTable(&pb_table)) {
+ context->GetDiagnostics()->Error(DiagMessage(file_path)
+ << "failed to parse proto table: " << entry->GetError());
+ continue;
+ }
- uint64_t offset, len;
- if (!input.ReadDataMetaData(&offset, &len)) {
- context->GetDiagnostics()->Warn(DiagMessage() << "failed to read meta data");
- return false;
- }
+ ResourceTable table;
+ err.clear();
+ if (!DeserializeTableFromPb(pb_table, &table, &err)) {
+ context->GetDiagnostics()->Error(DiagMessage(file_path)
+ << "failed to parse table: " << err);
+ continue;
+ }
- const void* data = static_cast<const uint8_t*>(file_map->getDataPtr()) + offset;
- if (!DumpCompiledFile(compiled_file, data, len, Source(file_path), context)) {
- return false;
+ Debug::PrintTable(table, print_options);
+ } else if (entry->Type() == ContainerEntryType::kResFile) {
+ pb::internal::CompiledFile pb_compiled_file;
+ off64_t offset;
+ size_t length;
+ if (!entry->GetResFileOffsets(&pb_compiled_file, &offset, &length)) {
+ context->GetDiagnostics()->Error(
+ DiagMessage(file_path) << "failed to parse compiled proto file: " << entry->GetError());
+ continue;
+ }
+
+ ResourceFile file;
+ std::string error;
+ if (!DeserializeCompiledFileFromPb(pb_compiled_file, &file, &error)) {
+ context->GetDiagnostics()->Warn(DiagMessage(file_path)
+ << "failed to parse compiled file: " << error);
+ continue;
+ }
+
+ DumpCompiledFile(file, Source(file_path), offset, length);
}
}
return true;
}
+namespace {
+
class DumpContext : public IAaptContext {
public:
PackageType GetPackageType() override {
@@ -153,7 +174,7 @@ class DumpContext : public IAaptContext {
}
NameMangler* GetNameMangler() override {
- abort();
+ UNIMPLEMENTED(FATAL);
return nullptr;
}
@@ -167,7 +188,7 @@ class DumpContext : public IAaptContext {
}
SymbolTable* GetExternalSymbols() override {
- abort();
+ UNIMPLEMENTED(FATAL);
return nullptr;
}
@@ -188,9 +209,9 @@ class DumpContext : public IAaptContext {
bool verbose_ = false;
};
-/**
- * Entry point for dump command.
- */
+} // namespace
+
+// Entry point for dump command.
int Dump(const std::vector<StringPiece>& args) {
bool verbose = false;
Flags flags = Flags().OptionalSwitch("-v", "increase verbosity of output", &verbose);
@@ -206,7 +227,6 @@ int Dump(const std::vector<StringPiece>& args) {
return 1;
}
}
-
return 0;
}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 40d71a3429d0..55a4c438755c 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -25,7 +25,6 @@
#include "android-base/file.h"
#include "android-base/stringprintf.h"
#include "androidfw/StringPiece.h"
-#include "google/protobuf/io/coded_stream.h"
#include "AppInfo.h"
#include "Debug.h"
@@ -39,13 +38,14 @@
#include "compile/IdAssigner.h"
#include "filter/ConfigFilter.h"
#include "format/Archive.h"
+#include "format/Container.h"
#include "format/binary/BinaryResourceParser.h"
#include "format/binary/TableFlattener.h"
#include "format/binary/XmlFlattener.h"
#include "format/proto/ProtoDeserialize.h"
#include "format/proto/ProtoSerialize.h"
-#include "io/BigBufferInputStream.h"
-#include "io/FileInputStream.h"
+#include "io/BigBufferStream.h"
+#include "io/FileStream.h"
#include "io/FileSystem.h"
#include "io/Util.h"
#include "io/ZipArchive.h"
@@ -71,6 +71,14 @@ using ::android::base::StringPrintf;
namespace aapt {
+constexpr static const char kApkResourceTablePath[] = "resources.arsc";
+constexpr static const char kProtoResourceTablePath[] = "resources.pb";
+
+enum class OutputFormat {
+ kApk,
+ kProto,
+};
+
struct LinkOptions {
std::string output_path;
std::string manifest_path;
@@ -79,6 +87,7 @@ struct LinkOptions {
std::vector<std::string> assets_dirs;
bool output_to_directory = false;
bool auto_add_overlay = false;
+ OutputFormat output_format = OutputFormat::kApk;
// Java/Proguard options.
Maybe<std::string> generate_java_class_path;
@@ -253,26 +262,39 @@ class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate {
IAaptContext* context_;
};
-static bool FlattenXml(IAaptContext* context, xml::XmlResource* xml_res, const StringPiece& path,
- bool keep_raw_values, bool utf16, IArchiveWriter* writer) {
- BigBuffer buffer(1024);
- XmlFlattenerOptions options = {};
- options.keep_raw_values = keep_raw_values;
- options.use_utf16 = utf16;
- XmlFlattener flattener(&buffer, options);
- if (!flattener.Consume(context, xml_res)) {
- return false;
- }
-
+static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml_res,
+ const StringPiece& path, bool keep_raw_values, bool utf16,
+ OutputFormat format, IArchiveWriter* writer) {
if (context->IsVerbose()) {
context->GetDiagnostics()->Note(DiagMessage(path) << "writing to archive (keep_raw_values="
<< (keep_raw_values ? "true" : "false")
<< ")");
}
- io::BigBufferInputStream input_stream(&buffer);
- return io::CopyInputStreamToArchive(context, &input_stream, path.to_string(),
- ArchiveEntry::kCompress, writer);
+ switch (format) {
+ case OutputFormat::kApk: {
+ BigBuffer buffer(1024);
+ XmlFlattenerOptions options = {};
+ options.keep_raw_values = keep_raw_values;
+ options.use_utf16 = utf16;
+ XmlFlattener flattener(&buffer, options);
+ if (!flattener.Consume(context, &xml_res)) {
+ return false;
+ }
+
+ io::BigBufferInputStream input_stream(&buffer);
+ return io::CopyInputStreamToArchive(context, &input_stream, path.to_string(),
+ ArchiveEntry::kCompress, writer);
+ } break;
+
+ case OutputFormat::kProto: {
+ pb::XmlNode pb_node;
+ SerializeXmlResourceToPb(xml_res, &pb_node);
+ return io::CopyProtoToArchive(context, &pb_node, path.to_string(), ArchiveEntry::kCompress,
+ writer);
+ } break;
+ }
+ return false;
}
static std::unique_ptr<ResourceTable> LoadTableFromPb(const Source& source, const void* data,
@@ -310,6 +332,7 @@ struct ResourceFileFlattenerOptions {
bool keep_raw_values = false;
bool do_not_compress_anything = false;
bool update_proguard_spec = false;
+ OutputFormat output_format = OutputFormat::kApk;
std::unordered_set<std::string> extensions_to_not_compress;
};
@@ -467,6 +490,10 @@ std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVer
<< "linking " << src.path << " (" << doc->file.name << ")");
}
+ // First, strip out any tools namespace attributes. AAPT stripped them out early, which means
+ // that existing projects have out-of-date references which pass compilation.
+ xml::StripAndroidStudioAttributes(doc->root.get());
+
XmlReferenceLinker xml_linker;
if (!xml_linker.Consume(context_, doc)) {
return {};
@@ -543,9 +570,9 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
file_op.config = config_value->config;
file_op.file_to_copy = file;
- const StringPiece src_path = file->GetSource().path;
if (type->type != ResourceType::kRaw &&
- (util::EndsWith(src_path, ".xml.flat") || util::EndsWith(src_path, ".xml"))) {
+ (file_ref->type == ResourceFile::Type::kBinaryXml ||
+ file_ref->type == ResourceFile::Type::kProtoXml)) {
std::unique_ptr<io::IData> data = file->OpenAsData();
if (!data) {
context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
@@ -553,11 +580,27 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
return false;
}
- file_op.xml_to_flatten = xml::Inflate(data->data(), data->size(),
- context_->GetDiagnostics(), file->GetSource());
+ if (file_ref->type == ResourceFile::Type::kProtoXml) {
+ pb::XmlNode pb_xml_node;
+ if (!pb_xml_node.ParseFromArray(data->data(), static_cast<int>(data->size()))) {
+ context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
+ << "failed to parse proto xml");
+ return false;
+ }
- if (!file_op.xml_to_flatten) {
- return false;
+ std::string error;
+ file_op.xml_to_flatten = DeserializeXmlResourceFromPb(pb_xml_node, &error);
+ if (file_op.xml_to_flatten == nullptr) {
+ context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
+ << "failed to deserialize proto xml: " << error);
+ return false;
+ }
+ } else {
+ file_op.xml_to_flatten = xml::Inflate(data->data(), data->size(),
+ context_->GetDiagnostics(), file->GetSource());
+ if (file_op.xml_to_flatten == nullptr) {
+ return false;
+ }
}
file_op.xml_to_flatten->file.config = config_value->config;
@@ -607,8 +650,9 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
return false;
}
}
- error |= !FlattenXml(context_, doc.get(), dst_path, options_.keep_raw_values,
- false /*utf16*/, archive_writer);
+
+ error |= !FlattenXml(context_, *doc, dst_path, options_.keep_raw_values,
+ false /*utf16*/, options_.output_format, archive_writer);
}
} else {
error |= !io::CopyFileToArchive(context_, file_op.file_to_copy, file_op.dst_path,
@@ -907,23 +951,29 @@ class LinkCommand {
}
}
- bool FlattenTable(ResourceTable* table, IArchiveWriter* writer) {
- BigBuffer buffer(1024);
- TableFlattener flattener(options_.table_flattener_options, &buffer);
- if (!flattener.Consume(context_, table)) {
- context_->GetDiagnostics()->Error(DiagMessage() << "failed to flatten resource table");
- return false;
- }
+ bool FlattenTable(ResourceTable* table, OutputFormat format, IArchiveWriter* writer) {
+ switch (format) {
+ case OutputFormat::kApk: {
+ BigBuffer buffer(1024);
+ TableFlattener flattener(options_.table_flattener_options, &buffer);
+ if (!flattener.Consume(context_, table)) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to flatten resource table");
+ return false;
+ }
- io::BigBufferInputStream input_stream(&buffer);
- return io::CopyInputStreamToArchive(context_, &input_stream, "resources.arsc",
- ArchiveEntry::kAlign, writer);
- }
+ io::BigBufferInputStream input_stream(&buffer);
+ return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath,
+ ArchiveEntry::kAlign, writer);
+ } break;
- bool FlattenTableToPb(ResourceTable* table, IArchiveWriter* writer) {
- pb::ResourceTable pb_table;
- SerializeTableToPb(*table, &pb_table);
- return io::CopyProtoToArchive(context_, &pb_table, "resources.arsc.flat", 0, writer);
+ case OutputFormat::kProto: {
+ pb::ResourceTable pb_table;
+ SerializeTableToPb(*table, &pb_table);
+ return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath,
+ ArchiveEntry::kCompress, writer);
+ } break;
+ }
+ return false;
}
bool WriteJavaFile(ResourceTable* table, const StringPiece& package_name_to_generate,
@@ -1152,7 +1202,7 @@ class LinkCommand {
}
std::unique_ptr<ResourceTable> LoadTablePbFromCollection(io::IFileCollection* collection) {
- io::IFile* file = collection->FindFile("resources.arsc.flat");
+ io::IFile* file = collection->FindFile(kProtoResourceTablePath);
if (!file) {
return {};
}
@@ -1201,11 +1251,7 @@ class LinkCommand {
// Clear the package name, so as to make the resources look like they are coming from the
// local package.
pkg->name = "";
- if (override) {
- result = table_merger_->MergeOverlay(Source(input), table.get(), collection.get());
- } else {
- result = table_merger_->Merge(Source(input), table.get(), collection.get());
- }
+ result = table_merger_->Merge(Source(input), table.get(), override, collection.get());
} else {
// This is the proper way to merge libraries, where the package name is
@@ -1241,49 +1287,34 @@ class LinkCommand {
return false;
}
- bool result = false;
- if (override) {
- result = table_merger_->MergeOverlay(file->GetSource(), table.get());
- } else {
- result = table_merger_->Merge(file->GetSource(), table.get());
- }
- return result;
+ return table_merger_->Merge(file->GetSource(), table.get(), override);
}
- bool MergeCompiledFile(io::IFile* file, ResourceFile* file_desc, bool override) {
+ bool MergeCompiledFile(const ResourceFile& compiled_file, io::IFile* file, bool override) {
if (context_->IsVerbose()) {
- context_->GetDiagnostics()->Note(DiagMessage() << "merging '" << file_desc->name
- << "' from compiled file "
- << file->GetSource());
- }
-
- bool result = false;
- if (override) {
- result = table_merger_->MergeFileOverlay(*file_desc, file);
- } else {
- result = table_merger_->MergeFile(*file_desc, file);
+ context_->GetDiagnostics()->Note(DiagMessage()
+ << "merging '" << compiled_file.name
+ << "' from compiled file " << compiled_file.source);
}
- if (!result) {
+ if (!table_merger_->MergeFile(compiled_file, override, file)) {
return false;
}
// Add the exports of this file to the table.
- for (SourcedResourceName& exported_symbol : file_desc->exported_symbols) {
- if (exported_symbol.name.package.empty()) {
- exported_symbol.name.package = context_->GetCompilationPackage();
+ for (const SourcedResourceName& exported_symbol : compiled_file.exported_symbols) {
+ ResourceName res_name = exported_symbol.name;
+ if (res_name.package.empty()) {
+ res_name.package = context_->GetCompilationPackage();
}
- ResourceNameRef res_name = exported_symbol.name;
-
- Maybe<ResourceName> mangled_name =
- context_->GetNameMangler()->MangleName(exported_symbol.name);
+ Maybe<ResourceName> mangled_name = context_->GetNameMangler()->MangleName(res_name);
if (mangled_name) {
res_name = mangled_name.value();
}
std::unique_ptr<Id> id = util::make_unique<Id>();
- id->SetSource(file_desc->source.WithLine(exported_symbol.line));
+ id->SetSource(compiled_file.source.WithLine(exported_symbol.line));
bool result = final_table_.AddResourceAllowMangled(
res_name, ConfigDescription::DefaultConfig(), std::string(), std::move(id),
context_->GetDiagnostics());
@@ -1294,15 +1325,11 @@ class LinkCommand {
return true;
}
- /**
- * Takes a path to load as a ZIP file and merges the files within into the
- * master ResourceTable.
- * If override is true, conflicting resources are allowed to override each
- * other, in order of last seen.
- *
- * An io::IFileCollection is created from the ZIP file and added to the set of
- * io::IFileCollections that are open.
- */
+ // Takes a path to load as a ZIP file and merges the files within into the master ResourceTable.
+ // If override is true, conflicting resources are allowed to override each other, in order of last
+ // seen.
+ // An io::IFileCollection is created from the ZIP file and added to the set of
+ // io::IFileCollections that are open.
bool MergeArchive(const std::string& input, bool override) {
if (context_->IsVerbose()) {
context_->GetDiagnostics()->Note(DiagMessage() << "merging archive " << input);
@@ -1328,18 +1355,11 @@ class LinkCommand {
return !error;
}
- /**
- * Takes a path to load and merge into the master ResourceTable. If override
- * is true,
- * conflicting resources are allowed to override each other, in order of last
- * seen.
- *
- * If the file path ends with .flata, .jar, .jack, or .zip the file is treated
- * as ZIP archive
- * and the files within are merged individually.
- *
- * Otherwise the files is processed on its own.
- */
+ // Takes a path to load and merge into the master ResourceTable. If override is true,
+ // conflicting resources are allowed to override each other, in order of last seen.
+ // If the file path ends with .flata, .jar, .jack, or .zip the file is treated
+ // as ZIP archive and the files within are merged individually.
+ // Otherwise the file is processed on its own.
bool MergePath(const std::string& path, bool override) {
if (util::EndsWith(path, ".flata") || util::EndsWith(path, ".jar") ||
util::EndsWith(path, ".jack") || util::EndsWith(path, ".zip")) {
@@ -1352,82 +1372,87 @@ class LinkCommand {
return MergeFile(file, override);
}
- /**
- * Takes a file to load and merge into the master ResourceTable. If override
- * is true,
- * conflicting resources are allowed to override each other, in order of last
- * seen.
- *
- * If the file ends with .arsc.flat, then it is loaded as a ResourceTable and
- * merged into the
- * master ResourceTable. If the file ends with .flat, then it is treated like
- * a compiled file
- * and the header data is read and merged into the final ResourceTable.
- *
- * All other file types are ignored. This is because these files could be
- * coming from a zip,
- * where we could have other files like classes.dex.
- */
+ // Takes an AAPT Container file (.apc/.flat) to load and merge into the master ResourceTable.
+ // If override is true, conflicting resources are allowed to override each other, in order of last
+ // seen.
+ // All other file types are ignored. This is because these files could be coming from a zip,
+ // where we could have other files like classes.dex.
bool MergeFile(io::IFile* file, bool override) {
const Source& src = file->GetSource();
- if (util::EndsWith(src.path, ".arsc.flat")) {
- return MergeResourceTable(file, override);
-
- } else if (util::EndsWith(src.path, ".flat")) {
- // Try opening the file and looking for an Export header.
- std::unique_ptr<io::IData> data = file->OpenAsData();
- if (!data) {
- context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to open");
- return false;
- }
- CompiledFileInputStream input_stream(data->data(), data->size());
- uint32_t num_files = 0;
- if (!input_stream.ReadLittleEndian32(&num_files)) {
- context_->GetDiagnostics()->Error(DiagMessage(src) << "failed read num files");
- return false;
+ if (util::EndsWith(src.path, ".xml") || util::EndsWith(src.path, ".png")) {
+ // Since AAPT compiles these file types and appends .flat to them, seeing
+ // their raw extensions is a sign that they weren't compiled.
+ const StringPiece file_type = util::EndsWith(src.path, ".xml") ? "XML" : "PNG";
+ context_->GetDiagnostics()->Error(DiagMessage(src) << "uncompiled " << file_type
+ << " file passed as argument. Must be "
+ "compiled first into .flat file.");
+ return false;
+ } else if (!util::EndsWith(src.path, ".apc") && !util::EndsWith(src.path, ".flat")) {
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring unrecognized file");
+ return true;
}
+ }
+
+ std::unique_ptr<io::InputStream> input_stream = file->OpenInputStream();
+ if (input_stream == nullptr) {
+ context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to open file");
+ return false;
+ }
+
+ if (input_stream->HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "failed to open file: " << input_stream->GetError());
+ return false;
+ }
+
+ ContainerReaderEntry* entry;
+ ContainerReader reader(input_stream.get());
+ while ((entry = reader.Next()) != nullptr) {
+ if (entry->Type() == ContainerEntryType::kResTable) {
+ pb::ResourceTable pb_table;
+ if (!entry->GetResTable(&pb_table)) {
+ context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to read resource table: "
+ << entry->GetError());
+ return false;
+ }
- for (uint32_t i = 0; i < num_files; i++) {
- pb::internal::CompiledFile compiled_file;
- if (!input_stream.ReadCompiledFile(&compiled_file)) {
+ ResourceTable table;
+ std::string error;
+ if (!DeserializeTableFromPb(pb_table, &table, &error)) {
context_->GetDiagnostics()->Error(DiagMessage(src)
- << "failed to read compiled file header");
+ << "failed to deserialize resource table: " << error);
return false;
}
- uint64_t offset, len;
- if (!input_stream.ReadDataMetaData(&offset, &len)) {
- context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to read data meta data");
+ if (!table_merger_->Merge(src, &table, override)) {
+ context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to merge resource table");
+ return false;
+ }
+ } else if (entry->Type() == ContainerEntryType::kResFile) {
+ pb::internal::CompiledFile pb_compiled_file;
+ off64_t offset;
+ size_t len;
+ if (!entry->GetResFileOffsets(&pb_compiled_file, &offset, &len)) {
+ context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to get resource file: "
+ << entry->GetError());
return false;
}
ResourceFile resource_file;
std::string error;
- if (!DeserializeCompiledFileFromPb(compiled_file, &resource_file, &error)) {
+ if (!DeserializeCompiledFileFromPb(pb_compiled_file, &resource_file, &error)) {
context_->GetDiagnostics()->Error(DiagMessage(src)
<< "failed to read compiled header: " << error);
return false;
}
- if (!MergeCompiledFile(file->CreateFileSegment(offset, len), &resource_file, override)) {
+ if (!MergeCompiledFile(resource_file, file->CreateFileSegment(offset, len), override)) {
return false;
}
}
- return true;
- } else if (util::EndsWith(src.path, ".xml") || util::EndsWith(src.path, ".png")) {
- // Since AAPT compiles these file types and appends .flat to them, seeing
- // their raw extensions is a sign that they weren't compiled.
- const StringPiece file_type = util::EndsWith(src.path, ".xml") ? "XML" : "PNG";
- context_->GetDiagnostics()->Error(DiagMessage(src) << "uncompiled " << file_type
- << " file passed as argument. Must be "
- "compiled first into .flat file.");
- return false;
}
-
- // Ignore non .flat files. This could be classes.dex or something else that
- // happens
- // to be in an archive.
return true;
}
@@ -1471,15 +1496,13 @@ class LinkCommand {
return true;
}
- /**
- * Writes the AndroidManifest, ResourceTable, and all XML files referenced by
- * the ResourceTable to the IArchiveWriter.
- */
+ // Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable
+ // to the IArchiveWriter.
bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
ResourceTable* table) {
const bool keep_raw_values = context_->GetPackageType() == PackageType::kStaticLib;
- bool result = FlattenXml(context_, manifest, "AndroidManifest.xml", keep_raw_values,
- true /*utf16*/, writer);
+ bool result = FlattenXml(context_, *manifest, "AndroidManifest.xml", keep_raw_values,
+ true /*utf16*/, options_.output_format, writer);
if (!result) {
return false;
}
@@ -1494,6 +1517,7 @@ class LinkCommand {
file_flattener_options.no_xml_namespaces = options_.no_xml_namespaces;
file_flattener_options.update_proguard_spec =
static_cast<bool>(options_.generate_proguard_rules_path);
+ file_flattener_options.output_format = options_.output_format;
ResourceFileFlattener file_flattener(file_flattener_options, context_, keep_set);
@@ -1502,15 +1526,9 @@ class LinkCommand {
return false;
}
- if (context_->GetPackageType() == PackageType::kStaticLib) {
- if (!FlattenTableToPb(table, writer)) {
- return false;
- }
- } else {
- if (!FlattenTable(table, writer)) {
- context_->GetDiagnostics()->Error(DiagMessage() << "failed to write resources.arsc");
- return false;
- }
+ if (!FlattenTable(table, options_.output_format, writer)) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to write resource table");
+ return false;
}
return true;
}
@@ -1874,6 +1892,7 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
bool verbose = false;
bool shared_lib = false;
bool static_lib = false;
+ bool proto_format = false;
Maybe<std::string> stable_id_file_path;
std::vector<std::string> split_args;
Flags flags =
@@ -1954,6 +1973,10 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
.OptionalSwitch("--shared-lib", "Generates a shared Android runtime library.",
&shared_lib)
.OptionalSwitch("--static-lib", "Generate a static Android library.", &static_lib)
+ .OptionalSwitch("--proto-format",
+ "Generates compiled resources in Protobuf format.\n"
+ "Suitable as input to the bundle tool for generating an App Bundle.",
+ &proto_format)
.OptionalSwitch("--no-static-lib-packages",
"Merge all library resources under the app's package.",
&options.no_static_lib_packages)
@@ -2040,21 +2063,25 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
context.SetVerbose(verbose);
}
- if (shared_lib && static_lib) {
- context.GetDiagnostics()->Error(DiagMessage()
- << "only one of --shared-lib and --static-lib can be defined");
+ if (int{shared_lib} + int{static_lib} + int{proto_format} > 1) {
+ context.GetDiagnostics()->Error(
+ DiagMessage()
+ << "only one of --shared-lib, --static-lib, or --proto_format can be defined");
return 1;
}
+ // The default build type.
+ context.SetPackageType(PackageType::kApp);
+ context.SetPackageId(kAppPackageId);
+
if (shared_lib) {
context.SetPackageType(PackageType::kSharedLib);
context.SetPackageId(0x00);
} else if (static_lib) {
context.SetPackageType(PackageType::kStaticLib);
- context.SetPackageId(kAppPackageId);
- } else {
- context.SetPackageType(PackageType::kApp);
- context.SetPackageId(kAppPackageId);
+ options.output_format = OutputFormat::kProto;
+ } else if (proto_format) {
+ options.output_format = OutputFormat::kProto;
}
if (package_id) {
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 67ac67a9367e..44e148ee9732 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -33,7 +33,7 @@
#include "filter/AbiFilter.h"
#include "format/binary/TableFlattener.h"
#include "format/binary/XmlFlattener.h"
-#include "io/BigBufferInputStream.h"
+#include "io/BigBufferStream.h"
#include "io/Util.h"
#include "optimize/MultiApkGenerator.h"
#include "optimize/ResourceDeduper.h"
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index a79a577c663b..b99240f0a40a 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -30,7 +30,7 @@
#include "ResourceUtils.h"
#include "io/File.h"
#include "io/FileSystem.h"
-#include "io/StringInputStream.h"
+#include "io/StringStream.h"
#include "util/Files.h"
#include "util/Maybe.h"
#include "util/Util.h"
diff --git a/tools/aapt2/format/Container.cpp b/tools/aapt2/format/Container.cpp
new file mode 100644
index 000000000000..739555c5b15d
--- /dev/null
+++ b/tools/aapt2/format/Container.cpp
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2017 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 "format/Container.h"
+
+#include "android-base/scopeguard.h"
+#include "android-base/stringprintf.h"
+
+using ::android::base::StringPrintf;
+using ::google::protobuf::io::CodedInputStream;
+using ::google::protobuf::io::CodedOutputStream;
+using ::google::protobuf::io::ZeroCopyOutputStream;
+
+namespace aapt {
+
+constexpr const static uint32_t kContainerFormatMagic = 0x54504141u;
+constexpr const static uint32_t kContainerFormatVersion = 1u;
+
+ContainerWriter::ContainerWriter(ZeroCopyOutputStream* out, size_t entry_count)
+ : out_(out), total_entry_count_(entry_count), current_entry_count_(0u) {
+ CodedOutputStream coded_out(out_);
+
+ // Write the magic.
+ coded_out.WriteLittleEndian32(kContainerFormatMagic);
+
+ // Write the version.
+ coded_out.WriteLittleEndian32(kContainerFormatVersion);
+
+ // Write the total number of entries.
+ coded_out.WriteLittleEndian32(static_cast<uint32_t>(total_entry_count_));
+
+ if (coded_out.HadError()) {
+ error_ = "failed writing container format header";
+ }
+}
+
+inline static void WritePadding(int padding, CodedOutputStream* out) {
+ if (padding < 4) {
+ const uint32_t zero = 0u;
+ out->WriteRaw(&zero, padding);
+ }
+}
+
+bool ContainerWriter::AddResTableEntry(const pb::ResourceTable& table) {
+ if (current_entry_count_ >= total_entry_count_) {
+ error_ = "too many entries being serialized";
+ return false;
+ }
+ current_entry_count_++;
+
+ CodedOutputStream coded_out(out_);
+
+ // Write the type.
+ coded_out.WriteLittleEndian32(kResTable);
+
+ // Write the aligned size.
+ const ::google::protobuf::uint64 size = table.ByteSize();
+ const int padding = 4 - (size % 4);
+ coded_out.WriteLittleEndian64(size);
+
+ // Write the table.
+ table.SerializeWithCachedSizes(&coded_out);
+
+ // Write the padding.
+ WritePadding(padding, &coded_out);
+
+ if (coded_out.HadError()) {
+ error_ = "failed writing to output";
+ return false;
+ }
+ return true;
+}
+
+bool ContainerWriter::AddResFileEntry(const pb::internal::CompiledFile& file,
+ io::KnownSizeInputStream* in) {
+ if (current_entry_count_ >= total_entry_count_) {
+ error_ = "too many entries being serialized";
+ return false;
+ }
+ current_entry_count_++;
+
+ constexpr const static int kResFileEntryHeaderSize = 12;
+
+ CodedOutputStream coded_out(out_);
+
+ // Write the type.
+ coded_out.WriteLittleEndian32(kResFile);
+
+ // Write the aligned size.
+ const ::google::protobuf::uint32 header_size = file.ByteSize();
+ const int header_padding = 4 - (header_size % 4);
+ const ::google::protobuf::uint64 data_size = in->TotalSize();
+ const int data_padding = 4 - (data_size % 4);
+ coded_out.WriteLittleEndian64(kResFileEntryHeaderSize + header_size + header_padding + data_size +
+ data_padding);
+
+ // Write the res file header size.
+ coded_out.WriteLittleEndian32(header_size);
+
+ // Write the data payload size.
+ coded_out.WriteLittleEndian64(data_size);
+
+ // Write the header.
+ file.SerializeToCodedStream(&coded_out);
+
+ WritePadding(header_padding, &coded_out);
+
+ // Write the data payload. We need to call Trim() since we are going to write to the underlying
+ // ZeroCopyOutputStream.
+ coded_out.Trim();
+
+ // Check at this point if there were any errors.
+ if (coded_out.HadError()) {
+ error_ = "failed writing to output";
+ return false;
+ }
+
+ if (!io::Copy(out_, in)) {
+ if (in->HadError()) {
+ std::ostringstream error;
+ error << "failed reading from input: " << in->GetError();
+ error_ = error.str();
+ } else {
+ error_ = "failed writing to output";
+ }
+ return false;
+ }
+ WritePadding(data_padding, &coded_out);
+
+ if (coded_out.HadError()) {
+ error_ = "failed writing to output";
+ return false;
+ }
+ return true;
+}
+
+bool ContainerWriter::HadError() const {
+ return !error_.empty();
+}
+
+std::string ContainerWriter::GetError() const {
+ return error_;
+}
+
+static bool AlignRead(CodedInputStream* in) {
+ const int padding = 4 - (in->CurrentPosition() % 4);
+ if (padding < 4) {
+ return in->Skip(padding);
+ }
+ return true;
+}
+
+ContainerReaderEntry::ContainerReaderEntry(ContainerReader* reader) : reader_(reader) {
+}
+
+ContainerEntryType ContainerReaderEntry::Type() const {
+ return type_;
+}
+
+bool ContainerReaderEntry::GetResTable(pb::ResourceTable* out_table) {
+ CHECK(type_ == ContainerEntryType::kResTable) << "reading a kResTable when the type is kResFile";
+ if (length_ > std::numeric_limits<int>::max()) {
+ reader_->error_ = StringPrintf("entry length %zu is too large", length_);
+ return false;
+ }
+
+ CodedInputStream& coded_in = reader_->coded_in_;
+
+ const CodedInputStream::Limit limit = coded_in.PushLimit(static_cast<int>(length_));
+ auto guard = ::android::base::make_scope_guard([&]() { coded_in.PopLimit(limit); });
+
+ if (!out_table->ParseFromCodedStream(&coded_in)) {
+ reader_->error_ = "failed to parse ResourceTable";
+ return false;
+ }
+ return true;
+}
+
+bool ContainerReaderEntry::GetResFileOffsets(pb::internal::CompiledFile* out_file,
+ off64_t* out_offset, size_t* out_len) {
+ CHECK(type_ == ContainerEntryType::kResFile) << "reading a kResFile when the type is kResTable";
+
+ CodedInputStream& coded_in = reader_->coded_in_;
+
+ // Read the ResFile header.
+ ::google::protobuf::uint32 header_length;
+ if (!coded_in.ReadLittleEndian32(&header_length)) {
+ std::ostringstream error;
+ error << "failed to read header length from input: " << reader_->in_->GetError();
+ reader_->error_ = error.str();
+ return false;
+ }
+
+ ::google::protobuf::uint64 data_length;
+ if (!coded_in.ReadLittleEndian64(&data_length)) {
+ std::ostringstream error;
+ error << "failed to read data length from input: " << reader_->in_->GetError();
+ reader_->error_ = error.str();
+ return false;
+ }
+
+ if (header_length > std::numeric_limits<int>::max()) {
+ std::ostringstream error;
+ error << "header length " << header_length << " is too large";
+ reader_->error_ = error.str();
+ return false;
+ }
+
+ if (data_length > std::numeric_limits<size_t>::max()) {
+ std::ostringstream error;
+ error << "data length " << data_length << " is too large";
+ reader_->error_ = error.str();
+ return false;
+ }
+
+ {
+ const CodedInputStream::Limit limit = coded_in.PushLimit(static_cast<int>(header_length));
+ auto guard = ::android::base::make_scope_guard([&]() { coded_in.PopLimit(limit); });
+
+ if (!out_file->ParseFromCodedStream(&coded_in)) {
+ reader_->error_ = "failed to parse CompiledFile header";
+ return false;
+ }
+ }
+
+ AlignRead(&coded_in);
+
+ *out_offset = coded_in.CurrentPosition();
+ *out_len = data_length;
+
+ coded_in.Skip(static_cast<int>(data_length));
+ AlignRead(&coded_in);
+ return true;
+}
+
+bool ContainerReaderEntry::HadError() const {
+ return reader_->HadError();
+}
+
+std::string ContainerReaderEntry::GetError() const {
+ return reader_->GetError();
+}
+
+ContainerReader::ContainerReader(io::InputStream* in)
+ : in_(in),
+ adaptor_(in),
+ coded_in_(&adaptor_),
+ total_entry_count_(0u),
+ current_entry_count_(0u),
+ entry_(this) {
+ ::google::protobuf::uint32 magic;
+ if (!coded_in_.ReadLittleEndian32(&magic)) {
+ std::ostringstream error;
+ error << "failed to read magic from input: " << in_->GetError();
+ error_ = error.str();
+ return;
+ }
+
+ if (magic != kContainerFormatMagic) {
+ error_ = "magic value doesn't match AAPT";
+ return;
+ }
+
+ ::google::protobuf::uint32 version;
+ if (!coded_in_.ReadLittleEndian32(&version)) {
+ std::ostringstream error;
+ error << "failed to read version from input: " << in_->GetError();
+ error_ = error.str();
+ return;
+ }
+
+ if (version != kContainerFormatVersion) {
+ error_ = StringPrintf("container version is 0x%08x but AAPT expects version 0x%08x", version,
+ kContainerFormatVersion);
+ return;
+ }
+
+ ::google::protobuf::uint32 total_entry_count;
+ if (!coded_in_.ReadLittleEndian32(&total_entry_count)) {
+ std::ostringstream error;
+ error << "failed to read entry count from input: " << in_->GetError();
+ error_ = error.str();
+ return;
+ }
+
+ total_entry_count_ = total_entry_count;
+}
+
+ContainerReaderEntry* ContainerReader::Next() {
+ if (current_entry_count_ >= total_entry_count_) {
+ return nullptr;
+ }
+ current_entry_count_++;
+
+ // Ensure the next read is aligned.
+ AlignRead(&coded_in_);
+
+ ::google::protobuf::uint32 entry_type;
+ if (!coded_in_.ReadLittleEndian32(&entry_type)) {
+ std::ostringstream error;
+ error << "failed reading entry type from input: " << in_->GetError();
+ error_ = error.str();
+ return nullptr;
+ }
+
+ ::google::protobuf::uint64 entry_length;
+ if (!coded_in_.ReadLittleEndian64(&entry_length)) {
+ std::ostringstream error;
+ error << "failed reading entry length from input: " << in_->GetError();
+ error_ = error.str();
+ return nullptr;
+ }
+
+ if (entry_type == ContainerEntryType::kResFile || entry_type == ContainerEntryType::kResTable) {
+ entry_.type_ = static_cast<ContainerEntryType>(entry_type);
+ } else {
+ error_ = StringPrintf("entry type 0x%08x is invalid", entry_type);
+ return nullptr;
+ }
+
+ if (entry_length > std::numeric_limits<size_t>::max()) {
+ std::ostringstream error;
+ error << "entry length " << entry_length << " is too large";
+ error_ = error.str();
+ return nullptr;
+ }
+
+ entry_.length_ = entry_length;
+ return &entry_;
+}
+
+bool ContainerReader::HadError() const {
+ return !error_.empty();
+}
+
+std::string ContainerReader::GetError() const {
+ return error_;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/format/Container.h b/tools/aapt2/format/Container.h
new file mode 100644
index 000000000000..aa5c82cd322c
--- /dev/null
+++ b/tools/aapt2/format/Container.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 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 AAPT_FORMAT_CONTAINER_H
+#define AAPT_FORMAT_CONTAINER_H
+
+#include <inttypes.h>
+
+#include "google/protobuf/io/coded_stream.h"
+#include "google/protobuf/io/zero_copy_stream.h"
+
+#include "Resources.pb.h"
+#include "ResourcesInternal.pb.h"
+#include "io/Io.h"
+#include "io/Util.h"
+#include "util/BigBuffer.h"
+
+namespace aapt {
+
+enum ContainerEntryType : uint8_t {
+ kResTable = 0x00u,
+ kResFile = 0x01u,
+};
+
+class ContainerWriter {
+ public:
+ explicit ContainerWriter(::google::protobuf::io::ZeroCopyOutputStream* out, size_t entry_count);
+
+ bool AddResTableEntry(const pb::ResourceTable& table);
+ bool AddResFileEntry(const pb::internal::CompiledFile& file, io::KnownSizeInputStream* in);
+ bool HadError() const;
+ std::string GetError() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ContainerWriter);
+
+ ::google::protobuf::io::ZeroCopyOutputStream* out_;
+ size_t total_entry_count_;
+ size_t current_entry_count_;
+ std::string error_;
+};
+
+class ContainerReader;
+
+class ContainerReaderEntry {
+ public:
+ ContainerEntryType Type() const;
+
+ bool GetResTable(pb::ResourceTable* out_table);
+ bool GetResFileOffsets(pb::internal::CompiledFile* out_file, off64_t* out_offset,
+ size_t* out_len);
+
+ bool HadError() const;
+ std::string GetError() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ContainerReaderEntry);
+
+ friend class ContainerReader;
+
+ explicit ContainerReaderEntry(ContainerReader* reader);
+
+ ContainerReader* reader_;
+ ContainerEntryType type_ = ContainerEntryType::kResTable;
+ size_t length_ = 0u;
+};
+
+class ContainerReader {
+ public:
+ explicit ContainerReader(io::InputStream* in);
+
+ ContainerReaderEntry* Next();
+
+ bool HadError() const;
+ std::string GetError() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ContainerReader);
+
+ friend class ContainerReaderEntry;
+
+ io::InputStream* in_;
+ io::ZeroCopyInputAdaptor adaptor_;
+ ::google::protobuf::io::CodedInputStream coded_in_;
+ size_t total_entry_count_;
+ size_t current_entry_count_;
+ ContainerReaderEntry entry_;
+ std::string error_;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_FORMAT_CONTAINER_H */
diff --git a/tools/aapt2/format/Container_test.cpp b/tools/aapt2/format/Container_test.cpp
new file mode 100644
index 000000000000..dc81a3ae2219
--- /dev/null
+++ b/tools/aapt2/format/Container_test.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 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 "format/Container.h"
+
+#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
+
+#include "io/StringStream.h"
+#include "test/Test.h"
+
+using ::google::protobuf::io::StringOutputStream;
+using ::testing::Eq;
+using ::testing::IsEmpty;
+using ::testing::IsNull;
+using ::testing::NotNull;
+using ::testing::StrEq;
+
+namespace aapt {
+
+TEST(ContainerTest, SerializeCompiledFile) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ const std::string expected_data = "123";
+
+ std::string output_str;
+ {
+ StringOutputStream out_stream(&output_str);
+ ContainerWriter writer(&out_stream, 2u);
+ ASSERT_FALSE(writer.HadError());
+
+ pb::internal::CompiledFile pb_compiled_file;
+ pb_compiled_file.set_resource_name("android:layout/main.xml");
+ pb_compiled_file.set_type(pb::FileReference::PROTO_XML);
+ pb_compiled_file.set_source_path("res/layout/main.xml");
+ io::StringInputStream data(expected_data);
+ ASSERT_TRUE(writer.AddResFileEntry(pb_compiled_file, &data));
+
+ pb::ResourceTable pb_table;
+ pb::Package* pb_pkg = pb_table.add_package();
+ pb_pkg->set_package_name("android");
+ pb_pkg->mutable_package_id()->set_id(0x01u);
+ ASSERT_TRUE(writer.AddResTableEntry(pb_table));
+
+ ASSERT_FALSE(writer.HadError());
+ }
+
+ io::StringInputStream input(output_str);
+ ContainerReader reader(&input);
+ ASSERT_FALSE(reader.HadError());
+
+ ContainerReaderEntry* entry = reader.Next();
+ ASSERT_THAT(entry, NotNull());
+ ASSERT_THAT(entry->Type(), Eq(ContainerEntryType::kResFile));
+
+ pb::internal::CompiledFile pb_new_file;
+ off64_t offset;
+ size_t len;
+ ASSERT_TRUE(entry->GetResFileOffsets(&pb_new_file, &offset, &len)) << entry->GetError();
+ EXPECT_THAT(offset & 0x03, Eq(0u));
+ EXPECT_THAT(output_str.substr(static_cast<size_t>(offset), len), StrEq(expected_data));
+
+ entry = reader.Next();
+ ASSERT_THAT(entry, NotNull());
+ ASSERT_THAT(entry->Type(), Eq(ContainerEntryType::kResTable));
+
+ pb::ResourceTable pb_new_table;
+ ASSERT_TRUE(entry->GetResTable(&pb_new_table));
+ ASSERT_THAT(pb_new_table.package_size(), Eq(1));
+ EXPECT_THAT(pb_new_table.package(0).package_name(), StrEq("android"));
+ EXPECT_THAT(pb_new_table.package(0).package_id().id(), Eq(0x01u));
+
+ EXPECT_THAT(reader.Next(), IsNull());
+ EXPECT_FALSE(reader.HadError());
+ EXPECT_THAT(reader.GetError(), IsEmpty());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/format/binary/XmlFlattener.cpp b/tools/aapt2/format/binary/XmlFlattener.cpp
index 2456c3dfd5e9..345cc95cfb29 100644
--- a/tools/aapt2/format/binary/XmlFlattener.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener.cpp
@@ -56,9 +56,9 @@ static bool cmp_xml_attribute_by_id(const xml::Attribute* a, const xml::Attribut
return false;
}
-class XmlFlattenerVisitor : public xml::Visitor {
+class XmlFlattenerVisitor : public xml::ConstVisitor {
public:
- using xml::Visitor::Visit;
+ using xml::ConstVisitor::Visit;
StringPool pool;
std::map<uint8_t, StringPool> package_pools;
@@ -74,7 +74,7 @@ class XmlFlattenerVisitor : public xml::Visitor {
: buffer_(buffer), options_(options) {
}
- void Visit(xml::Text* node) override {
+ void Visit(const xml::Text* node) override {
if (util::TrimWhitespace(node->text).empty()) {
// Skip whitespace only text nodes.
return;
@@ -95,7 +95,7 @@ class XmlFlattenerVisitor : public xml::Visitor {
writer.Finish();
}
- void Visit(xml::Element* node) override {
+ void Visit(const xml::Element* node) override {
for (const xml::NamespaceDecl& decl : node->namespace_decls) {
// Skip dedicated tools namespace.
if (decl.uri != xml::kSchemaTools) {
@@ -125,7 +125,7 @@ class XmlFlattenerVisitor : public xml::Visitor {
start_writer.Finish();
}
- xml::Visitor::Visit(node);
+ xml::ConstVisitor::Visit(node);
{
ChunkWriter end_writer(buffer_);
@@ -182,12 +182,13 @@ class XmlFlattenerVisitor : public xml::Visitor {
writer.Finish();
}
- void WriteAttributes(xml::Element* node, ResXMLTree_attrExt* flat_elem, ChunkWriter* writer) {
+ void WriteAttributes(const xml::Element* node, ResXMLTree_attrExt* flat_elem,
+ ChunkWriter* writer) {
filtered_attrs_.clear();
filtered_attrs_.reserve(node->attributes.size());
// Filter the attributes.
- for (xml::Attribute& attr : node->attributes) {
+ for (const xml::Attribute& attr : node->attributes) {
if (attr.namespace_uri != xml::kSchemaTools) {
filtered_attrs_.push_back(&attr);
}
@@ -282,12 +283,12 @@ class XmlFlattenerVisitor : public xml::Visitor {
XmlFlattenerOptions options_;
// Scratch vector to filter attributes. We avoid allocations making this a member.
- std::vector<xml::Attribute*> filtered_attrs_;
+ std::vector<const xml::Attribute*> filtered_attrs_;
};
} // namespace
-bool XmlFlattener::Flatten(IAaptContext* context, xml::Node* node) {
+bool XmlFlattener::Flatten(IAaptContext* context, const xml::Node* node) {
BigBuffer node_buffer(1024);
XmlFlattenerVisitor visitor(&node_buffer, options_);
node->Accept(&visitor);
@@ -341,7 +342,7 @@ bool XmlFlattener::Flatten(IAaptContext* context, xml::Node* node) {
return true;
}
-bool XmlFlattener::Consume(IAaptContext* context, xml::XmlResource* resource) {
+bool XmlFlattener::Consume(IAaptContext* context, const xml::XmlResource* resource) {
if (!resource->root) {
return false;
}
diff --git a/tools/aapt2/format/binary/XmlFlattener.h b/tools/aapt2/format/binary/XmlFlattener.h
index 8db2281cd74a..1f9e777f7a1a 100644
--- a/tools/aapt2/format/binary/XmlFlattener.h
+++ b/tools/aapt2/format/binary/XmlFlattener.h
@@ -34,18 +34,18 @@ struct XmlFlattenerOptions {
bool use_utf16 = false;
};
-class XmlFlattener : public IXmlResourceConsumer {
+class XmlFlattener {
public:
XmlFlattener(BigBuffer* buffer, XmlFlattenerOptions options)
: buffer_(buffer), options_(options) {
}
- bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
+ bool Consume(IAaptContext* context, const xml::XmlResource* resource);
private:
DISALLOW_COPY_AND_ASSIGN(XmlFlattener);
- bool Flatten(IAaptContext* context, xml::Node* node);
+ bool Flatten(IAaptContext* context, const xml::Node* node);
BigBuffer* buffer_;
XmlFlattenerOptions options_;
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index c14f09a3b103..86bd86536cc4 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -26,7 +26,6 @@
#include "ValueVisitor.h"
using ::android::ResStringPool;
-using ::google::protobuf::io::CodedInputStream;
namespace aapt {
@@ -391,8 +390,15 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr
}
ResourceTableType* type = pkg->FindOrCreateType(*res_type);
+ if (pb_type.has_type_id()) {
+ type->id = static_cast<uint8_t>(pb_type.type_id().id());
+ }
+
for (const pb::Entry& pb_entry : pb_type.entry()) {
ResourceEntry* entry = type->FindOrCreateEntry(pb_entry.name());
+ if (pb_entry.has_entry_id()) {
+ entry->id = static_cast<uint16_t>(pb_entry.entry_id().id());
+ }
// Deserialize the symbol status (public/private with source and comments).
if (pb_entry.has_symbol_status()) {
@@ -406,21 +412,11 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr
const SymbolState visibility = DeserializeVisibilityFromPb(pb_status.visibility());
entry->symbol_status.state = visibility;
-
if (visibility == SymbolState::kPublic) {
- // This is a public symbol, we must encode the ID now if there is one.
- if (pb_entry.has_entry_id()) {
- entry->id = static_cast<uint16_t>(pb_entry.entry_id().id());
- }
-
- if (type->symbol_status.state != SymbolState::kPublic) {
- // If the type has not been made public, do so now.
- type->symbol_status.state = SymbolState::kPublic;
- if (pb_type.has_type_id()) {
- type->id = static_cast<uint8_t>(pb_type.type_id().id());
- }
- }
+ // Propagate the public visibility up to the Type.
+ type->symbol_status.state = SymbolState::kPublic;
} else if (visibility == SymbolState::kPrivate) {
+ // Only propagate if no previous state was assigned.
if (type->symbol_status.state == SymbolState::kUndefined) {
type->symbol_status.state = SymbolState::kPrivate;
}
@@ -485,6 +481,19 @@ bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, ResourceTable* ou
return true;
}
+static ResourceFile::Type DeserializeFileReferenceTypeFromPb(const pb::FileReference::Type& type) {
+ switch (type) {
+ case pb::FileReference::BINARY_XML:
+ return ResourceFile::Type::kBinaryXml;
+ case pb::FileReference::PROTO_XML:
+ return ResourceFile::Type::kProtoXml;
+ case pb::FileReference::PNG:
+ return ResourceFile::Type::kPng;
+ default:
+ return ResourceFile::Type::kUnknown;
+ }
+}
+
bool DeserializeCompiledFileFromPb(const pb::internal::CompiledFile& pb_file,
ResourceFile* out_file, std::string* out_error) {
ResourceNameRef name_ref;
@@ -497,6 +506,7 @@ bool DeserializeCompiledFileFromPb(const pb::internal::CompiledFile& pb_file,
out_file->name = name_ref.ToResourceName();
out_file->source.path = pb_file.source_path();
+ out_file->type = DeserializeFileReferenceTypeFromPb(pb_file.type());
std::string config_error;
if (!DeserializeConfigFromPb(pb_file.config(), &out_file->config, &config_error)) {
@@ -759,8 +769,12 @@ std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
} break;
case pb::Item::kFile: {
- return util::make_unique<FileReference>(value_pool->MakeRef(
- pb_item.file().path(), StringPool::Context(StringPool::Context::kHighPriority, config)));
+ const pb::FileReference& pb_file = pb_item.file();
+ std::unique_ptr<FileReference> file_ref =
+ util::make_unique<FileReference>(value_pool->MakeRef(
+ pb_file.path(), StringPool::Context(StringPool::Context::kHighPriority, config)));
+ file_ref->type = DeserializeFileReferenceTypeFromPb(pb_file.type());
+ return std::move(file_ref);
} break;
default:
@@ -847,72 +861,4 @@ bool DeserializeXmlFromPb(const pb::XmlNode& pb_node, xml::Element* out_el, Stri
return true;
}
-CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size)
- : in_(static_cast<const uint8_t*>(data), size) {
-}
-
-void CompiledFileInputStream::EnsureAlignedRead() {
- const int overflow = in_.CurrentPosition() % 4;
- if (overflow > 0) {
- // Reads are always 4 byte aligned.
- in_.Skip(4 - overflow);
- }
-}
-
-bool CompiledFileInputStream::ReadLittleEndian32(uint32_t* out_val) {
- EnsureAlignedRead();
- return in_.ReadLittleEndian32(out_val);
-}
-
-bool CompiledFileInputStream::ReadCompiledFile(pb::internal::CompiledFile* out_val) {
- EnsureAlignedRead();
-
- google::protobuf::uint64 pb_size = 0u;
- if (!in_.ReadLittleEndian64(&pb_size)) {
- return false;
- }
-
- CodedInputStream::Limit l = in_.PushLimit(static_cast<int>(pb_size));
-
- // Check that we haven't tried to read past the end.
- if (static_cast<uint64_t>(in_.BytesUntilLimit()) != pb_size) {
- in_.PopLimit(l);
- in_.PushLimit(0);
- return false;
- }
-
- if (!out_val->ParsePartialFromCodedStream(&in_)) {
- in_.PopLimit(l);
- in_.PushLimit(0);
- return false;
- }
-
- in_.PopLimit(l);
- return true;
-}
-
-bool CompiledFileInputStream::ReadDataMetaData(uint64_t* out_offset, uint64_t* out_len) {
- EnsureAlignedRead();
-
- google::protobuf::uint64 pb_size = 0u;
- if (!in_.ReadLittleEndian64(&pb_size)) {
- return false;
- }
-
- // Check that we aren't trying to read past the end.
- if (pb_size > static_cast<uint64_t>(in_.BytesUntilLimit())) {
- in_.PushLimit(0);
- return false;
- }
-
- uint64_t offset = static_cast<uint64_t>(in_.CurrentPosition());
- if (!in_.Skip(pb_size)) {
- return false;
- }
-
- *out_offset = offset;
- *out_len = pb_size;
- return true;
-}
-
} // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.h b/tools/aapt2/format/proto/ProtoDeserialize.h
index c8a7199eb8b5..7dc54f242d94 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.h
+++ b/tools/aapt2/format/proto/ProtoDeserialize.h
@@ -19,7 +19,6 @@
#include "android-base/macros.h"
#include "androidfw/ResourceTypes.h"
-#include "google/protobuf/io/coded_stream.h"
#include "ConfigDescription.h"
#include "Configuration.pb.h"
@@ -57,22 +56,6 @@ bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, ResourceTable* ou
bool DeserializeCompiledFileFromPb(const pb::internal::CompiledFile& pb_file,
ResourceFile* out_file, std::string* out_error);
-class CompiledFileInputStream {
- public:
- explicit CompiledFileInputStream(const void* data, size_t size);
-
- bool ReadLittleEndian32(uint32_t* outVal);
- bool ReadCompiledFile(pb::internal::CompiledFile* outVal);
- bool ReadDataMetaData(uint64_t* outOffset, uint64_t* outLen);
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CompiledFileInputStream);
-
- void EnsureAlignedRead();
-
- ::google::protobuf::io::CodedInputStream in_;
-};
-
} // namespace aapt
#endif /* AAPT_FORMAT_PROTO_PROTODESERIALIZE_H */
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index c0d36141d457..1d184fe5a8d2 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -16,14 +16,9 @@
#include "format/proto/ProtoSerialize.h"
-#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
-
#include "ValueVisitor.h"
#include "util/BigBuffer.h"
-using ::google::protobuf::io::CodedOutputStream;
-using ::google::protobuf::io::ZeroCopyOutputStream;
-
namespace aapt {
void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool) {
@@ -366,6 +361,19 @@ static pb::Plural_Arity SerializePluralEnumToPb(size_t plural_idx) {
return pb::Plural_Arity_OTHER;
}
+static pb::FileReference::Type SerializeFileReferenceTypeToPb(const ResourceFile::Type& type) {
+ switch (type) {
+ case ResourceFile::Type::kBinaryXml:
+ return pb::FileReference::BINARY_XML;
+ case ResourceFile::Type::kProtoXml:
+ return pb::FileReference::PROTO_XML;
+ case ResourceFile::Type::kPng:
+ return pb::FileReference::PNG;
+ default:
+ return pb::FileReference::UNKNOWN;
+ }
+}
+
namespace {
class ValueSerializer : public ConstValueVisitor {
@@ -400,7 +408,9 @@ class ValueSerializer : public ConstValueVisitor {
}
void Visit(const FileReference* file) override {
- out_value_->mutable_item()->mutable_file()->set_path(*file->path);
+ pb::FileReference* pb_file = out_value_->mutable_item()->mutable_file();
+ pb_file->set_path(*file->path);
+ pb_file->set_type(SerializeFileReferenceTypeToPb(file->type));
}
void Visit(const Id* /*id*/) override {
@@ -515,6 +525,7 @@ void SerializeItemToPb(const Item& item, pb::Item* out_item) {
void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file) {
out_file->set_resource_name(file.name.ToString());
out_file->set_source_path(file.source.path);
+ out_file->set_type(SerializeFileReferenceTypeToPb(file.type));
SerializeConfig(file.config, out_file->mutable_config());
for (const SourcedResourceName& exported : file.exported_symbols) {
@@ -579,44 +590,4 @@ void SerializeXmlResourceToPb(const xml::XmlResource& resource, pb::XmlNode* out
SerializeXmlToPb(*resource.root, out_node);
}
-CompiledFileOutputStream::CompiledFileOutputStream(ZeroCopyOutputStream* out) : out_(out) {
-}
-
-void CompiledFileOutputStream::EnsureAlignedWrite() {
- const int overflow = out_.ByteCount() % 4;
- if (overflow > 0) {
- uint32_t zero = 0u;
- out_.WriteRaw(&zero, 4 - overflow);
- }
-}
-
-void CompiledFileOutputStream::WriteLittleEndian32(uint32_t val) {
- EnsureAlignedWrite();
- out_.WriteLittleEndian32(val);
-}
-
-void CompiledFileOutputStream::WriteCompiledFile(const pb::internal::CompiledFile& compiled_file) {
- EnsureAlignedWrite();
- out_.WriteLittleEndian64(static_cast<uint64_t>(compiled_file.ByteSize()));
- compiled_file.SerializeWithCachedSizes(&out_);
-}
-
-void CompiledFileOutputStream::WriteData(const BigBuffer& buffer) {
- EnsureAlignedWrite();
- out_.WriteLittleEndian64(static_cast<uint64_t>(buffer.size()));
- for (const BigBuffer::Block& block : buffer) {
- out_.WriteRaw(block.buffer.get(), block.size);
- }
-}
-
-void CompiledFileOutputStream::WriteData(const void* data, size_t len) {
- EnsureAlignedWrite();
- out_.WriteLittleEndian64(static_cast<uint64_t>(len));
- out_.WriteRaw(data, len);
-}
-
-bool CompiledFileOutputStream::HadError() {
- return out_.HadError();
-}
-
} // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoSerialize.h b/tools/aapt2/format/proto/ProtoSerialize.h
index 1694b16f3277..95dd413c1713 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.h
+++ b/tools/aapt2/format/proto/ProtoSerialize.h
@@ -18,7 +18,6 @@
#define AAPT_FORMAT_PROTO_PROTOSERIALIZE_H
#include "android-base/macros.h"
-#include "google/protobuf/io/coded_stream.h"
#include "ConfigDescription.h"
#include "Configuration.pb.h"
@@ -29,14 +28,6 @@
#include "StringPool.h"
#include "xml/XmlDom.h"
-namespace google {
-namespace protobuf {
-namespace io {
-class ZeroCopyOutputStream;
-} // namespace io
-} // namespace protobuf
-} // namespace google
-
namespace aapt {
// Serializes a Value to its protobuf representation. An optional StringPool will hold the
@@ -66,24 +57,6 @@ void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table
// Serializes a ResourceFile into its protobuf representation.
void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file);
-class CompiledFileOutputStream {
- public:
- explicit CompiledFileOutputStream(::google::protobuf::io::ZeroCopyOutputStream* out);
-
- void WriteLittleEndian32(uint32_t value);
- void WriteCompiledFile(const pb::internal::CompiledFile& compiledFile);
- void WriteData(const BigBuffer& buffer);
- void WriteData(const void* data, size_t len);
- bool HadError();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CompiledFileOutputStream);
-
- void EnsureAlignedWrite();
-
- ::google::protobuf::io::CodedOutputStream out_;
-};
-
} // namespace aapt
#endif /* AAPT_FORMAT_PROTO_PROTOSERIALIZE_H */
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 2154d5a9ab2c..8efac8ae6ffd 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -16,14 +16,11 @@
#include "format/proto/ProtoSerialize.h"
-#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
-
#include "ResourceUtils.h"
#include "format/proto/ProtoDeserialize.h"
#include "test/Test.h"
using ::android::StringPiece;
-using ::google::protobuf::io::StringOutputStream;
using ::testing::Eq;
using ::testing::IsEmpty;
using ::testing::NotNull;
@@ -137,113 +134,6 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) {
EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u));
}
-TEST(ProtoSerializeTest, SerializeFileHeader) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-
- ResourceFile f;
- f.config = test::ParseConfigOrDie("hdpi-v9");
- f.name = test::ParseNameOrDie("com.app.a:layout/main");
- f.source.path = "res/layout-hdpi-v9/main.xml";
- f.exported_symbols.push_back(SourcedResourceName{test::ParseNameOrDie("id/unchecked"), 23u});
-
- const std::string expected_data1 = "123";
- const std::string expected_data2 = "1234";
-
- std::string output_str;
- {
- pb::internal::CompiledFile pb_f1, pb_f2;
- SerializeCompiledFileToPb(f, &pb_f1);
-
- f.name.entry = "__" + f.name.entry + "$0";
- SerializeCompiledFileToPb(f, &pb_f2);
-
- StringOutputStream out_stream(&output_str);
- CompiledFileOutputStream out_file_stream(&out_stream);
- out_file_stream.WriteLittleEndian32(2);
- out_file_stream.WriteCompiledFile(pb_f1);
- out_file_stream.WriteData(expected_data1.data(), expected_data1.size());
- out_file_stream.WriteCompiledFile(pb_f2);
- out_file_stream.WriteData(expected_data2.data(), expected_data2.size());
- ASSERT_FALSE(out_file_stream.HadError());
- }
-
- CompiledFileInputStream in_file_stream(output_str.data(), output_str.size());
- uint32_t num_files = 0;
- ASSERT_TRUE(in_file_stream.ReadLittleEndian32(&num_files));
- ASSERT_EQ(2u, num_files);
-
- // Read the first compiled file.
-
- pb::internal::CompiledFile new_pb_f1;
- ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_f1));
-
- ResourceFile new_f1;
- std::string error;
- ASSERT_TRUE(DeserializeCompiledFileFromPb(new_pb_f1, &new_f1, &error));
- EXPECT_THAT(error, IsEmpty());
-
- uint64_t offset, len;
- ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len));
-
- std::string actual_data(output_str.data() + offset, len);
- EXPECT_EQ(expected_data1, actual_data);
-
- // Expect the data to be aligned.
- EXPECT_EQ(0u, offset & 0x03);
-
- ASSERT_EQ(1u, new_f1.exported_symbols.size());
- EXPECT_EQ(test::ParseNameOrDie("id/unchecked"), new_f1.exported_symbols[0].name);
-
- // Read the second compiled file.
-
- pb::internal::CompiledFile new_pb_f2;
- ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_f2));
-
- ResourceFile new_f2;
- ASSERT_TRUE(DeserializeCompiledFileFromPb(new_pb_f2, &new_f2, &error));
- EXPECT_THAT(error, IsEmpty());
-
- ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len));
-
- actual_data = std::string(output_str.data() + offset, len);
- EXPECT_EQ(expected_data2, actual_data);
-
- // Expect the data to be aligned.
- EXPECT_EQ(0u, offset & 0x03);
-}
-
-TEST(ProtoSerializeTest, DeserializeCorruptHeaderSafely) {
- ResourceFile f;
- pb::internal::CompiledFile pb_file;
- SerializeCompiledFileToPb(f, &pb_file);
-
- const std::string expected_data = "1234";
-
- std::string output_str;
- {
- StringOutputStream out_stream(&output_str);
- CompiledFileOutputStream out_file_stream(&out_stream);
- out_file_stream.WriteLittleEndian32(1);
- out_file_stream.WriteCompiledFile(pb_file);
- out_file_stream.WriteData(expected_data.data(), expected_data.size());
- ASSERT_FALSE(out_file_stream.HadError());
- }
-
- output_str[4] = 0xff;
-
- CompiledFileInputStream in_file_stream(output_str.data(), output_str.size());
-
- uint32_t num_files = 0;
- EXPECT_TRUE(in_file_stream.ReadLittleEndian32(&num_files));
- EXPECT_EQ(1u, num_files);
-
- pb::internal::CompiledFile new_pb_file;
- EXPECT_FALSE(in_file_stream.ReadCompiledFile(&new_pb_file));
-
- uint64_t offset, len;
- EXPECT_FALSE(in_file_stream.ReadDataMetaData(&offset, &len));
-}
-
TEST(ProtoSerializeTest, SerializeAndDeserializeXml) {
xml::Element element;
element.line_number = 22;
diff --git a/tools/aapt2/formats.md b/tools/aapt2/formats.md
new file mode 100644
index 000000000000..bb31a005ef42
--- /dev/null
+++ b/tools/aapt2/formats.md
@@ -0,0 +1,44 @@
+# AAPT2 On-Disk Formats
+- AAPT2 Container Format (extension `.apc`)
+- AAPT2 Static Library Format (extension `.sapk`)
+
+## AAPT2 Container Format (extension `.apc`)
+The APC format (AAPT2 Container Format) is generated by AAPT2 during the compile phase and
+consumed by the AAPT2 link phase. It is a simple container format for storing compiled PNGs,
+binary and protobuf XML, and intermediate protobuf resource tables. It also stores all associated
+meta-data from the compile phase.
+
+### Format
+The file starts with a simple header. All multi-byte fields are little-endian.
+
+| Size (in bytes) | Field | Description |
+|:----------------|:--------------|:-----------------------------------------------------|
+| `4` | `magic` | The magic bytes must equal `'AAPT'` or `0x54504141`. |
+| `4` | `version` | The version of the container format. |
+| `4` | `entry_count` | The number of entries in this container. |
+
+This is followed by `entry_count` of the following data structure. It must be aligned on a 32-bit
+boundary, so if a previous entry ends unaligned, padding must be inserted.
+
+| Size (in bytes) | Field | Description |
+|:----------------|:---------------|:----------------------------------------------------------------------------------------------------------|
+| `4` | `entry_type` | The type of the entry. This can be one of two types: `RES_TABLE (0x00000000)` or `RES_FILE (0x00000001)`. |
+| `8` | `entry_length` | The length of the data that follows. |
+| `entry_length` | `data` | The payload. The contents of this varies based on the `entry_type`. |
+
+If the `entry_type` is equal to `RES_TABLE (0x00000000)`, the `data` field contains a serialized
+[aapt.pb.ResourceTable](Resources.proto).
+
+If the `entry_type` is equal to `RES_FILE (0x00000001)`, the `data` field contains the following:
+
+
+| Size (in bytes) | Field | Description |
+|:----------------|:---------------|:----------------------------------------------------------------------------------------------------------|
+| `4` | `header_size` | The size of the `header` field. |
+| `8` | `data_size` | The size of the `data` field. |
+| `header_size` | `header` | The serialized Protobuf message [aapt.pb.internal.CompiledFile](ResourcesInternal.proto). |
+| `x` | `padding` | Up to 4 bytes of zeros, if padding is necessary to align the `data` field on a 32-bit boundary. |
+| `data_size` | `data` | The payload, which is determined by the `type` field in the `aapt.pb.internal.CompiledFile`. This can be a PNG file, binary XML, or [aapt.pb.XmlNode](Resources.proto). |
+
+## AAPT2 Static Library Format (extension `.sapk`)
+
diff --git a/tools/aapt2/io/BigBufferOutputStream.h b/tools/aapt2/io/BigBufferOutputStream.h
deleted file mode 100644
index 95113bc2132c..000000000000
--- a/tools/aapt2/io/BigBufferOutputStream.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2017 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 AAPT_IO_BIGBUFFEROUTPUTSTREAM_H
-#define AAPT_IO_BIGBUFFEROUTPUTSTREAM_H
-
-#include "io/Io.h"
-#include "util/BigBuffer.h"
-
-namespace aapt {
-namespace io {
-
-class BigBufferOutputStream : public OutputStream {
- public:
- inline explicit BigBufferOutputStream(BigBuffer* buffer) : buffer_(buffer) {}
- virtual ~BigBufferOutputStream() = default;
-
- bool Next(void** data, size_t* size) override;
-
- void BackUp(size_t count) override;
-
- size_t ByteCount() const override;
-
- bool HadError() const override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(BigBufferOutputStream);
-
- BigBuffer* buffer_;
-};
-
-} // namespace io
-} // namespace aapt
-
-#endif // AAPT_IO_BIGBUFFEROUTPUTSTREAM_H
diff --git a/tools/aapt2/io/BigBufferStreams.cpp b/tools/aapt2/io/BigBufferStream.cpp
index eb99033e1cbe..9704caae4719 100644
--- a/tools/aapt2/io/BigBufferStreams.cpp
+++ b/tools/aapt2/io/BigBufferStream.cpp
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#include "io/BigBufferInputStream.h"
-#include "io/BigBufferOutputStream.h"
+#include "io/BigBufferStream.h"
namespace aapt {
namespace io {
@@ -54,7 +53,9 @@ void BigBufferInputStream::BackUp(size_t count) {
}
}
-bool BigBufferInputStream::CanRewind() const { return true; }
+bool BigBufferInputStream::CanRewind() const {
+ return true;
+}
bool BigBufferInputStream::Rewind() {
iter_ = buffer_->begin();
@@ -63,9 +64,17 @@ bool BigBufferInputStream::Rewind() {
return true;
}
-size_t BigBufferInputStream::ByteCount() const { return bytes_read_; }
+size_t BigBufferInputStream::ByteCount() const {
+ return bytes_read_;
+}
-bool BigBufferInputStream::HadError() const { return false; }
+bool BigBufferInputStream::HadError() const {
+ return false;
+}
+
+size_t BigBufferInputStream::TotalSize() const {
+ return buffer_->size();
+}
//
// BigBufferOutputStream
@@ -76,11 +85,17 @@ bool BigBufferOutputStream::Next(void** data, size_t* size) {
return true;
}
-void BigBufferOutputStream::BackUp(size_t count) { buffer_->BackUp(count); }
+void BigBufferOutputStream::BackUp(size_t count) {
+ buffer_->BackUp(count);
+}
-size_t BigBufferOutputStream::ByteCount() const { return buffer_->size(); }
+size_t BigBufferOutputStream::ByteCount() const {
+ return buffer_->size();
+}
-bool BigBufferOutputStream::HadError() const { return false; }
+bool BigBufferOutputStream::HadError() const {
+ return false;
+}
} // namespace io
} // namespace aapt
diff --git a/tools/aapt2/io/BigBufferInputStream.h b/tools/aapt2/io/BigBufferStream.h
index 92612c744aae..8b5c8b84cd3c 100644
--- a/tools/aapt2/io/BigBufferInputStream.h
+++ b/tools/aapt2/io/BigBufferStream.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAPT_IO_BIGBUFFERINPUTSTREAM_H
-#define AAPT_IO_BIGBUFFERINPUTSTREAM_H
+#ifndef AAPT_IO_BIGBUFFERSTREAM_H
+#define AAPT_IO_BIGBUFFERSTREAM_H
#include "io/Io.h"
#include "util/BigBuffer.h"
@@ -23,10 +23,11 @@
namespace aapt {
namespace io {
-class BigBufferInputStream : public InputStream {
+class BigBufferInputStream : public KnownSizeInputStream {
public:
inline explicit BigBufferInputStream(const BigBuffer* buffer)
- : buffer_(buffer), iter_(buffer->begin()) {}
+ : buffer_(buffer), iter_(buffer->begin()) {
+ }
virtual ~BigBufferInputStream() = default;
bool Next(const void** data, size_t* size) override;
@@ -41,6 +42,8 @@ class BigBufferInputStream : public InputStream {
bool HadError() const override;
+ size_t TotalSize() const override;
+
private:
DISALLOW_COPY_AND_ASSIGN(BigBufferInputStream);
@@ -50,7 +53,27 @@ class BigBufferInputStream : public InputStream {
size_t bytes_read_ = 0;
};
+class BigBufferOutputStream : public OutputStream {
+ public:
+ inline explicit BigBufferOutputStream(BigBuffer* buffer) : buffer_(buffer) {
+ }
+ virtual ~BigBufferOutputStream() = default;
+
+ bool Next(void** data, size_t* size) override;
+
+ void BackUp(size_t count) override;
+
+ size_t ByteCount() const override;
+
+ bool HadError() const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BigBufferOutputStream);
+
+ BigBuffer* buffer_;
+};
+
} // namespace io
} // namespace aapt
-#endif // AAPT_IO_BIGBUFFERINPUTSTREAM_H
+#endif // AAPT_IO_BIGBUFFERSTREAM_H
diff --git a/tools/aapt2/io/Data.h b/tools/aapt2/io/Data.h
index 09dc7ea5b1c4..db91a77a5ae6 100644
--- a/tools/aapt2/io/Data.h
+++ b/tools/aapt2/io/Data.h
@@ -28,12 +28,16 @@ namespace aapt {
namespace io {
// Interface for a block of contiguous memory. An instance of this interface owns the data.
-class IData : public InputStream {
+class IData : public KnownSizeInputStream {
public:
virtual ~IData() = default;
virtual const void* data() const = 0;
virtual size_t size() const = 0;
+
+ virtual size_t TotalSize() const override {
+ return size();
+ }
};
class DataSegment : public IData {
diff --git a/tools/aapt2/io/File.cpp b/tools/aapt2/io/File.cpp
index ee737280ec81..b4f1ff3a5b49 100644
--- a/tools/aapt2/io/File.cpp
+++ b/tools/aapt2/io/File.cpp
@@ -39,5 +39,9 @@ std::unique_ptr<IData> FileSegment::OpenAsData() {
return {};
}
+std::unique_ptr<io::InputStream> FileSegment::OpenInputStream() {
+ return OpenAsData();
+}
+
} // namespace io
} // namespace aapt
diff --git a/tools/aapt2/io/File.h b/tools/aapt2/io/File.h
index 7ef6d88c1e3b..f06e28c79c7b 100644
--- a/tools/aapt2/io/File.h
+++ b/tools/aapt2/io/File.h
@@ -43,6 +43,8 @@ class IFile {
// Returns nullptr on failure.
virtual std::unique_ptr<IData> OpenAsData() = 0;
+ virtual std::unique_ptr<io::InputStream> OpenInputStream() = 0;
+
// Returns the source of this file. This is for presentation to the user and
// may not be a valid file system path (for example, it may contain a '@' sign to separate
// the files within a ZIP archive from the path to the containing ZIP archive.
@@ -71,8 +73,11 @@ class FileSegment : public IFile {
: file_(file), offset_(offset), len_(len) {}
std::unique_ptr<IData> OpenAsData() override;
+ std::unique_ptr<io::InputStream> OpenInputStream() override;
- const Source& GetSource() const override { return file_->GetSource(); }
+ const Source& GetSource() const override {
+ return file_->GetSource();
+ }
private:
DISALLOW_COPY_AND_ASSIGN(FileSegment);
diff --git a/tools/aapt2/io/FileInputStream.cpp b/tools/aapt2/io/FileStream.cpp
index 07dbb5a98add..2f7a4b3479ca 100644
--- a/tools/aapt2/io/FileInputStream.cpp
+++ b/tools/aapt2/io/FileStream.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "io/FileInputStream.h"
+#include "io/FileStream.h"
#include <errno.h> // for errno
#include <fcntl.h> // for O_RDONLY
@@ -66,6 +66,7 @@ bool FileInputStream::Next(const void** data, size_t* size) {
if (n < 0) {
error_ = SystemErrorCodeToString(errno);
fd_.reset();
+ buffer_.reset();
return false;
}
@@ -91,12 +92,89 @@ size_t FileInputStream::ByteCount() const {
}
bool FileInputStream::HadError() const {
- return !error_.empty();
+ return fd_ == -1;
}
std::string FileInputStream::GetError() const {
return error_;
}
+FileOutputStream::FileOutputStream(const std::string& path, int mode, size_t buffer_capacity)
+ : FileOutputStream(::android::base::utf8::open(path.c_str(), mode), buffer_capacity) {
+}
+
+FileOutputStream::FileOutputStream(int fd, size_t buffer_capacity)
+ : fd_(fd), buffer_capacity_(buffer_capacity), buffer_offset_(0u), total_byte_count_(0u) {
+ if (fd_ == -1) {
+ error_ = SystemErrorCodeToString(errno);
+ } else {
+ buffer_.reset(new uint8_t[buffer_capacity_]);
+ }
+}
+
+FileOutputStream::~FileOutputStream() {
+ // Flush the buffer.
+ Flush();
+}
+
+bool FileOutputStream::Next(void** data, size_t* size) {
+ if (fd_ == -1 || HadError()) {
+ return false;
+ }
+
+ if (buffer_offset_ == buffer_capacity_) {
+ if (!FlushImpl()) {
+ return false;
+ }
+ }
+
+ const size_t buffer_size = buffer_capacity_ - buffer_offset_;
+ *data = buffer_.get() + buffer_offset_;
+ *size = buffer_size;
+ total_byte_count_ += buffer_size;
+ buffer_offset_ = buffer_capacity_;
+ return true;
+}
+
+void FileOutputStream::BackUp(size_t count) {
+ if (count > buffer_offset_) {
+ count = buffer_offset_;
+ }
+ buffer_offset_ -= count;
+ total_byte_count_ -= count;
+}
+
+size_t FileOutputStream::ByteCount() const {
+ return total_byte_count_;
+}
+
+bool FileOutputStream::Flush() {
+ if (!HadError()) {
+ return FlushImpl();
+ }
+ return false;
+}
+
+bool FileOutputStream::FlushImpl() {
+ ssize_t n = TEMP_FAILURE_RETRY(write(fd_, buffer_.get(), buffer_offset_));
+ if (n < 0) {
+ error_ = SystemErrorCodeToString(errno);
+ fd_.reset();
+ buffer_.reset();
+ return false;
+ }
+
+ buffer_offset_ = 0u;
+ return true;
+}
+
+bool FileOutputStream::HadError() const {
+ return fd_ == -1;
+}
+
+std::string FileOutputStream::GetError() const {
+ return error_;
+}
+
} // namespace io
} // namespace aapt
diff --git a/tools/aapt2/io/FileInputStream.h b/tools/aapt2/io/FileStream.h
index 6beb9a186ce5..3b0766750eae 100644
--- a/tools/aapt2/io/FileInputStream.h
+++ b/tools/aapt2/io/FileStream.h
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-#ifndef AAPT_IO_FILEINPUTSTREAM_H
-#define AAPT_IO_FILEINPUTSTREAM_H
+#ifndef AAPT_IO_FILESTREAM_H
+#define AAPT_IO_FILESTREAM_H
#include "io/Io.h"
#include <memory>
#include <string>
+#include "android-base/file.h" // for O_BINARY
#include "android-base/macros.h"
#include "android-base/unique_fd.h"
@@ -57,7 +58,43 @@ class FileInputStream : public InputStream {
size_t total_byte_count_;
};
+class FileOutputStream : public OutputStream {
+ public:
+ explicit FileOutputStream(const std::string& path, int mode = O_RDWR | O_CREAT | O_BINARY,
+ size_t buffer_capacity = 4096);
+
+ // Takes ownership of `fd`.
+ explicit FileOutputStream(int fd, size_t buffer_capacity = 4096);
+
+ ~FileOutputStream();
+
+ bool Next(void** data, size_t* size) override;
+
+ // Immediately flushes out the contents of the buffer to disk.
+ bool Flush();
+
+ void BackUp(size_t count) override;
+
+ size_t ByteCount() const override;
+
+ bool HadError() const override;
+
+ std::string GetError() const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FileOutputStream);
+
+ bool FlushImpl();
+
+ android::base::unique_fd fd_;
+ std::string error_;
+ std::unique_ptr<uint8_t[]> buffer_;
+ size_t buffer_capacity_;
+ size_t buffer_offset_;
+ size_t total_byte_count_;
+};
+
} // namespace io
} // namespace aapt
-#endif // AAPT_IO_FILEINPUTSTREAM_H
+#endif // AAPT_IO_FILESTREAM_H
diff --git a/tools/aapt2/io/FileInputStream_test.cpp b/tools/aapt2/io/FileStream_test.cpp
index 7314ab7beeba..68c3cb1f4156 100644
--- a/tools/aapt2/io/FileInputStream_test.cpp
+++ b/tools/aapt2/io/FileStream_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "io/FileInputStream.h"
+#include "io/FileStream.h"
#include "android-base/macros.h"
#include "android-base/test_utils.h"
@@ -36,7 +36,7 @@ TEST(FileInputStreamTest, NextAndBackup) {
lseek64(file.fd, 0, SEEK_SET);
// Use a small buffer size so that we can call Next() a few times.
- FileInputStream in(file.fd, 10u);
+ FileInputStream in(file.release(), 10u);
ASSERT_FALSE(in.HadError());
EXPECT_THAT(in.ByteCount(), Eq(0u));
@@ -83,5 +83,47 @@ TEST(FileInputStreamTest, NextAndBackup) {
EXPECT_FALSE(in.HadError());
}
+TEST(FileOutputStreamTest, NextAndBackup) {
+ const std::string input = "this is a cool string";
+
+ TemporaryFile file;
+ int fd = file.release();
+
+ // FileOutputStream takes ownership.
+ FileOutputStream out(fd, 10u);
+ ASSERT_FALSE(out.HadError());
+ EXPECT_THAT(out.ByteCount(), Eq(0u));
+
+ char* buffer;
+ size_t size;
+ ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size));
+ ASSERT_THAT(size, Eq(10u));
+ ASSERT_THAT(buffer, NotNull());
+ EXPECT_THAT(out.ByteCount(), Eq(10u));
+ memcpy(buffer, input.c_str(), size);
+
+ ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size));
+ ASSERT_THAT(size, Eq(10u));
+ ASSERT_THAT(buffer, NotNull());
+ EXPECT_THAT(out.ByteCount(), Eq(20u));
+ memcpy(buffer, input.c_str() + 10u, size);
+
+ ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size));
+ ASSERT_THAT(size, Eq(10u));
+ ASSERT_THAT(buffer, NotNull());
+ EXPECT_THAT(out.ByteCount(), Eq(30u));
+ buffer[0] = input[20u];
+ out.BackUp(size - 1);
+ EXPECT_THAT(out.ByteCount(), Eq(21u));
+
+ ASSERT_TRUE(out.Flush());
+
+ lseek64(fd, 0, SEEK_SET);
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(fd, &actual));
+ EXPECT_THAT(actual, StrEq(input));
+}
+
} // namespace io
} // namespace aapt
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index 027cbd041fa6..1387d2218ed4 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -20,11 +20,12 @@
#include "utils/FileMap.h"
#include "Source.h"
+#include "io/FileStream.h"
#include "util/Files.h"
#include "util/Maybe.h"
#include "util/Util.h"
-using android::StringPiece;
+using ::android::StringPiece;
namespace aapt {
namespace io {
@@ -42,12 +43,20 @@ std::unique_ptr<IData> RegularFile::OpenAsData() {
return {};
}
-const Source& RegularFile::GetSource() const { return source_; }
+std::unique_ptr<io::InputStream> RegularFile::OpenInputStream() {
+ return util::make_unique<FileInputStream>(source_.path);
+}
+
+const Source& RegularFile::GetSource() const {
+ return source_;
+}
FileCollectionIterator::FileCollectionIterator(FileCollection* collection)
: current_(collection->files_.begin()), end_(collection->files_.end()) {}
-bool FileCollectionIterator::HasNext() { return current_ != end_; }
+bool FileCollectionIterator::HasNext() {
+ return current_ != end_;
+}
IFile* FileCollectionIterator::Next() {
IFile* result = current_->second.get();
diff --git a/tools/aapt2/io/FileSystem.h b/tools/aapt2/io/FileSystem.h
index dfd37172004b..6be8807735f1 100644
--- a/tools/aapt2/io/FileSystem.h
+++ b/tools/aapt2/io/FileSystem.h
@@ -24,17 +24,18 @@
namespace aapt {
namespace io {
-/**
- * A regular file from the file system. Uses mmap to open the data.
- */
+// A regular file from the file system. Uses mmap to open the data.
class RegularFile : public IFile {
public:
explicit RegularFile(const Source& source);
std::unique_ptr<IData> OpenAsData() override;
+ std::unique_ptr<io::InputStream> OpenInputStream() override;
const Source& GetSource() const override;
private:
+ DISALLOW_COPY_AND_ASSIGN(RegularFile);
+
Source source_;
};
@@ -48,23 +49,26 @@ class FileCollectionIterator : public IFileCollectionIterator {
io::IFile* Next() override;
private:
+ DISALLOW_COPY_AND_ASSIGN(FileCollectionIterator);
+
std::map<std::string, std::unique_ptr<IFile>>::const_iterator current_, end_;
};
-/**
- * An IFileCollection representing the file system.
- */
+// An IFileCollection representing the file system.
class FileCollection : public IFileCollection {
public:
- /**
- * Adds a file located at path. Returns the IFile representation of that file.
- */
+ FileCollection() = default;
+
+ // Adds a file located at path. Returns the IFile representation of that file.
IFile* InsertFile(const android::StringPiece& path);
IFile* FindFile(const android::StringPiece& path) override;
std::unique_ptr<IFileCollectionIterator> Iterator() override;
private:
+ DISALLOW_COPY_AND_ASSIGN(FileCollection);
+
friend class FileCollectionIterator;
+
std::map<std::string, std::unique_ptr<IFile>> files_;
};
diff --git a/tools/aapt2/io/Io.h b/tools/aapt2/io/Io.h
index a656740b35da..e1df23a698f5 100644
--- a/tools/aapt2/io/Io.h
+++ b/tools/aapt2/io/Io.h
@@ -58,6 +58,12 @@ class InputStream {
virtual bool HadError() const = 0;
};
+// A sub-InputStream interface that knows the total size of its stream.
+class KnownSizeInputStream : public InputStream {
+ public:
+ virtual size_t TotalSize() const = 0;
+};
+
// OutputStream interface that mimics protobuf's ZeroCopyOutputStream,
// with added error handling methods to better report issues.
class OutputStream {
diff --git a/tools/aapt2/io/StringInputStream.cpp b/tools/aapt2/io/StringStream.cpp
index 51a18a7d8a9f..4ca04a8c7477 100644
--- a/tools/aapt2/io/StringInputStream.cpp
+++ b/tools/aapt2/io/StringStream.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "io/StringInputStream.h"
+#include "io/StringStream.h"
using ::android::StringPiece;
@@ -37,14 +37,64 @@ bool StringInputStream::Next(const void** data, size_t* size) {
void StringInputStream::BackUp(size_t count) {
if (count > offset_) {
- count = offset_;
+ offset_ = 0u;
+ } else {
+ offset_ -= count;
}
- offset_ -= count;
}
size_t StringInputStream::ByteCount() const {
return offset_;
}
+size_t StringInputStream::TotalSize() const {
+ return str_.size();
+}
+
+StringOutputStream::StringOutputStream(std::string* str, size_t buffer_capacity)
+ : str_(str),
+ buffer_capacity_(buffer_capacity),
+ buffer_offset_(0u),
+ buffer_(new char[buffer_capacity]) {
+}
+
+StringOutputStream::~StringOutputStream() {
+ Flush();
+}
+
+bool StringOutputStream::Next(void** data, size_t* size) {
+ if (buffer_offset_ == buffer_capacity_) {
+ FlushImpl();
+ }
+
+ *data = buffer_.get() + buffer_offset_;
+ *size = buffer_capacity_ - buffer_offset_;
+ buffer_offset_ = buffer_capacity_;
+ return true;
+}
+
+void StringOutputStream::BackUp(size_t count) {
+ if (count > buffer_offset_) {
+ buffer_offset_ = 0u;
+ } else {
+ buffer_offset_ -= count;
+ }
+}
+
+size_t StringOutputStream::ByteCount() const {
+ return str_->size() + buffer_offset_;
+}
+
+void StringOutputStream::Flush() {
+ if (buffer_offset_ != 0u) {
+ FlushImpl();
+ }
+}
+
+void StringOutputStream::FlushImpl() {
+ str_->append(buffer_.get(), buffer_offset_);
+ buffer_offset_ = 0u;
+}
+
} // namespace io
} // namespace aapt
diff --git a/tools/aapt2/io/StringInputStream.h b/tools/aapt2/io/StringStream.h
index ff5b112ef274..f29890ab7ee5 100644
--- a/tools/aapt2/io/StringInputStream.h
+++ b/tools/aapt2/io/StringStream.h
@@ -14,18 +14,20 @@
* limitations under the License.
*/
-#ifndef AAPT_IO_STRINGINPUTSTREAM_H
-#define AAPT_IO_STRINGINPUTSTREAM_H
+#ifndef AAPT_IO_STRINGSTREAM_H
+#define AAPT_IO_STRINGSTREAM_H
#include "io/Io.h"
+#include <memory>
+
#include "android-base/macros.h"
#include "androidfw/StringPiece.h"
namespace aapt {
namespace io {
-class StringInputStream : public InputStream {
+class StringInputStream : public KnownSizeInputStream {
public:
explicit StringInputStream(const android::StringPiece& str);
@@ -43,6 +45,8 @@ class StringInputStream : public InputStream {
return {};
}
+ size_t TotalSize() const override;
+
private:
DISALLOW_COPY_AND_ASSIGN(StringInputStream);
@@ -50,7 +54,40 @@ class StringInputStream : public InputStream {
size_t offset_;
};
+class StringOutputStream : public OutputStream {
+ public:
+ explicit StringOutputStream(std::string* str, size_t buffer_capacity = 4096u);
+
+ ~StringOutputStream();
+
+ bool Next(void** data, size_t* size) override;
+
+ void BackUp(size_t count) override;
+
+ void Flush();
+
+ size_t ByteCount() const override;
+
+ inline bool HadError() const override {
+ return false;
+ }
+
+ inline std::string GetError() const override {
+ return {};
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StringOutputStream);
+
+ void FlushImpl();
+
+ std::string* str_;
+ size_t buffer_capacity_;
+ size_t buffer_offset_;
+ std::unique_ptr<char[]> buffer_;
+};
+
} // namespace io
} // namespace aapt
-#endif // AAPT_IO_STRINGINPUTSTREAM_H
+#endif // AAPT_IO_STRINGSTREAM_H
diff --git a/tools/aapt2/io/StringInputStream_test.cpp b/tools/aapt2/io/StringStream_test.cpp
index cc57bc498313..fb43fb773dd4 100644
--- a/tools/aapt2/io/StringInputStream_test.cpp
+++ b/tools/aapt2/io/StringStream_test.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-#include "io/StringInputStream.h"
+#include "io/StringStream.h"
+
+#include "io/Util.h"
#include "test/Test.h"
@@ -68,5 +70,16 @@ TEST(StringInputStreamTest, BackUp) {
EXPECT_THAT(in.ByteCount(), Eq(input.size()));
}
+TEST(StringOutputStreamTest, NextAndBackUp) {
+ std::string input = "hello this is a string";
+ std::string output;
+
+ StringInputStream in(input);
+ StringOutputStream out(&output, 10u);
+ ASSERT_TRUE(Copy(&out, &in));
+ out.Flush();
+ EXPECT_THAT(output, StrEq(input));
+}
+
} // namespace io
} // namespace aapt
diff --git a/tools/aapt2/io/Util.cpp b/tools/aapt2/io/Util.cpp
index 15114e8dbf54..d270340a180c 100644
--- a/tools/aapt2/io/Util.cpp
+++ b/tools/aapt2/io/Util.cpp
@@ -18,6 +18,8 @@
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
+using ::google::protobuf::io::ZeroCopyOutputStream;
+
namespace aapt {
namespace io {
@@ -91,5 +93,10 @@ bool Copy(OutputStream* out, InputStream* in) {
return !in->HadError();
}
+bool Copy(ZeroCopyOutputStream* out, InputStream* in) {
+ OutputStreamAdaptor adaptor(out);
+ return Copy(&adaptor, in);
+}
+
} // namespace io
} // namespace aapt
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index 02ee876014f7..1e48508bb4b2 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -42,6 +42,81 @@ bool CopyProtoToArchive(IAaptContext* context, ::google::protobuf::MessageLite*
// Copies the data from in to out. Returns false if there was an error.
// If there was an error, check the individual streams' HadError/GetError methods.
bool Copy(OutputStream* out, InputStream* in);
+bool Copy(::google::protobuf::io::ZeroCopyOutputStream* out, InputStream* in);
+
+class OutputStreamAdaptor : public io::OutputStream {
+ public:
+ explicit OutputStreamAdaptor(::google::protobuf::io::ZeroCopyOutputStream* out) : out_(out) {
+ }
+
+ bool Next(void** data, size_t* size) override {
+ int out_size;
+ bool result = out_->Next(data, &out_size);
+ *size = static_cast<size_t>(out_size);
+ if (!result) {
+ error_ocurred_ = true;
+ }
+ return result;
+ }
+
+ void BackUp(size_t count) override {
+ out_->BackUp(static_cast<int>(count));
+ }
+
+ size_t ByteCount() const override {
+ return static_cast<size_t>(out_->ByteCount());
+ }
+
+ bool HadError() const override {
+ return error_ocurred_;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OutputStreamAdaptor);
+
+ ::google::protobuf::io::ZeroCopyOutputStream* out_;
+ bool error_ocurred_ = false;
+};
+
+class ZeroCopyInputAdaptor : public ::google::protobuf::io::ZeroCopyInputStream {
+ public:
+ explicit ZeroCopyInputAdaptor(io::InputStream* in) : in_(in) {
+ }
+
+ bool Next(const void** data, int* size) override {
+ size_t out_size;
+ bool result = in_->Next(data, &out_size);
+ *size = static_cast<int>(out_size);
+ return result;
+ }
+
+ void BackUp(int count) override {
+ in_->BackUp(static_cast<size_t>(count));
+ }
+
+ bool Skip(int count) override {
+ const void* data;
+ int size;
+ while (Next(&data, &size)) {
+ if (size > count) {
+ BackUp(size - count);
+ return true;
+ } else {
+ count -= size;
+ }
+ }
+ return false;
+ }
+
+ ::google::protobuf::int64 ByteCount() const override {
+ return static_cast<::google::protobuf::int64>(in_->ByteCount());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ZeroCopyInputAdaptor);
+
+ io::InputStream* in_;
+};
} // namespace io
} // namespace aapt
diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp
index 6494d2def748..269b6c5a12e1 100644
--- a/tools/aapt2/io/ZipArchive.cpp
+++ b/tools/aapt2/io/ZipArchive.cpp
@@ -22,7 +22,7 @@
#include "Source.h"
#include "util/Util.h"
-using android::StringPiece;
+using ::android::StringPiece;
namespace aapt {
namespace io {
@@ -57,7 +57,13 @@ std::unique_ptr<IData> ZipFile::OpenAsData() {
}
}
-const Source& ZipFile::GetSource() const { return source_; }
+std::unique_ptr<io::InputStream> ZipFile::OpenInputStream() {
+ return OpenAsData();
+}
+
+const Source& ZipFile::GetSource() const {
+ return source_;
+}
bool ZipFile::WasCompressed() {
return zip_entry_.method != kCompressStored;
@@ -67,7 +73,9 @@ ZipFileCollectionIterator::ZipFileCollectionIterator(
ZipFileCollection* collection)
: current_(collection->files_.begin()), end_(collection->files_.end()) {}
-bool ZipFileCollectionIterator::HasNext() { return current_ != end_; }
+bool ZipFileCollectionIterator::HasNext() {
+ return current_ != end_;
+}
IFile* ZipFileCollectionIterator::Next() {
IFile* result = current_->get();
diff --git a/tools/aapt2/io/ZipArchive.h b/tools/aapt2/io/ZipArchive.h
index 56c74e326e6c..8381259d77c5 100644
--- a/tools/aapt2/io/ZipArchive.h
+++ b/tools/aapt2/io/ZipArchive.h
@@ -28,23 +28,20 @@
namespace aapt {
namespace io {
-/**
- * An IFile representing a file within a ZIP archive. If the file is compressed,
- * it is uncompressed
- * and copied into memory when opened. Otherwise it is mmapped from the ZIP
- * archive.
- */
+// An IFile representing a file within a ZIP archive. If the file is compressed, it is uncompressed
+// and copied into memory when opened. Otherwise it is mmapped from the ZIP archive.
class ZipFile : public IFile {
public:
- ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const Source& source);
+ ZipFile(::ZipArchiveHandle handle, const ::ZipEntry& entry, const Source& source);
std::unique_ptr<IData> OpenAsData() override;
+ std::unique_ptr<io::InputStream> OpenInputStream() override;
const Source& GetSource() const override;
bool WasCompressed() override;
private:
- ZipArchiveHandle zip_handle_;
- ZipEntry zip_entry_;
+ ::ZipArchiveHandle zip_handle_;
+ ::ZipEntry zip_entry_;
Source source_;
};
@@ -61,9 +58,7 @@ class ZipFileCollectionIterator : public IFileCollectionIterator {
std::vector<std::unique_ptr<IFile>>::const_iterator current_, end_;
};
-/**
- * An IFileCollection that represents a ZIP archive and the entries within it.
- */
+// An IFileCollection that represents a ZIP archive and the entries within it.
class ZipFileCollection : public IFileCollection {
public:
static std::unique_ptr<ZipFileCollection> Create(const android::StringPiece& path,
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 93c904f1a743..21c6b111b855 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -37,13 +37,12 @@ TableMerger::TableMerger(IAaptContext* context, ResourceTable* out_table,
CHECK(master_package_ != nullptr) << "package name or ID already taken";
}
-bool TableMerger::Merge(const Source& src, ResourceTable* table, io::IFileCollection* collection) {
- return MergeImpl(src, table, collection, false /*overlay*/, true /*allow_new*/);
-}
-
-bool TableMerger::MergeOverlay(const Source& src, ResourceTable* table,
- io::IFileCollection* collection) {
- return MergeImpl(src, table, collection, true /*overlay*/, options_.auto_add_overlay);
+bool TableMerger::Merge(const Source& src, ResourceTable* table, bool overlay,
+ io::IFileCollection* collection) {
+ // We allow adding new resources if this is not an overlay, or if the options allow overlays
+ // to add new resources.
+ return MergeImpl(src, table, collection, overlay,
+ options_.auto_add_overlay || !overlay /*allow_new*/);
}
// This will merge packages with the same package name (or no package name).
@@ -322,17 +321,20 @@ std::unique_ptr<FileReference> TableMerger::CloneAndMangleFile(
util::make_unique<FileReference>(master_table_->string_pool.MakeRef(newPath));
new_file_ref->SetComment(file_ref.GetComment());
new_file_ref->SetSource(file_ref.GetSource());
+ new_file_ref->type = file_ref.type;
+ new_file_ref->file = file_ref.file;
return new_file_ref;
}
return std::unique_ptr<FileReference>(file_ref.Clone(&master_table_->string_pool));
}
-bool TableMerger::MergeFileImpl(const ResourceFile& file_desc, io::IFile* file, bool overlay) {
+bool TableMerger::MergeFile(const ResourceFile& file_desc, bool overlay, io::IFile* file) {
ResourceTable table;
std::string path = ResourceUtils::BuildResourceFileName(file_desc);
std::unique_ptr<FileReference> file_ref =
util::make_unique<FileReference>(table.string_pool.MakeRef(path));
file_ref->SetSource(file_desc.source);
+ file_ref->type = file_desc.type;
file_ref->file = file;
ResourceTablePackage* pkg = table.CreatePackage(file_desc.name.package, 0x0);
@@ -341,17 +343,8 @@ bool TableMerger::MergeFileImpl(const ResourceFile& file_desc, io::IFile* file,
->FindOrCreateValue(file_desc.config, {})
->value = std::move(file_ref);
- return DoMerge(file->GetSource(), &table, pkg, false /* mangle */,
- overlay /* overlay */, true /* allow_new */, {});
-}
-
-bool TableMerger::MergeFile(const ResourceFile& file_desc, io::IFile* file) {
- return MergeFileImpl(file_desc, file, false /* overlay */);
-}
-
-bool TableMerger::MergeFileOverlay(const ResourceFile& file_desc,
- io::IFile* file) {
- return MergeFileImpl(file_desc, file, true /* overlay */);
+ return DoMerge(file->GetSource(), &table, pkg, false /* mangle */, overlay /* overlay */,
+ true /* allow_new */, {});
}
} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index 81518ffb2441..d024aa47d952 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -59,24 +59,18 @@ class TableMerger {
}
// Merges resources from the same or empty package. This is for local sources.
+ // If overlay is true, the resources are treated as overlays.
// An io::IFileCollection is optional and used to find the referenced Files and process them.
- bool Merge(const Source& src, ResourceTable* table, io::IFileCollection* collection = nullptr);
-
- // Merges resources from an overlay ResourceTable.
- // An io::IFileCollection is optional and used to find the referenced Files and process them.
- bool MergeOverlay(const Source& src, ResourceTable* table,
- io::IFileCollection* collection = nullptr);
+ bool Merge(const Source& src, ResourceTable* table, bool overlay,
+ io::IFileCollection* collection = nullptr);
// Merges resources from the given package, mangling the name. This is for static libraries.
// An io::IFileCollection is needed in order to find the referenced Files and process them.
bool MergeAndMangle(const Source& src, const android::StringPiece& package, ResourceTable* table,
io::IFileCollection* collection);
- // Merges a compiled file that belongs to this same or empty package. This is for local sources.
- bool MergeFile(const ResourceFile& fileDesc, io::IFile* file);
-
- // Merges a compiled file from an overlay, overriding an existing definition.
- bool MergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file);
+ // Merges a compiled file that belongs to this same or empty package.
+ bool MergeFile(const ResourceFile& fileDesc, bool overlay, io::IFile* file);
private:
DISALLOW_COPY_AND_ASSIGN(TableMerger);
@@ -91,9 +85,6 @@ class TableMerger {
ResourceTablePackage* master_package_;
std::set<std::string> merged_packages_;
- bool MergeFileImpl(const ResourceFile& file_desc, io::IFile* file,
- bool overlay);
-
bool MergeImpl(const Source& src, ResourceTable* src_table,
io::IFileCollection* collection, bool overlay, bool allow_new);
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 45b01a494164..3499809e5a83 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -69,7 +69,7 @@ TEST_F(TableMergerTest, SimpleMerge) {
TableMerger merger(context_.get(), &final_table, TableMergerOptions{});
io::FileCollection collection;
- ASSERT_TRUE(merger.Merge({}, table_a.get()));
+ ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
ASSERT_TRUE(merger.MergeAndMangle({}, "com.app.b", table_b.get(), &collection));
EXPECT_TRUE(merger.merged_packages().count("com.app.b") != 0);
@@ -98,7 +98,7 @@ TEST_F(TableMergerTest, MergeFile) {
file_desc.source = Source("res/layout-hdpi/main.xml");
test::TestFile test_file("path/to/res/layout-hdpi/main.xml.flat");
- ASSERT_TRUE(merger.MergeFile(file_desc, &test_file));
+ ASSERT_TRUE(merger.MergeFile(file_desc, false /*overlay*/, &test_file));
FileReference* file = test::GetValueForConfig<FileReference>(
&final_table, "com.app.a:layout/main", test::ParseConfigOrDie("hdpi-v4"));
@@ -117,8 +117,8 @@ TEST_F(TableMergerTest, MergeFileOverlay) {
test::TestFile file_a("path/to/fileA.xml.flat");
test::TestFile file_b("path/to/fileB.xml.flat");
- ASSERT_TRUE(merger.MergeFile(file_desc, &file_a));
- ASSERT_TRUE(merger.MergeFileOverlay(file_desc, &file_b));
+ ASSERT_TRUE(merger.MergeFile(file_desc, false /*overlay*/, &file_a));
+ ASSERT_TRUE(merger.MergeFile(file_desc, true /*overlay*/, &file_b));
}
TEST_F(TableMergerTest, MergeFileReferences) {
@@ -138,7 +138,7 @@ TEST_F(TableMergerTest, MergeFileReferences) {
io::FileCollection collection;
collection.InsertFile("res/xml/file.xml");
- ASSERT_TRUE(merger.Merge({}, table_a.get()));
+ ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
ASSERT_TRUE(merger.MergeAndMangle({}, "com.app.b", table_b.get(), &collection));
FileReference* f = test::GetValue<FileReference>(&final_table, "com.app.a:xml/file");
@@ -167,8 +167,8 @@ TEST_F(TableMergerTest, OverrideResourceWithOverlay) {
options.auto_add_overlay = false;
TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.Merge({}, base.get()));
- ASSERT_TRUE(merger.MergeOverlay({}, overlay.get()));
+ ASSERT_TRUE(merger.Merge({}, base.get(), false /*overlay*/));
+ ASSERT_TRUE(merger.Merge({}, overlay.get(), true /*overlay*/));
BinaryPrimitive* foo = test::GetValue<BinaryPrimitive>(&final_table, "com.app.a:bool/foo");
ASSERT_THAT(foo,
@@ -194,8 +194,8 @@ TEST_F(TableMergerTest, OverrideSameResourceIdsWithOverlay) {
options.auto_add_overlay = false;
TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.Merge({}, base.get()));
- ASSERT_TRUE(merger.MergeOverlay({}, overlay.get()));
+ ASSERT_TRUE(merger.Merge({}, base.get(), false /*overlay*/));
+ ASSERT_TRUE(merger.Merge({}, overlay.get(), true /*overlay*/));
}
TEST_F(TableMergerTest, FailToOverrideConflictingTypeIdsWithOverlay) {
@@ -217,8 +217,8 @@ TEST_F(TableMergerTest, FailToOverrideConflictingTypeIdsWithOverlay) {
options.auto_add_overlay = false;
TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.Merge({}, base.get()));
- ASSERT_FALSE(merger.MergeOverlay({}, overlay.get()));
+ ASSERT_TRUE(merger.Merge({}, base.get(), false /*overlay*/));
+ ASSERT_FALSE(merger.Merge({}, overlay.get(), true /*overlay*/));
}
TEST_F(TableMergerTest, FailToOverrideConflictingEntryIdsWithOverlay) {
@@ -240,8 +240,8 @@ TEST_F(TableMergerTest, FailToOverrideConflictingEntryIdsWithOverlay) {
options.auto_add_overlay = false;
TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.Merge({}, base.get()));
- ASSERT_FALSE(merger.MergeOverlay({}, overlay.get()));
+ ASSERT_TRUE(merger.Merge({}, base.get(), false /*overlay*/));
+ ASSERT_FALSE(merger.Merge({}, overlay.get(), true /*overlay*/));
}
TEST_F(TableMergerTest, MergeAddResourceFromOverlay) {
@@ -259,8 +259,8 @@ TEST_F(TableMergerTest, MergeAddResourceFromOverlay) {
options.auto_add_overlay = false;
TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.Merge({}, table_a.get()));
- ASSERT_TRUE(merger.MergeOverlay({}, table_b.get()));
+ ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+ ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
}
TEST_F(TableMergerTest, MergeAddResourceFromOverlayWithAutoAddOverlay) {
@@ -277,8 +277,8 @@ TEST_F(TableMergerTest, MergeAddResourceFromOverlayWithAutoAddOverlay) {
options.auto_add_overlay = true;
TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.Merge({}, table_a.get()));
- ASSERT_TRUE(merger.MergeOverlay({}, table_b.get()));
+ ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+ ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
}
TEST_F(TableMergerTest, FailToMergeNewResourceWithoutAutoAddOverlay) {
@@ -295,8 +295,8 @@ TEST_F(TableMergerTest, FailToMergeNewResourceWithoutAutoAddOverlay) {
options.auto_add_overlay = false;
TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.Merge({}, table_a.get()));
- ASSERT_FALSE(merger.MergeOverlay({}, table_b.get()));
+ ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+ ASSERT_FALSE(merger.Merge({}, table_b.get(), true /*overlay*/));
}
TEST_F(TableMergerTest, OverlaidStyleablesAndStylesShouldBeMerged) {
@@ -337,8 +337,8 @@ TEST_F(TableMergerTest, OverlaidStyleablesAndStylesShouldBeMerged) {
options.auto_add_overlay = true;
TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.Merge({}, table_a.get()));
- ASSERT_TRUE(merger.MergeOverlay({}, table_b.get()));
+ ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+ ASSERT_TRUE(merger.Merge({}, table_b.get(), true /*overlay*/));
Styleable* styleable = test::GetValue<Styleable>(&final_table, "com.app.a:styleable/Foo");
ASSERT_THAT(styleable, NotNull());
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index e658664d8653..5a62e9777065 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -19,7 +19,7 @@
#include "android-base/logging.h"
#include "androidfw/StringPiece.h"
-#include "io/StringInputStream.h"
+#include "io/StringStream.h"
#include "test/Common.h"
#include "util/Util.h"
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 61d056309a69..4e318a92f3fa 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -90,6 +90,10 @@ class TestFile : public io::IFile {
return {};
}
+ std::unique_ptr<io::InputStream> OpenInputStream() override {
+ return OpenAsData();
+ }
+
const Source& GetSource() const override {
return source_;
}
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index b3e0a926b4bd..35225066633c 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -215,8 +215,8 @@ std::unique_ptr<XmlResource> Inflate(InputStream* in, IDiagnostics* diag, const
return {};
}
}
- return util::make_unique<XmlResource>(ResourceFile{{}, {}, source}, StringPool{},
- std::move(stack.root));
+ return util::make_unique<XmlResource>(ResourceFile{{}, {}, ResourceFile::Type::kUnknown, source},
+ StringPool{}, std::move(stack.root));
}
static void CopyAttributes(Element* el, android::ResXMLParser* parser, StringPool* out_pool) {
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index 4ba04430ced3..34e6d3f54f56 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -19,7 +19,7 @@
#include <string>
#include "format/binary/XmlFlattener.h"
-#include "io/StringInputStream.h"
+#include "io/StringStream.h"
#include "test/Test.h"
using ::aapt::io::StringInputStream;
diff --git a/tools/aapt2/xml/XmlPullParser_test.cpp b/tools/aapt2/xml/XmlPullParser_test.cpp
index 681d9d48173f..5304bded85a5 100644
--- a/tools/aapt2/xml/XmlPullParser_test.cpp
+++ b/tools/aapt2/xml/XmlPullParser_test.cpp
@@ -18,43 +18,49 @@
#include "androidfw/StringPiece.h"
-#include "io/StringInputStream.h"
+#include "io/StringStream.h"
#include "test/Test.h"
using ::aapt::io::StringInputStream;
using ::android::StringPiece;
+using ::testing::Eq;
+using ::testing::StrEq;
+
+using Event = ::aapt::xml::XmlPullParser::Event;
namespace aapt {
+namespace xml {
TEST(XmlPullParserTest, NextChildNodeTraversesCorrectly) {
std::string str =
R"(<?xml version="1.0" encoding="utf-8"?>
<a><b><c xmlns:a="http://schema.org"><d/></c><e/></b></a>)";
StringInputStream input(str);
- xml::XmlPullParser parser(&input);
+ XmlPullParser parser(&input);
const size_t depth_outer = parser.depth();
- ASSERT_TRUE(xml::XmlPullParser::NextChildNode(&parser, depth_outer));
+ ASSERT_TRUE(XmlPullParser::NextChildNode(&parser, depth_outer));
- EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.event());
- EXPECT_EQ(StringPiece("a"), StringPiece(parser.element_name()));
+ EXPECT_THAT(parser.event(), Eq(XmlPullParser::Event::kStartElement));
+ EXPECT_THAT(parser.element_name(), StrEq("a"));
const size_t depth_a = parser.depth();
- ASSERT_TRUE(xml::XmlPullParser::NextChildNode(&parser, depth_a));
- EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.event());
- EXPECT_EQ(StringPiece("b"), StringPiece(parser.element_name()));
+ ASSERT_TRUE(XmlPullParser::NextChildNode(&parser, depth_a));
+ EXPECT_THAT(parser.event(), Eq(XmlPullParser::Event::kStartElement));
+ EXPECT_THAT(parser.element_name(), StrEq("b"));
const size_t depth_b = parser.depth();
- ASSERT_TRUE(xml::XmlPullParser::NextChildNode(&parser, depth_b));
- EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.event());
- EXPECT_EQ(StringPiece("c"), StringPiece(parser.element_name()));
+ ASSERT_TRUE(XmlPullParser::NextChildNode(&parser, depth_b));
+ EXPECT_THAT(parser.event(), Eq(XmlPullParser::Event::kStartElement));
+ EXPECT_THAT(parser.element_name(), StrEq("c"));
- ASSERT_TRUE(xml::XmlPullParser::NextChildNode(&parser, depth_b));
- EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.event());
- EXPECT_EQ(StringPiece("e"), StringPiece(parser.element_name()));
+ ASSERT_TRUE(XmlPullParser::NextChildNode(&parser, depth_b));
+ EXPECT_THAT(parser.event(), Eq(XmlPullParser::Event::kStartElement));
+ EXPECT_THAT(parser.element_name(), StrEq("e"));
- ASSERT_FALSE(xml::XmlPullParser::NextChildNode(&parser, depth_outer));
- EXPECT_EQ(xml::XmlPullParser::Event::kEndDocument, parser.event());
+ ASSERT_FALSE(XmlPullParser::NextChildNode(&parser, depth_outer));
+ EXPECT_THAT(parser.event(), Eq(XmlPullParser::Event::kEndDocument));
}
+} // namespace xml
} // namespace aapt
diff --git a/tools/aapt2/xml/XmlUtil.cpp b/tools/aapt2/xml/XmlUtil.cpp
index c1186e83369c..0a622b2bd336 100644
--- a/tools/aapt2/xml/XmlUtil.cpp
+++ b/tools/aapt2/xml/XmlUtil.cpp
@@ -16,20 +16,20 @@
#include "xml/XmlUtil.h"
+#include <algorithm>
#include <string>
#include "util/Maybe.h"
#include "util/Util.h"
+#include "xml/XmlDom.h"
-using android::StringPiece;
+using ::android::StringPiece;
namespace aapt {
namespace xml {
-std::string BuildPackageNamespace(const StringPiece& package,
- bool private_reference) {
- std::string result =
- private_reference ? kSchemaPrivatePrefix : kSchemaPublicPrefix;
+std::string BuildPackageNamespace(const StringPiece& package, bool private_reference) {
+ std::string result = private_reference ? kSchemaPrivatePrefix : kSchemaPublicPrefix;
result.append(package.data(), package.size());
return result;
}
@@ -39,8 +39,7 @@ Maybe<ExtractedPackage> ExtractPackageFromNamespace(
if (util::StartsWith(namespace_uri, kSchemaPublicPrefix)) {
StringPiece schema_prefix = kSchemaPublicPrefix;
StringPiece package = namespace_uri;
- package = package.substr(schema_prefix.size(),
- package.size() - schema_prefix.size());
+ package = package.substr(schema_prefix.size(), package.size() - schema_prefix.size());
if (package.empty()) {
return {};
}
@@ -49,8 +48,7 @@ Maybe<ExtractedPackage> ExtractPackageFromNamespace(
} else if (util::StartsWith(namespace_uri, kSchemaPrivatePrefix)) {
StringPiece schema_prefix = kSchemaPrivatePrefix;
StringPiece package = namespace_uri;
- package = package.substr(schema_prefix.size(),
- package.size() - schema_prefix.size());
+ package = package.substr(schema_prefix.size(), package.size() - schema_prefix.size());
if (package.empty()) {
return {};
}
@@ -76,5 +74,33 @@ void ResolvePackage(const IPackageDeclStack* decl_stack, Reference* in_ref) {
}
}
+namespace {
+
+class ToolsNamespaceRemover : public Visitor {
+ public:
+ using Visitor::Visit;
+
+ void Visit(Element* el) override {
+ auto new_end =
+ std::remove_if(el->namespace_decls.begin(), el->namespace_decls.end(),
+ [](const NamespaceDecl& decl) -> bool { return decl.uri == kSchemaTools; });
+ el->namespace_decls.erase(new_end, el->namespace_decls.end());
+
+ auto new_attr_end = std::remove_if(
+ el->attributes.begin(), el->attributes.end(),
+ [](const Attribute& attr) -> bool { return attr.namespace_uri == kSchemaTools; });
+ el->attributes.erase(new_attr_end, el->attributes.end());
+
+ Visitor::Visit(el);
+ }
+};
+
+} // namespace
+
+void StripAndroidStudioAttributes(Element* el) {
+ ToolsNamespaceRemover remover;
+ el->Accept(&remover);
+}
+
} // namespace xml
} // namespace aapt
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
index 4eb359a9eed4..592a604f87a6 100644
--- a/tools/aapt2/xml/XmlUtil.h
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -78,6 +78,12 @@ struct IPackageDeclStack {
// package declaration was private.
void ResolvePackage(const IPackageDeclStack* decl_stack, Reference* in_ref);
+class Element;
+
+// Strips out any attributes in the http://schemas.android.com/tools namespace, which is owned by
+// Android Studio and should not make it to the final APK.
+void StripAndroidStudioAttributes(Element* el);
+
} // namespace xml
} // namespace aapt