AAPT2: Better debugging output

Test: make aapt2_tests
Change-Id: I7778b773201381538dc1f2e376abee4eb33e44c0
diff --git a/tools/aapt2/io/FileStream.cpp b/tools/aapt2/io/FileStream.cpp
index 2f7a4b3..4ff6d78 100644
--- a/tools/aapt2/io/FileStream.cpp
+++ b/tools/aapt2/io/FileStream.cpp
@@ -26,6 +26,7 @@
 #include "android-base/utf8.h"
 
 using ::android::base::SystemErrorCodeToString;
+using ::android::base::unique_fd;
 
 namespace aapt {
 namespace io {
@@ -100,7 +101,13 @@
 }
 
 FileOutputStream::FileOutputStream(const std::string& path, int mode, size_t buffer_capacity)
-    : FileOutputStream(::android::base::utf8::open(path.c_str(), mode), buffer_capacity) {
+    : FileOutputStream(unique_fd(::android::base::utf8::open(path.c_str(), mode)),
+                       buffer_capacity) {
+}
+
+FileOutputStream::FileOutputStream(unique_fd fd, size_t buffer_capacity)
+    : FileOutputStream(fd.get(), buffer_capacity) {
+  owned_fd_ = std::move(fd);
 }
 
 FileOutputStream::FileOutputStream(int fd, size_t buffer_capacity)
@@ -118,7 +125,7 @@
 }
 
 bool FileOutputStream::Next(void** data, size_t* size) {
-  if (fd_ == -1 || HadError()) {
+  if (HadError()) {
     return false;
   }
 
@@ -159,7 +166,8 @@
   ssize_t n = TEMP_FAILURE_RETRY(write(fd_, buffer_.get(), buffer_offset_));
   if (n < 0) {
     error_ = SystemErrorCodeToString(errno);
-    fd_.reset();
+    owned_fd_.reset();
+    fd_ = -1;
     buffer_.reset();
     return false;
   }
diff --git a/tools/aapt2/io/FileStream.h b/tools/aapt2/io/FileStream.h
index 3b07667..4ed1ad5 100644
--- a/tools/aapt2/io/FileStream.h
+++ b/tools/aapt2/io/FileStream.h
@@ -29,12 +29,15 @@
 namespace aapt {
 namespace io {
 
+constexpr size_t kDefaultBufferCapacity = 4096u;
+
 class FileInputStream : public InputStream {
  public:
-  explicit FileInputStream(const std::string& path, size_t buffer_capacity = 4096);
+  explicit FileInputStream(const std::string& path,
+                           size_t buffer_capacity = kDefaultBufferCapacity);
 
-  // Takes ownership of `fd`.
-  explicit FileInputStream(int fd, size_t buffer_capacity = 4096);
+  // Take ownership of `fd`.
+  explicit FileInputStream(int fd, size_t buffer_capacity = kDefaultBufferCapacity);
 
   bool Next(const void** data, size_t* size) override;
 
@@ -61,10 +64,14 @@
 class FileOutputStream : public OutputStream {
  public:
   explicit FileOutputStream(const std::string& path, int mode = O_RDWR | O_CREAT | O_BINARY,
-                            size_t buffer_capacity = 4096);
+                            size_t buffer_capacity = kDefaultBufferCapacity);
+
+  // Does not take ownership of `fd`.
+  explicit FileOutputStream(int fd, size_t buffer_capacity = kDefaultBufferCapacity);
 
   // Takes ownership of `fd`.
-  explicit FileOutputStream(int fd, size_t buffer_capacity = 4096);
+  explicit FileOutputStream(android::base::unique_fd fd,
+                            size_t buffer_capacity = kDefaultBufferCapacity);
 
   ~FileOutputStream();
 
@@ -86,7 +93,8 @@
 
   bool FlushImpl();
 
-  android::base::unique_fd fd_;
+  android::base::unique_fd owned_fd_;
+  int fd_;
   std::string error_;
   std::unique_ptr<uint8_t[]> buffer_;
   size_t buffer_capacity_;
diff --git a/tools/aapt2/io/FileStream_test.cpp b/tools/aapt2/io/FileStream_test.cpp
index 68c3cb1..a6d58ca 100644
--- a/tools/aapt2/io/FileStream_test.cpp
+++ b/tools/aapt2/io/FileStream_test.cpp
@@ -87,10 +87,8 @@
   const std::string input = "this is a cool string";
 
   TemporaryFile file;
-  int fd = file.release();
 
-  // FileOutputStream takes ownership.
-  FileOutputStream out(fd, 10u);
+  FileOutputStream out(file.fd, 10u);
   ASSERT_FALSE(out.HadError());
   EXPECT_THAT(out.ByteCount(), Eq(0u));
 
@@ -118,10 +116,10 @@
 
   ASSERT_TRUE(out.Flush());
 
-  lseek64(fd, 0, SEEK_SET);
+  lseek64(file.fd, 0, SEEK_SET);
 
   std::string actual;
-  ASSERT_TRUE(android::base::ReadFdToString(fd, &actual));
+  ASSERT_TRUE(android::base::ReadFdToString(file.fd, &actual));
   EXPECT_THAT(actual, StrEq(input));
 }
 
diff --git a/tools/aapt2/io/Util.cpp b/tools/aapt2/io/Util.cpp
index d270340..7ee1016 100644
--- a/tools/aapt2/io/Util.cpp
+++ b/tools/aapt2/io/Util.cpp
@@ -18,6 +18,7 @@
 
 #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
 
+using ::android::StringPiece;
 using ::google::protobuf::io::ZeroCopyOutputStream;
 
 namespace aapt {
@@ -93,6 +94,25 @@
   return !in->HadError();
 }
 
+bool Copy(OutputStream* out, const StringPiece& in) {
+  const char* in_buffer = in.data();
+  size_t in_len = in.size();
+  while (in_len != 0) {
+    void* out_buffer;
+    size_t out_len;
+    if (!out->Next(&out_buffer, &out_len)) {
+      return false;
+    }
+
+    const size_t bytes_to_copy = in_len < out_len ? in_len : out_len;
+    memcpy(out_buffer, in_buffer, bytes_to_copy);
+    out->BackUp(out_len - bytes_to_copy);
+    in_buffer += bytes_to_copy;
+    in_len -= bytes_to_copy;
+  }
+  return true;
+}
+
 bool Copy(ZeroCopyOutputStream* out, InputStream* in) {
   OutputStreamAdaptor adaptor(out);
   return Copy(&adaptor, in);
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index 1e48508..de2ab39 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -42,6 +42,7 @@
 // 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(OutputStream* out, const ::android::StringPiece& in);
 bool Copy(::google::protobuf::io::ZeroCopyOutputStream* out, InputStream* in);
 
 class OutputStreamAdaptor : public io::OutputStream {