AAPT2 - Add ProtoApkSerializer implementation to convert from Binary to Proto.

Also new flag to choose the conversion direction:
  --output_format [binary|proto].

Test: Manual

Change-Id: I61d381aa49869b0fd6bd9f017dda0cac5ca998bb
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 027ac8f..2bd2405 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -26,36 +26,38 @@
 #include "format/binary/TableFlattener.h"
 #include "format/binary/XmlFlattener.h"
 #include "format/proto/ProtoDeserialize.h"
+#include "format/proto/ProtoSerialize.h"
 #include "io/BigBufferStream.h"
 #include "io/Util.h"
 #include "process/IResourceTableConsumer.h"
 #include "process/SymbolTable.h"
+#include "util/Util.h"
 
 using ::android::StringPiece;
+using ::android::base::StringPrintf;
 using ::std::unique_ptr;
 using ::std::vector;
 
 namespace aapt {
 
-class ISerializer {
+class IApkSerializer {
  public:
-  ISerializer(IAaptContext* context, const Source& source) : context_(context), source_(source) {}
+  IApkSerializer(IAaptContext* context, const Source& source) : context_(context), source_(source) {}
 
   virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
                             IArchiveWriter* writer) = 0;
   virtual bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) = 0;
   virtual bool SerializeFile(const FileReference* file, IArchiveWriter* writer) = 0;
 
-  virtual ~ISerializer() = default;
+  virtual ~IApkSerializer() = default;
 
  protected:
   IAaptContext* context_;
   Source source_;
 };
 
-bool ConvertProtoApkToBinaryApk(IAaptContext* context, unique_ptr<LoadedApk> apk,
-                                ISerializer* serializer, IArchiveWriter* writer) {
-  // AndroidManifest.xml
+bool ConvertApk(IAaptContext* context, unique_ptr<LoadedApk> apk, IApkSerializer* serializer,
+                IArchiveWriter* writer) {
   if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) {
     context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
                                      << "failed to serialize AndroidManifest.xml");
@@ -78,7 +80,7 @@
           if (file != nullptr) {
             if (file->file == nullptr) {
               context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                              << "no file associated with " << *file);
+                                               << "no file associated with " << *file);
               return false;
             }
 
@@ -121,14 +123,14 @@
 }
 
 
-class BinarySerializer : public ISerializer {
+class BinaryApkSerializer : public IApkSerializer {
  public:
-  BinarySerializer(IAaptContext* context, const Source& source,
+  BinaryApkSerializer(IAaptContext* context, const Source& source,
                    const TableFlattenerOptions& options)
-      : ISerializer(context, source), tableFlattenerOptions_(options) {}
+      : IApkSerializer(context, source), tableFlattenerOptions_(options) {}
 
   bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
-                    IArchiveWriter* writer) {
+                    IArchiveWriter* writer) override {
     BigBuffer buffer(4096);
     XmlFlattenerOptions options = {};
     options.use_utf16 = utf16;
@@ -142,7 +144,7 @@
                                         writer);
   }
 
-  bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) {
+  bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
     BigBuffer buffer(4096);
     TableFlattener table_flattener(tableFlattenerOptions_, &buffer);
     if (!table_flattener.Consume(context_, table)) {
@@ -154,7 +156,7 @@
                                         ArchiveEntry::kAlign, writer);
   }
 
-  bool SerializeFile(const FileReference* file, IArchiveWriter* writer) {
+  bool SerializeFile(const FileReference* file, IArchiveWriter* writer) override {
     if (file->type == ResourceFile::Type::kProtoXml) {
       unique_ptr<io::InputStream> in = file->file->OpenInputStream();
       if (in == nullptr) {
@@ -182,7 +184,7 @@
 
       if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer)) {
         context_->GetDiagnostics()->Error(DiagMessage(source_)
-                                          << "failed to serialize proto XML " << *file->path);
+                                          << "failed to serialize to binary XML: " << *file->path);
         return false;
       }
     } else {
@@ -199,7 +201,63 @@
  private:
   TableFlattenerOptions tableFlattenerOptions_;
 
-  DISALLOW_COPY_AND_ASSIGN(BinarySerializer);
+  DISALLOW_COPY_AND_ASSIGN(BinaryApkSerializer);
+};
+
+class ProtoApkSerializer : public IApkSerializer {
+ public:
+  ProtoApkSerializer(IAaptContext* context, const Source& source)
+      : IApkSerializer(context, source) {}
+
+  bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
+                    IArchiveWriter* writer) override {
+    pb::XmlNode pb_node;
+    SerializeXmlResourceToPb(*xml, &pb_node);
+    return io::CopyProtoToArchive(context_, &pb_node, path, ArchiveEntry::kCompress, writer);
+  }
+
+  bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
+    pb::ResourceTable pb_table;
+    SerializeTableToPb(*table, &pb_table);
+    return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath,
+                                  ArchiveEntry::kCompress, writer);
+  }
+
+  bool SerializeFile(const FileReference* file, IArchiveWriter* writer) override {
+    if (file->type == ResourceFile::Type::kBinaryXml) {
+      std::unique_ptr<io::IData> data = file->file->OpenAsData();
+      if (!data) {
+        context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                         << "failed to open file " << *file->path);
+        return false;
+      }
+
+      std::string error;
+      std::unique_ptr<xml::XmlResource> xml = xml::Inflate(data->data(), data->size(), &error);
+      if (xml == nullptr) {
+        context_->GetDiagnostics()->Error(DiagMessage(source_) << "failed to parse binary XML: "
+                                          << error);
+        return false;
+      }
+
+      if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer)) {
+        context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                          << "failed to serialize to proto XML: " << *file->path);
+        return false;
+      }
+    } else {
+      if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
+        context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                          << "failed to copy file " << *file->path);
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ProtoApkSerializer);
 };
 
 class Context : public IAaptContext {
@@ -254,12 +312,20 @@
 };
 
 int Convert(const vector<StringPiece>& args) {
+
+  static const char* kOutputFormatProto = "proto";
+  static const char* kOutputFormatBinary = "binary";
+
   Context context;
   std::string output_path;
+  Maybe<std::string> output_format;
   TableFlattenerOptions options;
   Flags flags =
       Flags()
           .RequiredFlag("-o", "Output path", &output_path)
+          .OptionalFlag("--output-format", StringPrintf("Format of the output. Accepted values are "
+                        "'%s' and '%s'. When not set, defaults to '%s'.", kOutputFormatProto,
+                        kOutputFormatBinary, kOutputFormatBinary), &output_format)
           .OptionalSwitch("--enable-sparse-encoding",
                           "Enables encoding sparse entries using a binary search tree.\n"
                           "This decreases APK size at the cost of resource retrieval performance.",
@@ -295,8 +361,21 @@
   if (writer == nullptr) {
     return 1;
   }
-  BinarySerializer serializer(&context, apk->GetSource(), options);
-  return ConvertProtoApkToBinaryApk(&context, std::move(apk), &serializer, writer.get()) ? 0 : 1;
+
+  unique_ptr<IApkSerializer> serializer;
+  if (!output_format || output_format.value() == kOutputFormatBinary) {
+    serializer.reset(new BinaryApkSerializer(&context, apk->GetSource(), options));
+  } else if (output_format.value() == kOutputFormatProto) {
+    serializer.reset(new ProtoApkSerializer(&context, apk->GetSource()));
+  } else {
+    context.GetDiagnostics()->Error(DiagMessage(path)
+                                    << "Invalid value for flag --output-format: "
+                                    << output_format.value());
+    return 1;
+  }
+
+
+  return ConvertApk(&context, std::move(apk), serializer.get(), writer.get()) ? 0 : 1;
 }
 
 }  // namespace aapt