AAPT2: Fix windows unicode path issues

Mingw64 was being difficult, so instead of defining a wmain entrypoint,
the command line parameters are parsed manually using built-in Windows
methods that support Unicode. The results are converted to UTF8 and
handled just like the rest of the linux/mac version of the code.

This also removes dependencies on std::istream in favour of a
FileInputStream which calls the appropriate unicode version of
open to read a file.

No speed regressions found on Linux or MacOS.

Bug: 62336414
Bug: 63830502
Test: manual
Change-Id: I597da51e33729ed1b98bf246e7e773337fd3fee8
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 14d05fd..53794e6 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -92,7 +92,9 @@
         "flatten/XmlFlattener.cpp",
         "io/BigBufferStreams.cpp",
         "io/File.cpp",
+        "io/FileInputStream.cpp",
         "io/FileSystem.cpp",
+        "io/StringInputStream.cpp",
         "io/Util.cpp",
         "io/ZipArchive.cpp",
         "link/AutoVersioner.cpp",
@@ -164,6 +166,7 @@
 cc_test_host {
     name: "aapt2_tests",
     srcs: [
+        "test/Builders.cpp",
         "test/Common.cpp",
         "**/*_test.cpp",
     ],
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index c5d38ab..89cce5f 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -14,9 +14,17 @@
  * limitations under the License.
  */
 
+#ifdef _WIN32
+// clang-format off
+#include <windows.h>
+#include <shellapi.h>
+// clang-format on
+#endif
+
 #include <iostream>
 #include <vector>
 
+#include "android-base/utf8.h"
 #include "androidfw/StringPiece.h"
 
 #include "Diagnostics.h"
@@ -43,7 +51,7 @@
 
 }  // namespace aapt
 
-int main(int argc, char** argv) {
+int MainImpl(int argc, char** argv) {
   if (argc >= 2) {
     argv += 1;
     argc -= 1;
@@ -74,7 +82,31 @@
     std::cerr << "no command specified\n";
   }
 
-  std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..."
-            << std::endl;
+  std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..." << std::endl;
   return 1;
 }
+
+int main(int argc, char** argv) {
+#ifdef _WIN32
+  LPWSTR* wide_argv = CommandLineToArgvW(GetCommandLineW(), &argc);
+  CHECK(wide_argv != nullptr) << "invalid command line parameters passed to process";
+
+  std::vector<std::string> utf8_args;
+  for (int i = 0; i < argc; i++) {
+    std::string utf8_arg;
+    if (!::android::base::WideToUTF8(wide_argv[i], &utf8_arg)) {
+      std::cerr << "error converting input arguments to UTF-8" << std::endl;
+      return 1;
+    }
+    utf8_args.push_back(std::move(utf8_arg));
+  }
+  LocalFree(wide_argv);
+
+  std::unique_ptr<char* []> utf8_argv(new char*[utf8_args.size()]);
+  for (int i = 0; i < argc; i++) {
+    utf8_argv[i] = const_cast<char*>(utf8_args[i].c_str());
+  }
+  argv = utf8_argv.get();
+#endif
+  return MainImpl(argc, argv);
+}
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 1683c64..971b45e 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -22,9 +22,11 @@
 #include "ResourceTable.h"
 #include "ResourceUtils.h"
 #include "ResourceValues.h"
+#include "io/StringInputStream.h"
 #include "test/Test.h"
 #include "xml/XmlPullParser.h"
 
+using ::aapt::io::StringInputStream;
 using ::aapt::test::StrValueEq;
 using ::aapt::test::ValueEq;
 using ::android::ResTable_map;
@@ -43,11 +45,13 @@
 
 TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-  std::stringstream input(kXmlPreamble);
-  input << R"(<attr name="foo"/>)" << std::endl;
   ResourceTable table;
   ResourceParser parser(context->GetDiagnostics(), &table, Source{"test"}, {});
-  xml::XmlPullParser xml_parser(input);
+
+  std::string input = kXmlPreamble;
+  input += R"(<attr name="foo"/>)";
+  StringInputStream in(input);
+  xml::XmlPullParser xml_parser(&in);
   ASSERT_FALSE(parser.Parse(&xml_parser));
 }
 
@@ -62,12 +66,16 @@
   }
 
   ::testing::AssertionResult TestParse(const StringPiece& str, const ConfigDescription& config) {
-    std::stringstream input(kXmlPreamble);
-    input << "<resources>\n" << str << "\n</resources>" << std::endl;
     ResourceParserOptions parserOptions;
     ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"}, config,
                           parserOptions);
-    xml::XmlPullParser xmlParser(input);
+
+    std::string input = kXmlPreamble;
+    input += "<resources>\n";
+    input.append(str.data(), str.size());
+    input += "\n</resources>";
+    StringInputStream in(input);
+    xml::XmlPullParser xmlParser(&in);
     if (parser.Parse(&xmlParser)) {
       return ::testing::AssertionSuccess();
     }
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index b64cd8c..8536edb 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -16,11 +16,11 @@
 
 #include <dirent.h>
 
-#include <fstream>
 #include <string>
 
 #include "android-base/errors.h"
 #include "android-base/file.h"
+#include "android-base/utf8.h"
 #include "androidfw/StringPiece.h"
 #include "google/protobuf/io/coded_stream.h"
 #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
@@ -38,6 +38,7 @@
 #include "flatten/Archive.h"
 #include "flatten/XmlFlattener.h"
 #include "io/BigBufferOutputStream.h"
+#include "io/FileInputStream.h"
 #include "io/Util.h"
 #include "proto/ProtoSerialize.h"
 #include "util/Files.h"
@@ -46,8 +47,9 @@
 #include "xml/XmlDom.h"
 #include "xml/XmlPullParser.h"
 
-using android::StringPiece;
-using google::protobuf::io::CopyingOutputStreamAdaptor;
+using ::aapt::io::FileInputStream;
+using ::android::StringPiece;
+using ::google::protobuf::io::CopyingOutputStreamAdaptor;
 
 namespace aapt {
 
@@ -57,19 +59,14 @@
   std::string name;
   std::string extension;
 
-  // Original config str. We keep this because when we parse the config, we may
-  // add on
-  // version qualifiers. We want to preserve the original input so the output is
-  // easily
+  // Original config str. We keep this because when we parse the config, we may add on
+  // version qualifiers. We want to preserve the original input so the output is easily
   // computed before hand.
   std::string config_str;
   ConfigDescription config;
 };
 
-/**
- * Resource file paths are expected to look like:
- * [--/res/]type[-config]/name
- */
+// Resource file paths are expected to look like: [--/res/]type[-config]/name
 static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
                                                        std::string* out_error) {
   std::vector<std::string> parts = util::Split(path, file::sDirSep);
@@ -137,9 +134,7 @@
   return util::StartsWith(filename, ".");
 }
 
-/**
- * Walks the res directory structure, looking for resource files.
- */
+// Walks the res directory structure, looking for resource files.
 static bool LoadInputFilesFromDir(IAaptContext* context, const CompileOptions& options,
                                   std::vector<ResourcePathData>* out_path_data) {
   const std::string& root_dir = options.res_dir.value();
@@ -195,22 +190,20 @@
                          const std::string& output_path) {
   ResourceTable table;
   {
-    std::ifstream fin(path_data.source.path, std::ifstream::binary);
-    if (!fin) {
+    FileInputStream fin(path_data.source.path);
+    if (fin.HadError()) {
       context->GetDiagnostics()->Error(DiagMessage(path_data.source)
-                                       << "failed to open file: "
-                                       << android::base::SystemErrorCodeToString(errno));
+                                       << "failed to open file: " << fin.GetError());
       return false;
     }
 
     // Parse the values file from XML.
-    xml::XmlPullParser xml_parser(fin);
+    xml::XmlPullParser xml_parser(&fin);
 
     ResourceParserOptions parser_options;
     parser_options.error_on_positional_arguments = !options.legacy_mode;
 
-    // If the filename includes donottranslate, then the default translatable is
-    // false.
+    // If the filename includes donottranslate, then the default translatable is false.
     parser_options.translatable = path_data.name.find("donottranslate") == std::string::npos;
 
     ResourceParser res_parser(context->GetDiagnostics(), &table, path_data.source, path_data.config,
@@ -218,8 +211,6 @@
     if (!res_parser.Parse(&xml_parser)) {
       return false;
     }
-
-    fin.close();
   }
 
   if (options.pseudolocalize) {
@@ -239,8 +230,7 @@
   // Assign an ID to any package that has resources.
   for (auto& pkg : table.packages) {
     if (!pkg->id) {
-      // If no package ID was set while parsing (public identifiers), auto
-      // assign an ID.
+      // If no package ID was set while parsing (public identifiers), auto assign an ID.
       pkg->id = context->GetPackageId();
     }
   }
@@ -367,7 +357,7 @@
   return true;
 }
 
-static bool IsValidFile(IAaptContext* context, const StringPiece& input_path) {
+static bool IsValidFile(IAaptContext* context, const std::string& input_path) {
   const file::FileType file_type = file::GetFileType(input_path);
   if (file_type != file::FileType::kRegular && file_type != file::FileType::kSymlink) {
     if (file_type == file::FileType::kDirectory) {
@@ -393,17 +383,14 @@
 
   std::unique_ptr<xml::XmlResource> xmlres;
   {
-    std::ifstream fin(path_data.source.path, std::ifstream::binary);
-    if (!fin) {
+    FileInputStream fin(path_data.source.path);
+    if (fin.HadError()) {
       context->GetDiagnostics()->Error(DiagMessage(path_data.source)
-                                       << "failed to open file: "
-                                       << android::base::SystemErrorCodeToString(errno));
+                                       << "failed to open file: " << fin.GetError());
       return false;
     }
 
     xmlres = xml::Inflate(&fin, context->GetDiagnostics(), path_data.source);
-
-    fin.close();
   }
 
   if (!xmlres) {
@@ -432,12 +419,9 @@
     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);
 
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index e6bf3a6..c9c836d 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -40,6 +40,7 @@
 #include "flatten/TableFlattener.h"
 #include "flatten/XmlFlattener.h"
 #include "io/BigBufferInputStream.h"
+#include "io/FileInputStream.h"
 #include "io/FileSystem.h"
 #include "io/Util.h"
 #include "io/ZipArchive.h"
@@ -61,8 +62,9 @@
 #include "util/Files.h"
 #include "xml/XmlDom.h"
 
-using android::StringPiece;
-using android::base::StringPrintf;
+using ::aapt::io::FileInputStream;
+using ::android::StringPiece;
+using ::android::base::StringPrintf;
 
 namespace aapt {
 
@@ -284,13 +286,11 @@
   return table;
 }
 
-/**
- * Inflates an XML file from the source path.
- */
+// Inflates an XML file from the source path.
 static std::unique_ptr<xml::XmlResource> LoadXml(const std::string& path, IDiagnostics* diag) {
-  std::ifstream fin(path, std::ifstream::binary);
-  if (!fin) {
-    diag->Error(DiagMessage(path) << strerror(errno));
+  FileInputStream fin(path);
+  if (fin.HadError()) {
+    diag->Error(DiagMessage(path) << "failed to load XML file: " << fin.GetError());
     return {};
   }
   return xml::Inflate(&fin, diag, Source(path));
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index c56492c..b0ed792 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -22,13 +22,14 @@
 #include <memory>
 #include <utility>
 
-#include <android-base/file.h>
-#include <android-base/logging.h>
+#include "android-base/file.h"
+#include "android-base/logging.h"
 
 #include "ConfigDescription.h"
 #include "Diagnostics.h"
 #include "io/File.h"
 #include "io/FileSystem.h"
+#include "io/StringInputStream.h"
 #include "util/Maybe.h"
 #include "util/Util.h"
 #include "xml/XmlActionExecutor.h"
@@ -49,6 +50,7 @@
 using ::aapt::configuration::Locale;
 using ::aapt::io::IFile;
 using ::aapt::io::RegularFile;
+using ::aapt::io::StringInputStream;
 using ::aapt::util::TrimWhitespace;
 using ::aapt::xml::Element;
 using ::aapt::xml::FindRootElement;
@@ -194,8 +196,7 @@
 }
 
 Maybe<PostProcessingConfiguration> ConfigurationParser::Parse() {
-  std::istringstream in(contents_);
-
+  StringInputStream in(contents_);
   auto doc = xml::Inflate(&in, diag_, Source("config.xml"));
   if (!doc) {
     return {};
diff --git a/tools/aapt2/flatten/Archive.cpp b/tools/aapt2/flatten/Archive.cpp
index 826f91b..5f8bd06 100644
--- a/tools/aapt2/flatten/Archive.cpp
+++ b/tools/aapt2/flatten/Archive.cpp
@@ -23,12 +23,14 @@
 
 #include "android-base/errors.h"
 #include "android-base/macros.h"
+#include "android-base/utf8.h"
 #include "androidfw/StringPiece.h"
 #include "ziparchive/zip_writer.h"
 
 #include "util/Files.h"
 
-using android::StringPiece;
+using ::android::StringPiece;
+using ::android::base::SystemErrorCodeToString;
 
 namespace aapt {
 
@@ -58,11 +60,11 @@
 
     std::string full_path = dir_;
     file::AppendPath(&full_path, path);
-    file::mkdirs(file::GetStem(full_path));
+    file::mkdirs(file::GetStem(full_path).to_string());
 
-    file_ = {fopen(full_path.data(), "wb"), fclose};
+    file_ = {::android::base::utf8::fopen(full_path.c_str(), "wb"), fclose};
     if (!file_) {
-      error_ = android::base::SystemErrorCodeToString(errno);
+      error_ = SystemErrorCodeToString(errno);
       return false;
     }
     return true;
@@ -74,7 +76,7 @@
     }
 
     if (fwrite(data, 1, len, file_.get()) != static_cast<size_t>(len)) {
-      error_ = android::base::SystemErrorCodeToString(errno);
+      error_ = SystemErrorCodeToString(errno);
       file_.reset(nullptr);
       return false;
     }
@@ -121,9 +123,9 @@
   ZipFileWriter() = default;
 
   bool Open(const StringPiece& path) {
-    file_ = {fopen(path.data(), "w+b"), fclose};
+    file_ = {::android::base::utf8::fopen(path.to_string().c_str(), "w+b"), fclose};
     if (!file_) {
-      error_ = android::base::SystemErrorCodeToString(errno);
+      error_ = SystemErrorCodeToString(errno);
       return false;
     }
     writer_ = util::make_unique<ZipWriter>(file_.get());
diff --git a/tools/aapt2/io/FileInputStream.cpp b/tools/aapt2/io/FileInputStream.cpp
new file mode 100644
index 0000000..07dbb5a
--- /dev/null
+++ b/tools/aapt2/io/FileInputStream.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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 "io/FileInputStream.h"
+
+#include <errno.h>   // for errno
+#include <fcntl.h>   // for O_RDONLY
+#include <unistd.h>  // for read
+
+#include "android-base/errors.h"
+#include "android-base/file.h"  // for O_BINARY
+#include "android-base/macros.h"
+#include "android-base/utf8.h"
+
+using ::android::base::SystemErrorCodeToString;
+
+namespace aapt {
+namespace io {
+
+FileInputStream::FileInputStream(const std::string& path, size_t buffer_capacity)
+    : FileInputStream(::android::base::utf8::open(path.c_str(), O_RDONLY | O_BINARY),
+                      buffer_capacity) {
+}
+
+FileInputStream::FileInputStream(int fd, size_t buffer_capacity)
+    : fd_(fd),
+      buffer_capacity_(buffer_capacity),
+      buffer_offset_(0u),
+      buffer_size_(0u),
+      total_byte_count_(0u) {
+  if (fd_ == -1) {
+    error_ = SystemErrorCodeToString(errno);
+  } else {
+    buffer_.reset(new uint8_t[buffer_capacity_]);
+  }
+}
+
+bool FileInputStream::Next(const void** data, size_t* size) {
+  if (HadError()) {
+    return false;
+  }
+
+  // Deal with any remaining bytes after BackUp was called.
+  if (buffer_offset_ != buffer_size_) {
+    *data = buffer_.get() + buffer_offset_;
+    *size = buffer_size_ - buffer_offset_;
+    total_byte_count_ += buffer_size_ - buffer_offset_;
+    buffer_offset_ = buffer_size_;
+    return true;
+  }
+
+  ssize_t n = TEMP_FAILURE_RETRY(read(fd_, buffer_.get(), buffer_capacity_));
+  if (n < 0) {
+    error_ = SystemErrorCodeToString(errno);
+    fd_.reset();
+    return false;
+  }
+
+  buffer_size_ = static_cast<size_t>(n);
+  buffer_offset_ = buffer_size_;
+  total_byte_count_ += buffer_size_;
+
+  *data = buffer_.get();
+  *size = buffer_size_;
+  return buffer_size_ != 0u;
+}
+
+void FileInputStream::BackUp(size_t count) {
+  if (count > buffer_offset_) {
+    count = buffer_offset_;
+  }
+  buffer_offset_ -= count;
+  total_byte_count_ -= count;
+}
+
+size_t FileInputStream::ByteCount() const {
+  return total_byte_count_;
+}
+
+bool FileInputStream::HadError() const {
+  return !error_.empty();
+}
+
+std::string FileInputStream::GetError() const {
+  return error_;
+}
+
+}  // namespace io
+}  // namespace aapt
diff --git a/tools/aapt2/io/FileInputStream.h b/tools/aapt2/io/FileInputStream.h
new file mode 100644
index 0000000..6beb9a1
--- /dev/null
+++ b/tools/aapt2/io/FileInputStream.h
@@ -0,0 +1,63 @@
+/*
+ * 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_FILEINPUTSTREAM_H
+#define AAPT_IO_FILEINPUTSTREAM_H
+
+#include "io/Io.h"
+
+#include <memory>
+#include <string>
+
+#include "android-base/macros.h"
+#include "android-base/unique_fd.h"
+
+namespace aapt {
+namespace io {
+
+class FileInputStream : public InputStream {
+ public:
+  explicit FileInputStream(const std::string& path, size_t buffer_capacity = 4096);
+
+  // Takes ownership of `fd`.
+  explicit FileInputStream(int fd, size_t buffer_capacity = 4096);
+
+  bool Next(const void** data, size_t* size) override;
+
+  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(FileInputStream);
+
+  android::base::unique_fd fd_;
+  std::string error_;
+  std::unique_ptr<uint8_t[]> buffer_;
+  size_t buffer_capacity_;
+  size_t buffer_offset_;
+  size_t buffer_size_;
+  size_t total_byte_count_;
+};
+
+}  // namespace io
+}  // namespace aapt
+
+#endif  // AAPT_IO_FILEINPUTSTREAM_H
diff --git a/tools/aapt2/io/FileInputStream_test.cpp b/tools/aapt2/io/FileInputStream_test.cpp
new file mode 100644
index 0000000..7314ab7
--- /dev/null
+++ b/tools/aapt2/io/FileInputStream_test.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 "io/FileInputStream.h"
+
+#include "android-base/macros.h"
+#include "android-base/test_utils.h"
+
+#include "test/Test.h"
+
+using ::android::StringPiece;
+using ::testing::Eq;
+using ::testing::NotNull;
+using ::testing::StrEq;
+
+namespace aapt {
+namespace io {
+
+TEST(FileInputStreamTest, NextAndBackup) {
+  std::string input = "this is a cool string";
+  TemporaryFile file;
+  ASSERT_THAT(TEMP_FAILURE_RETRY(write(file.fd, input.c_str(), input.size())), Eq(21));
+  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);
+  ASSERT_FALSE(in.HadError());
+  EXPECT_THAT(in.ByteCount(), Eq(0u));
+
+  const char* buffer;
+  size_t size;
+  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size)) << in.GetError();
+  ASSERT_THAT(size, Eq(10u));
+  ASSERT_THAT(buffer, NotNull());
+  EXPECT_THAT(in.ByteCount(), Eq(10u));
+  EXPECT_THAT(StringPiece(buffer, size), Eq("this is a "));
+
+  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_THAT(size, Eq(10u));
+  ASSERT_THAT(buffer, NotNull());
+  EXPECT_THAT(in.ByteCount(), Eq(20u));
+  EXPECT_THAT(StringPiece(buffer, size), Eq("cool strin"));
+
+  in.BackUp(5u);
+  EXPECT_THAT(in.ByteCount(), Eq(15u));
+
+  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_THAT(size, Eq(5u));
+  ASSERT_THAT(buffer, NotNull());
+  ASSERT_THAT(in.ByteCount(), Eq(20u));
+  EXPECT_THAT(StringPiece(buffer, size), Eq("strin"));
+
+  // Backup 1 more than possible. Should clamp.
+  in.BackUp(11u);
+  EXPECT_THAT(in.ByteCount(), Eq(10u));
+
+  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_THAT(size, Eq(10u));
+  ASSERT_THAT(buffer, NotNull());
+  ASSERT_THAT(in.ByteCount(), Eq(20u));
+  EXPECT_THAT(StringPiece(buffer, size), Eq("cool strin"));
+
+  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_THAT(size, Eq(1u));
+  ASSERT_THAT(buffer, NotNull());
+  ASSERT_THAT(in.ByteCount(), Eq(21u));
+  EXPECT_THAT(StringPiece(buffer, size), Eq("g"));
+
+  EXPECT_FALSE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  EXPECT_FALSE(in.HadError());
+}
+
+}  // namespace io
+}  // namespace aapt
diff --git a/tools/aapt2/io/StringInputStream.cpp b/tools/aapt2/io/StringInputStream.cpp
new file mode 100644
index 0000000..51a18a7
--- /dev/null
+++ b/tools/aapt2/io/StringInputStream.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 "io/StringInputStream.h"
+
+using ::android::StringPiece;
+
+namespace aapt {
+namespace io {
+
+StringInputStream::StringInputStream(const StringPiece& str) : str_(str), offset_(0u) {
+}
+
+bool StringInputStream::Next(const void** data, size_t* size) {
+  if (offset_ == str_.size()) {
+    return false;
+  }
+
+  *data = str_.data() + offset_;
+  *size = str_.size() - offset_;
+  offset_ = str_.size();
+  return true;
+}
+
+void StringInputStream::BackUp(size_t count) {
+  if (count > offset_) {
+    count = offset_;
+  }
+  offset_ -= count;
+}
+
+size_t StringInputStream::ByteCount() const {
+  return offset_;
+}
+
+}  // namespace io
+}  // namespace aapt
diff --git a/tools/aapt2/io/StringInputStream.h b/tools/aapt2/io/StringInputStream.h
new file mode 100644
index 0000000..ff5b112
--- /dev/null
+++ b/tools/aapt2/io/StringInputStream.h
@@ -0,0 +1,56 @@
+/*
+ * 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_STRINGINPUTSTREAM_H
+#define AAPT_IO_STRINGINPUTSTREAM_H
+
+#include "io/Io.h"
+
+#include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
+
+namespace aapt {
+namespace io {
+
+class StringInputStream : public InputStream {
+ public:
+  explicit StringInputStream(const android::StringPiece& str);
+
+  bool Next(const void** data, size_t* size) override;
+
+  void BackUp(size_t count) override;
+
+  size_t ByteCount() const override;
+
+  inline bool HadError() const override {
+    return false;
+  }
+
+  inline std::string GetError() const override {
+    return {};
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StringInputStream);
+
+  android::StringPiece str_;
+  size_t offset_;
+};
+
+}  // namespace io
+}  // namespace aapt
+
+#endif  // AAPT_IO_STRINGINPUTSTREAM_H
diff --git a/tools/aapt2/io/StringInputStream_test.cpp b/tools/aapt2/io/StringInputStream_test.cpp
new file mode 100644
index 0000000..cc57bc4
--- /dev/null
+++ b/tools/aapt2/io/StringInputStream_test.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "io/StringInputStream.h"
+
+#include "test/Test.h"
+
+using ::android::StringPiece;
+using ::testing::Eq;
+using ::testing::NotNull;
+using ::testing::StrEq;
+
+namespace aapt {
+namespace io {
+
+TEST(StringInputStreamTest, OneCallToNextShouldReturnEntireBuffer) {
+  constexpr const size_t kCount = 1000;
+  std::string input;
+  input.resize(kCount, 0x7f);
+  input[0] = 0x00;
+  input[kCount - 1] = 0xff;
+  StringInputStream in(input);
+
+  const char* buffer;
+  size_t size;
+  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_THAT(size, Eq(kCount));
+  ASSERT_THAT(buffer, NotNull());
+
+  EXPECT_THAT(buffer[0], Eq(0x00));
+  EXPECT_THAT(buffer[kCount - 1], Eq('\xff'));
+
+  EXPECT_FALSE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  EXPECT_FALSE(in.HadError());
+}
+
+TEST(StringInputStreamTest, BackUp) {
+  std::string input = "hello this is a string";
+  StringInputStream in(input);
+
+  const char* buffer;
+  size_t size;
+  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_THAT(size, Eq(input.size()));
+  ASSERT_THAT(buffer, NotNull());
+  EXPECT_THAT(in.ByteCount(), Eq(input.size()));
+
+  in.BackUp(6u);
+  EXPECT_THAT(in.ByteCount(), Eq(input.size() - 6u));
+
+  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_THAT(size, Eq(6u));
+  ASSERT_THAT(buffer, NotNull());
+  ASSERT_THAT(buffer, StrEq("string"));
+  EXPECT_THAT(in.ByteCount(), Eq(input.size()));
+}
+
+}  // namespace io
+}  // namespace aapt
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
new file mode 100644
index 0000000..b579545
--- /dev/null
+++ b/tools/aapt2/test/Builders.cpp
@@ -0,0 +1,216 @@
+/*
+ * 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 "test/Builders.h"
+
+#include "android-base/logging.h"
+#include "androidfw/StringPiece.h"
+
+#include "io/StringInputStream.h"
+#include "test/Common.h"
+#include "util/Util.h"
+
+using ::aapt::io::StringInputStream;
+using ::android::StringPiece;
+
+namespace aapt {
+namespace test {
+
+ResourceTableBuilder& ResourceTableBuilder::SetPackageId(const StringPiece& package_name,
+                                                         uint8_t id) {
+  ResourceTablePackage* package = table_->CreatePackage(package_name, id);
+  CHECK(package != nullptr);
+  return *this;
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddSimple(const StringPiece& name,
+                                                      const ResourceId& id) {
+  return AddValue(name, id, util::make_unique<Id>());
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddSimple(const StringPiece& name,
+                                                      const ConfigDescription& config,
+                                                      const ResourceId& id) {
+  return AddValue(name, config, id, util::make_unique<Id>());
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddReference(const StringPiece& name,
+                                                         const StringPiece& ref) {
+  return AddReference(name, {}, ref);
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddReference(const StringPiece& name,
+                                                         const ResourceId& id,
+                                                         const StringPiece& ref) {
+  return AddValue(name, id, util::make_unique<Reference>(ParseNameOrDie(ref)));
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddString(const StringPiece& name,
+                                                      const StringPiece& str) {
+  return AddString(name, {}, str);
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddString(const StringPiece& name, const ResourceId& id,
+                                                      const StringPiece& str) {
+  return AddValue(name, id, util::make_unique<String>(table_->string_pool.MakeRef(str)));
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddString(const StringPiece& name, const ResourceId& id,
+                                                      const ConfigDescription& config,
+                                                      const StringPiece& str) {
+  return AddValue(name, config, id, util::make_unique<String>(table_->string_pool.MakeRef(str)));
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name,
+                                                             const StringPiece& path) {
+  return AddFileReference(name, {}, path);
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name,
+                                                             const ResourceId& id,
+                                                             const StringPiece& path) {
+  return AddValue(name, id, util::make_unique<FileReference>(table_->string_pool.MakeRef(path)));
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name,
+                                                             const StringPiece& path,
+                                                             const ConfigDescription& config) {
+  return AddValue(name, config, {},
+                  util::make_unique<FileReference>(table_->string_pool.MakeRef(path)));
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddValue(const StringPiece& name,
+                                                     std::unique_ptr<Value> value) {
+  return AddValue(name, {}, std::move(value));
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddValue(const StringPiece& name, const ResourceId& id,
+                                                     std::unique_ptr<Value> value) {
+  return AddValue(name, {}, id, std::move(value));
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddValue(const StringPiece& name,
+                                                     const ConfigDescription& config,
+                                                     const ResourceId& id,
+                                                     std::unique_ptr<Value> value) {
+  ResourceName res_name = ParseNameOrDie(name);
+  CHECK(table_->AddResourceAllowMangled(res_name, id, config, {}, std::move(value),
+                                        GetDiagnostics()));
+  return *this;
+}
+
+ResourceTableBuilder& ResourceTableBuilder::SetSymbolState(const StringPiece& name,
+                                                           const ResourceId& id, SymbolState state,
+                                                           bool allow_new) {
+  ResourceName res_name = ParseNameOrDie(name);
+  Symbol symbol;
+  symbol.state = state;
+  symbol.allow_new = allow_new;
+  CHECK(table_->SetSymbolStateAllowMangled(res_name, id, symbol, GetDiagnostics()));
+  return *this;
+}
+
+StringPool* ResourceTableBuilder::string_pool() {
+  return &table_->string_pool;
+}
+
+std::unique_ptr<ResourceTable> ResourceTableBuilder::Build() {
+  return std::move(table_);
+}
+
+std::unique_ptr<Reference> BuildReference(const StringPiece& ref, const Maybe<ResourceId>& id) {
+  std::unique_ptr<Reference> reference = util::make_unique<Reference>(ParseNameOrDie(ref));
+  reference->id = id;
+  return reference;
+}
+
+std::unique_ptr<BinaryPrimitive> BuildPrimitive(uint8_t type, uint32_t data) {
+  android::Res_value value = {};
+  value.size = sizeof(value);
+  value.dataType = type;
+  value.data = data;
+  return util::make_unique<BinaryPrimitive>(value);
+}
+
+AttributeBuilder::AttributeBuilder(bool weak) : attr_(util::make_unique<Attribute>(weak)) {
+  attr_->type_mask = android::ResTable_map::TYPE_ANY;
+}
+
+AttributeBuilder& AttributeBuilder::SetTypeMask(uint32_t typeMask) {
+  attr_->type_mask = typeMask;
+  return *this;
+}
+
+AttributeBuilder& AttributeBuilder::AddItem(const StringPiece& name, uint32_t value) {
+  attr_->symbols.push_back(
+      Attribute::Symbol{Reference(ResourceName({}, ResourceType::kId, name)), value});
+  return *this;
+}
+
+std::unique_ptr<Attribute> AttributeBuilder::Build() {
+  return std::move(attr_);
+}
+
+StyleBuilder& StyleBuilder::SetParent(const StringPiece& str) {
+  style_->parent = Reference(ParseNameOrDie(str));
+  return *this;
+}
+
+StyleBuilder& StyleBuilder::AddItem(const StringPiece& str, std::unique_ptr<Item> value) {
+  style_->entries.push_back(Style::Entry{Reference(ParseNameOrDie(str)), std::move(value)});
+  return *this;
+}
+
+StyleBuilder& StyleBuilder::AddItem(const StringPiece& str, const ResourceId& id,
+                                    std::unique_ptr<Item> value) {
+  AddItem(str, std::move(value));
+  style_->entries.back().key.id = id;
+  return *this;
+}
+
+std::unique_ptr<Style> StyleBuilder::Build() {
+  return std::move(style_);
+}
+
+StyleableBuilder& StyleableBuilder::AddItem(const StringPiece& str, const Maybe<ResourceId>& id) {
+  styleable_->entries.push_back(Reference(ParseNameOrDie(str)));
+  styleable_->entries.back().id = id;
+  return *this;
+}
+
+std::unique_ptr<Styleable> StyleableBuilder::Build() {
+  return std::move(styleable_);
+}
+
+std::unique_ptr<xml::XmlResource> BuildXmlDom(const StringPiece& str) {
+  std::string input = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+  input.append(str.data(), str.size());
+  StringInputStream in(input);
+  StdErrDiagnostics diag;
+  std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, &diag, Source("test.xml"));
+  CHECK(doc != nullptr) << "failed to parse inline XML string";
+  return doc;
+}
+
+std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(IAaptContext* context,
+                                                            const StringPiece& str) {
+  std::unique_ptr<xml::XmlResource> doc = BuildXmlDom(str);
+  doc->file.name.package = context->GetCompilationPackage();
+  return doc;
+}
+
+}  // namespace test
+}  // namespace aapt
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 6b82076..d9f3912 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -19,13 +19,13 @@
 
 #include <memory>
 
-#include "android-base/logging.h"
 #include "android-base/macros.h"
 
+#include "Resource.h"
 #include "ResourceTable.h"
 #include "ResourceValues.h"
-#include "test/Common.h"
-#include "util/Util.h"
+#include "process/IResourceTableConsumer.h"
+#include "util/Maybe.h"
 #include "xml/XmlDom.h"
 
 namespace aapt {
@@ -35,97 +35,37 @@
  public:
   ResourceTableBuilder() = default;
 
-  StringPool* string_pool() { return &table_->string_pool; }
-
-  ResourceTableBuilder& SetPackageId(const android::StringPiece& package_name, uint8_t id) {
-    ResourceTablePackage* package = table_->CreatePackage(package_name, id);
-    CHECK(package != nullptr);
-    return *this;
-  }
-
-  ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ResourceId& id = {}) {
-    return AddValue(name, id, util::make_unique<Id>());
-  }
-
+  ResourceTableBuilder& SetPackageId(const android::StringPiece& package_name, uint8_t id);
+  ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ResourceId& id = {});
   ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ConfigDescription& config,
-                                  const ResourceId& id = {}) {
-    return AddValue(name, config, id, util::make_unique<Id>());
-  }
-
+                                  const ResourceId& id = {});
   ResourceTableBuilder& AddReference(const android::StringPiece& name,
-                                     const android::StringPiece& ref) {
-    return AddReference(name, {}, ref);
-  }
-
+                                     const android::StringPiece& ref);
   ResourceTableBuilder& AddReference(const android::StringPiece& name, const ResourceId& id,
-                                     const android::StringPiece& ref) {
-    return AddValue(name, id, util::make_unique<Reference>(ParseNameOrDie(ref)));
-  }
-
+                                     const android::StringPiece& ref);
   ResourceTableBuilder& AddString(const android::StringPiece& name,
-                                  const android::StringPiece& str) {
-    return AddString(name, {}, str);
-  }
-
+                                  const android::StringPiece& str);
   ResourceTableBuilder& AddString(const android::StringPiece& name, const ResourceId& id,
-                                  const android::StringPiece& str) {
-    return AddValue(
-        name, id, util::make_unique<String>(table_->string_pool.MakeRef(str)));
-  }
-
+                                  const android::StringPiece& str);
   ResourceTableBuilder& AddString(const android::StringPiece& name, const ResourceId& id,
-                                  const ConfigDescription& config,
-                                  const android::StringPiece& str) {
-    return AddValue(name, config, id, util::make_unique<String>(
-                                          table_->string_pool.MakeRef(str)));
-  }
-
+                                  const ConfigDescription& config, const android::StringPiece& str);
   ResourceTableBuilder& AddFileReference(const android::StringPiece& name,
-                                         const android::StringPiece& path) {
-    return AddFileReference(name, {}, path);
-  }
-
+                                         const android::StringPiece& path);
   ResourceTableBuilder& AddFileReference(const android::StringPiece& name, const ResourceId& id,
-                                         const android::StringPiece& path) {
-    return AddValue(name, id, util::make_unique<FileReference>(
-                                  table_->string_pool.MakeRef(path)));
-  }
-
+                                         const android::StringPiece& path);
   ResourceTableBuilder& AddFileReference(const android::StringPiece& name,
                                          const android::StringPiece& path,
-                                         const ConfigDescription& config) {
-    return AddValue(name, config, {}, util::make_unique<FileReference>(
-                                          table_->string_pool.MakeRef(path)));
-  }
-
-  ResourceTableBuilder& AddValue(const android::StringPiece& name, std::unique_ptr<Value> value) {
-    return AddValue(name, {}, std::move(value));
-  }
-
+                                         const ConfigDescription& config);
+  ResourceTableBuilder& AddValue(const android::StringPiece& name, std::unique_ptr<Value> value);
   ResourceTableBuilder& AddValue(const android::StringPiece& name, const ResourceId& id,
-                                 std::unique_ptr<Value> value) {
-    return AddValue(name, {}, id, std::move(value));
-  }
-
+                                 std::unique_ptr<Value> value);
   ResourceTableBuilder& AddValue(const android::StringPiece& name, const ConfigDescription& config,
-                                 const ResourceId& id, std::unique_ptr<Value> value) {
-    ResourceName res_name = ParseNameOrDie(name);
-    CHECK(table_->AddResourceAllowMangled(res_name, id, config, {}, std::move(value),
-                                          GetDiagnostics()));
-    return *this;
-  }
-
+                                 const ResourceId& id, std::unique_ptr<Value> value);
   ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
-                                       SymbolState state, bool allow_new = false) {
-    ResourceName res_name = ParseNameOrDie(name);
-    Symbol symbol;
-    symbol.state = state;
-    symbol.allow_new = allow_new;
-    CHECK(table_->SetSymbolStateAllowMangled(res_name, id, symbol, GetDiagnostics()));
-    return *this;
-  }
+                                       SymbolState state, bool allow_new = false);
 
-  std::unique_ptr<ResourceTable> Build() { return std::move(table_); }
+  StringPool* string_pool();
+  std::unique_ptr<ResourceTable> Build();
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ResourceTableBuilder);
@@ -133,29 +73,16 @@
   std::unique_ptr<ResourceTable> table_ = util::make_unique<ResourceTable>();
 };
 
-inline std::unique_ptr<Reference> BuildReference(const android::StringPiece& ref,
-                                                 const Maybe<ResourceId>& id = {}) {
-  std::unique_ptr<Reference> reference =
-      util::make_unique<Reference>(ParseNameOrDie(ref));
-  reference->id = id;
-  return reference;
-}
-
-inline std::unique_ptr<BinaryPrimitive> BuildPrimitive(uint8_t type,
-                                                       uint32_t data) {
-  android::Res_value value = {};
-  value.size = sizeof(value);
-  value.dataType = type;
-  value.data = data;
-  return util::make_unique<BinaryPrimitive>(value);
-}
+std::unique_ptr<Reference> BuildReference(const android::StringPiece& ref,
+                                          const Maybe<ResourceId>& id = {});
+std::unique_ptr<BinaryPrimitive> BuildPrimitive(uint8_t type, uint32_t data);
 
 template <typename T>
 class ValueBuilder {
  public:
   template <typename... Args>
-  explicit ValueBuilder(Args&&... args)
-      : value_(new T{std::forward<Args>(args)...}) {}
+  explicit ValueBuilder(Args&&... args) : value_(new T{std::forward<Args>(args)...}) {
+  }
 
   template <typename... Args>
   ValueBuilder& SetSource(Args&&... args) {
@@ -168,7 +95,9 @@
     return *this;
   }
 
-  std::unique_ptr<Value> Build() { return std::move(value_); }
+  std::unique_ptr<Value> Build() {
+    return std::move(value_);
+  }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ValueBuilder);
@@ -178,23 +107,10 @@
 
 class AttributeBuilder {
  public:
-  explicit AttributeBuilder(bool weak = false)
-      : attr_(util::make_unique<Attribute>(weak)) {
-    attr_->type_mask = android::ResTable_map::TYPE_ANY;
-  }
-
-  AttributeBuilder& SetTypeMask(uint32_t typeMask) {
-    attr_->type_mask = typeMask;
-    return *this;
-  }
-
-  AttributeBuilder& AddItem(const android::StringPiece& name, uint32_t value) {
-    attr_->symbols.push_back(Attribute::Symbol{
-        Reference(ResourceName({}, ResourceType::kId, name)), value});
-    return *this;
-  }
-
-  std::unique_ptr<Attribute> Build() { return std::move(attr_); }
+  explicit AttributeBuilder(bool weak = false);
+  AttributeBuilder& SetTypeMask(uint32_t typeMask);
+  AttributeBuilder& AddItem(const android::StringPiece& name, uint32_t value);
+  std::unique_ptr<Attribute> Build();
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AttributeBuilder);
@@ -205,27 +121,11 @@
 class StyleBuilder {
  public:
   StyleBuilder() = default;
-
-  StyleBuilder& SetParent(const android::StringPiece& str) {
-    style_->parent = Reference(ParseNameOrDie(str));
-    return *this;
-  }
-
-  StyleBuilder& AddItem(const android::StringPiece& str, std::unique_ptr<Item> value) {
-    style_->entries.push_back(Style::Entry{Reference(ParseNameOrDie(str)), std::move(value)});
-    return *this;
-  }
-
+  StyleBuilder& SetParent(const android::StringPiece& str);
+  StyleBuilder& AddItem(const android::StringPiece& str, std::unique_ptr<Item> value);
   StyleBuilder& AddItem(const android::StringPiece& str, const ResourceId& id,
-                        std::unique_ptr<Item> value) {
-    AddItem(str, std::move(value));
-    style_->entries.back().key.id = id;
-    return *this;
-  }
-
-  std::unique_ptr<Style> Build() {
-    return std::move(style_);
-  }
+                        std::unique_ptr<Item> value);
+  std::unique_ptr<Style> Build();
 
  private:
   DISALLOW_COPY_AND_ASSIGN(StyleBuilder);
@@ -236,14 +136,8 @@
 class StyleableBuilder {
  public:
   StyleableBuilder() = default;
-
-  StyleableBuilder& AddItem(const android::StringPiece& str, const Maybe<ResourceId>& id = {}) {
-    styleable_->entries.push_back(Reference(ParseNameOrDie(str)));
-    styleable_->entries.back().id = id;
-    return *this;
-  }
-
-  std::unique_ptr<Styleable> Build() { return std::move(styleable_); }
+  StyleableBuilder& AddItem(const android::StringPiece& str, const Maybe<ResourceId>& id = {});
+  std::unique_ptr<Styleable> Build();
 
  private:
   DISALLOW_COPY_AND_ASSIGN(StyleableBuilder);
@@ -251,22 +145,9 @@
   std::unique_ptr<Styleable> styleable_ = util::make_unique<Styleable>();
 };
 
-inline std::unique_ptr<xml::XmlResource> BuildXmlDom(const android::StringPiece& str) {
-  std::stringstream in;
-  in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
-  StdErrDiagnostics diag;
-  std::unique_ptr<xml::XmlResource> doc =
-      xml::Inflate(&in, &diag, Source("test.xml"));
-  CHECK(doc != nullptr) << "failed to parse inline XML string";
-  return doc;
-}
-
-inline std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(
-    IAaptContext* context, const android::StringPiece& str) {
-  std::unique_ptr<xml::XmlResource> doc = BuildXmlDom(str);
-  doc->file.name.package = context->GetCompilationPackage();
-  return doc;
-}
+std::unique_ptr<xml::XmlResource> BuildXmlDom(const android::StringPiece& str);
+std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(IAaptContext* context,
+                                                            const android::StringPiece& str);
 
 }  // namespace test
 }  // namespace aapt
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 1bf2594..6f97efe 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -27,6 +27,8 @@
 #include "android-base/errors.h"
 #include "android-base/file.h"
 #include "android-base/logging.h"
+#include "android-base/unique_fd.h"
+#include "android-base/utf8.h"
 
 #include "util/Util.h"
 
@@ -35,14 +37,32 @@
 #include <direct.h>
 #endif
 
-using android::StringPiece;
+using ::android::FileMap;
+using ::android::StringPiece;
+using ::android::base::ReadFileToString;
+using ::android::base::SystemErrorCodeToString;
+using ::android::base::unique_fd;
 
 namespace aapt {
 namespace file {
 
-FileType GetFileType(const StringPiece& path) {
+FileType GetFileType(const std::string& path) {
+// TODO(adamlesinski): I'd like to move this to ::android::base::utf8 but Windows does some macro
+// trickery with 'stat' and things don't override very well.
+#ifdef _WIN32
+  std::wstring path_utf16;
+  if (!::android::base::UTF8PathToWindowsLongPath(path.c_str(), &path_utf16)) {
+    return FileType::kNonexistant;
+  }
+
+  struct _stat64 sb;
+  int result = _wstat64(path_utf16.c_str(), &sb);
+#else
   struct stat sb;
-  if (stat(path.data(), &sb) < 0) {
+  int result = stat(path.c_str(), &sb);
+#endif
+
+  if (result == -1) {
     if (errno == ENOENT || errno == ENOTDIR) {
       return FileType::kNonexistant;
     }
@@ -72,27 +92,18 @@
   }
 }
 
-inline static int MkdirImpl(const StringPiece& path) {
-#ifdef _WIN32
-  return _mkdir(path.to_string().c_str());
-#else
-  return mkdir(path.to_string().c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP);
-#endif
-}
-
-bool mkdirs(const StringPiece& path) {
-  const char* start = path.begin();
-  const char* end = path.end();
-  for (const char* current = start; current != end; ++current) {
-    if (*current == sDirSep && current != start) {
-      StringPiece parent_path(start, current - start);
-      int result = MkdirImpl(parent_path);
-      if (result < 0 && errno != EEXIST) {
-        return false;
-      }
+bool mkdirs(const std::string& path) {
+  constexpr const mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP;
+  size_t current_pos = 0u;
+  while ((current_pos = path.find(sDirSep, current_pos)) != std::string::npos) {
+    std::string parent_path = path.substr(0, current_pos);
+    int result = ::android::base::utf8::mkdir(parent_path.c_str(), mode);
+    if (result < 0 && errno != EEXIST) {
+      return false;
     }
+    current_pos += 1;
   }
-  return MkdirImpl(path) == 0 || errno == EEXIST;
+  return ::android::base::utf8::mkdir(path.c_str(), mode) == 0 || errno == EEXIST;
 }
 
 StringPiece GetStem(const StringPiece& path) {
@@ -129,10 +140,8 @@
 
 void AppendPath(std::string* base, StringPiece part) {
   CHECK(base != nullptr);
-  const bool base_has_trailing_sep =
-      (!base->empty() && *(base->end() - 1) == sDirSep);
-  const bool part_has_leading_sep =
-      (!part.empty() && *(part.begin()) == sDirSep);
+  const bool base_has_trailing_sep = (!base->empty() && *(base->end() - 1) == sDirSep);
+  const bool part_has_leading_sep = (!part.empty() && *(part.begin()) == sDirSep);
   if (base_has_trailing_sep && part_has_leading_sep) {
     // Remove the part's leading sep
     part = part.substr(1, part.size() - 1);
@@ -151,31 +160,34 @@
   return out_path;
 }
 
-Maybe<android::FileMap> MmapPath(const StringPiece& path,
-                                 std::string* out_error) {
-  std::unique_ptr<FILE, decltype(fclose)*> f = {fopen(path.data(), "rb"),
-                                                fclose};
-  if (!f) {
-    if (out_error) *out_error = android::base::SystemErrorCodeToString(errno);
+Maybe<FileMap> MmapPath(const std::string& path, std::string* out_error) {
+  int flags = O_RDONLY | O_CLOEXEC | O_BINARY;
+  unique_fd fd(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), flags)));
+  if (fd == -1) {
+    if (out_error) {
+      *out_error = SystemErrorCodeToString(errno);
+    }
     return {};
   }
 
-  int fd = fileno(f.get());
-
   struct stat filestats = {};
   if (fstat(fd, &filestats) != 0) {
-    if (out_error) *out_error = android::base::SystemErrorCodeToString(errno);
+    if (out_error) {
+      *out_error = SystemErrorCodeToString(errno);
+    }
     return {};
   }
 
-  android::FileMap filemap;
+  FileMap filemap;
   if (filestats.st_size == 0) {
     // mmap doesn't like a length of 0. Instead we return an empty FileMap.
     return std::move(filemap);
   }
 
-  if (!filemap.create(path.data(), fd, 0, filestats.st_size, true)) {
-    if (out_error) *out_error = android::base::SystemErrorCodeToString(errno);
+  if (!filemap.create(path.c_str(), fd, 0, filestats.st_size, true)) {
+    if (out_error) {
+      *out_error = SystemErrorCodeToString(errno);
+    }
     return {};
   }
   return std::move(filemap);
@@ -184,7 +196,7 @@
 bool AppendArgsFromFile(const StringPiece& path, std::vector<std::string>* out_arglist,
                         std::string* out_error) {
   std::string contents;
-  if (!android::base::ReadFileToString(path.to_string(), &contents, true /*follow_symlinks*/)) {
+  if (!ReadFileToString(path.to_string(), &contents, true /*follow_symlinks*/)) {
     if (out_error) {
       *out_error = "failed to read argument-list file";
     }
@@ -270,7 +282,7 @@
   const std::string root_dir = path.to_string();
   std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
   if (!d) {
-    diag->Error(DiagMessage() << android::base::SystemErrorCodeToString(errno));
+    diag->Error(DiagMessage() << SystemErrorCodeToString(errno));
     return {};
   }
 
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index b3b1e48..b6aa410 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -50,79 +50,56 @@
   kSocket,
 };
 
-FileType GetFileType(const android::StringPiece& path);
+FileType GetFileType(const std::string& path);
 
-/*
- * Appends a path to `base`, separated by the directory separator.
- */
+// Appends a path to `base`, separated by the directory separator.
 void AppendPath(std::string* base, android::StringPiece part);
 
-/*
- * Makes all the directories in `path`. The last element in the path
- * is interpreted as a directory.
- */
-bool mkdirs(const android::StringPiece& path);
+// Makes all the directories in `path`. The last element in the path is interpreted as a directory.
+bool mkdirs(const std::string& path);
 
-/**
- * Returns all but the last part of the path.
- */
+// Returns all but the last part of the path.
 android::StringPiece GetStem(const android::StringPiece& path);
 
-/**
- * Returns the last part of the path with extension.
- */
+// Returns the last part of the path with extension.
 android::StringPiece GetFilename(const android::StringPiece& path);
 
-/**
- * Returns the extension of the path. This is the entire string after
- * the first '.' of the last part of the path.
- */
+// Returns the extension of the path. This is the entire string after the first '.' of the last part
+// of the path.
 android::StringPiece GetExtension(const android::StringPiece& path);
 
-/**
- * Converts a package name (com.android.app) to a path: com/android/app
- */
+// Converts a package name (com.android.app) to a path: com/android/app
 std::string PackageToPath(const android::StringPiece& package);
 
-/**
- * Creates a FileMap for the file at path.
- */
-Maybe<android::FileMap> MmapPath(const android::StringPiece& path, std::string* out_error);
+// Creates a FileMap for the file at path.
+Maybe<android::FileMap> MmapPath(const std::string& path, std::string* out_error);
 
-/**
- * Reads the file at path and appends each line to the outArgList vector.
- */
+// Reads the file at path and appends each line to the outArgList vector.
 bool AppendArgsFromFile(const android::StringPiece& path, std::vector<std::string>* out_arglist,
                         std::string* out_error);
 
-/*
- * Filter that determines which resource files/directories are
- * processed by AAPT. Takes a pattern string supplied by the user.
- * Pattern format is specified in the FileFilter::SetPattern() method.
- */
+// Filter that determines which resource files/directories are
+// processed by AAPT. Takes a pattern string supplied by the user.
+// Pattern format is specified in the FileFilter::SetPattern() method.
 class FileFilter {
  public:
   explicit FileFilter(IDiagnostics* diag) : diag_(diag) {}
 
-  /*
-   * Patterns syntax:
-   * - Delimiter is :
-   * - Entry can start with the flag ! to avoid printing a warning
-   *   about the file being ignored.
-   * - Entry can have the flag "<dir>" to match only directories
-   *   or <file> to match only files. Default is to match both.
-   * - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
-   *   where prefix/suffix must have at least 1 character (so that
-   *   we don't match a '*' catch-all pattern.)
-   * - The special filenames "." and ".." are always ignored.
-   * - Otherwise the full string is matched.
-   * - match is not case-sensitive.
-   */
+  // Patterns syntax:
+  // - Delimiter is :
+  // - Entry can start with the flag ! to avoid printing a warning
+  //   about the file being ignored.
+  // - Entry can have the flag "<dir>" to match only directories
+  //   or <file> to match only files. Default is to match both.
+  // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
+  //   where prefix/suffix must have at least 1 character (so that
+  //   we don't match a '*' catch-all pattern.)
+  // - The special filenames "." and ".." are always ignored.
+  // - Otherwise the full string is matched.
+  // - match is not case-sensitive.
   bool SetPattern(const android::StringPiece& pattern);
 
-  /**
-   * Applies the filter, returning true for pass, false for fail.
-   */
+  // Applies the filter, returning true for pass, false for fail.
   bool operator()(const std::string& filename, FileType type) const;
 
  private:
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 885ab3e..d6df715 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -29,8 +29,9 @@
 #include "XmlPullParser.h"
 #include "util/Util.h"
 
-using android::StringPiece;
-using android::StringPiece16;
+using ::aapt::io::InputStream;
+using ::android::StringPiece;
+using ::android::StringPiece16;
 
 namespace aapt {
 namespace xml {
@@ -189,40 +190,41 @@
   stack->pending_comment += comment;
 }
 
-std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag, const Source& source) {
+std::unique_ptr<XmlResource> Inflate(InputStream* in, IDiagnostics* diag, const Source& source) {
   Stack stack;
 
-  XML_Parser parser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
-  XML_SetUserData(parser, &stack);
-  XML_UseParserAsHandlerArg(parser);
-  XML_SetElementHandler(parser, StartElementHandler, EndElementHandler);
-  XML_SetNamespaceDeclHandler(parser, StartNamespaceHandler, EndNamespaceHandler);
-  XML_SetCharacterDataHandler(parser, CharacterDataHandler);
-  XML_SetCommentHandler(parser, CommentDataHandler);
+  std::unique_ptr<std::remove_pointer<XML_Parser>::type, decltype(XML_ParserFree)*> parser = {
+      XML_ParserCreateNS(nullptr, kXmlNamespaceSep), XML_ParserFree};
+  XML_SetUserData(parser.get(), &stack);
+  XML_UseParserAsHandlerArg(parser.get());
+  XML_SetElementHandler(parser.get(), StartElementHandler, EndElementHandler);
+  XML_SetNamespaceDeclHandler(parser.get(), StartNamespaceHandler, EndNamespaceHandler);
+  XML_SetCharacterDataHandler(parser.get(), CharacterDataHandler);
+  XML_SetCommentHandler(parser.get(), CommentDataHandler);
 
-  char buffer[1024];
-  while (!in->eof()) {
-    in->read(buffer, sizeof(buffer) / sizeof(buffer[0]));
-    if (in->bad() && !in->eof()) {
-      stack.root = {};
-      diag->Error(DiagMessage(source) << strerror(errno));
-      break;
-    }
-
-    if (XML_Parse(parser, buffer, in->gcount(), in->eof()) == XML_STATUS_ERROR) {
-      stack.root = {};
-      diag->Error(DiagMessage(source.WithLine(XML_GetCurrentLineNumber(parser)))
-                  << XML_ErrorString(XML_GetErrorCode(parser)));
-      break;
+  const char* buffer = nullptr;
+  size_t buffer_size = 0;
+  while (in->Next(reinterpret_cast<const void**>(&buffer), &buffer_size)) {
+    if (XML_Parse(parser.get(), buffer, buffer_size, false) == XML_STATUS_ERROR) {
+      diag->Error(DiagMessage(source.WithLine(XML_GetCurrentLineNumber(parser.get())))
+                  << XML_ErrorString(XML_GetErrorCode(parser.get())));
+      return {};
     }
   }
 
-  XML_ParserFree(parser);
-  if (stack.root) {
-    return util::make_unique<XmlResource>(ResourceFile{{}, {}, source}, StringPool{},
-                                          std::move(stack.root));
+  if (in->HadError()) {
+    diag->Error(DiagMessage(source) << in->GetError());
+    return {};
+  } else {
+    // Finish off the parsing.
+    if (XML_Parse(parser.get(), nullptr, 0u, true) == XML_STATUS_ERROR) {
+      diag->Error(DiagMessage(source.WithLine(XML_GetCurrentLineNumber(parser.get())))
+                  << XML_ErrorString(XML_GetErrorCode(parser.get())));
+      return {};
+    }
   }
-  return {};
+  return util::make_unique<XmlResource>(ResourceFile{{}, {}, source}, StringPool{},
+                                        std::move(stack.root));
 }
 
 static void CopyAttributes(Element* el, android::ResXMLParser* parser, StringPool* out_pool) {
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index 2dc99d6..54a7033 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -17,7 +17,6 @@
 #ifndef AAPT_XML_DOM_H
 #define AAPT_XML_DOM_H
 
-#include <istream>
 #include <memory>
 #include <string>
 #include <vector>
@@ -27,6 +26,7 @@
 #include "Diagnostics.h"
 #include "Resource.h"
 #include "ResourceValues.h"
+#include "io/Io.h"
 #include "util/Util.h"
 #include "xml/XmlUtil.h"
 
@@ -37,9 +37,7 @@
 
 class Element;
 
-/**
- * Base class for all XML nodes.
- */
+// Base class for all XML nodes.
 class Node {
  public:
   Node* parent = nullptr;
@@ -60,19 +58,14 @@
   virtual std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) = 0;
 };
 
-/**
- * Base class that implements the visitor methods for a
- * subclass of Node.
- */
+// Base class that implements the visitor methods for a subclass of Node.
 template <typename Derived>
 class BaseNode : public Node {
  public:
   virtual void Accept(RawVisitor* visitor) override;
 };
 
-/**
- * A Namespace XML node. Can only have one child.
- */
+// A Namespace XML node. Can only have one child.
 class Namespace : public BaseNode<Namespace> {
  public:
   std::string namespace_prefix;
@@ -90,9 +83,7 @@
   Maybe<ResourceId> id;
 };
 
-/**
- * An XML attribute.
- */
+// An XML attribute.
 struct Attribute {
   std::string namespace_uri;
   std::string name;
@@ -102,9 +93,7 @@
   std::unique_ptr<Item> compiled_value;
 };
 
-/**
- * An Element XML node.
- */
+// An Element XML node.
 class Element : public BaseNode<Element> {
  public:
   std::string namespace_uri;
@@ -124,9 +113,7 @@
   std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) override;
 };
 
-/**
- * A Text (CDATA) XML node. Can not have any children.
- */
+// A Text (CDATA) XML node. Can not have any children.
 class Text : public BaseNode<Text> {
  public:
   std::string text;
@@ -134,9 +121,7 @@
   std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) override;
 };
 
-/**
- * An XML resource with a source, name, and XML tree.
- */
+// An XML resource with a source, name, and XML tree.
 class XmlResource {
  public:
   ResourceFile file;
@@ -149,27 +134,20 @@
   std::unique_ptr<xml::Node> root;
 };
 
-/**
- * Inflates an XML DOM from a text stream, logging errors to the logger.
- * Returns the root node on success, or nullptr on failure.
- */
-std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag, const Source& source);
+// Inflates an XML DOM from an InputStream, logging errors to the logger.
+// Returns the root node on success, or nullptr on failure.
+std::unique_ptr<XmlResource> Inflate(io::InputStream* in, IDiagnostics* diag, const Source& source);
 
-/**
- * Inflates an XML DOM from a binary ResXMLTree, logging errors to the logger.
- * Returns the root node on success, or nullptr on failure.
- */
+// Inflates an XML DOM from a binary ResXMLTree, logging errors to the logger.
+// Returns the root node on success, or nullptr on failure.
 std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnostics* diag,
                                      const Source& source);
 
 Element* FindRootElement(XmlResource* doc);
 Element* FindRootElement(Node* node);
 
-/**
- * A visitor interface for the different XML Node subtypes. This will not
- * traverse into
- * children. Use Visitor for that.
- */
+// A visitor interface for the different XML Node subtypes. This will not traverse into children.
+// Use Visitor for that.
 class RawVisitor {
  public:
   virtual ~RawVisitor() = default;
@@ -179,18 +157,22 @@
   virtual void Visit(Text* text) {}
 };
 
-/**
- * Visitor whose default implementation visits the children nodes of any node.
- */
+// Visitor whose default implementation visits the children nodes of any node.
 class Visitor : public RawVisitor {
  public:
   using RawVisitor::Visit;
 
-  void Visit(Namespace* node) override { VisitChildren(node); }
+  void Visit(Namespace* node) override {
+    VisitChildren(node);
+  }
 
-  void Visit(Element* node) override { VisitChildren(node); }
+  void Visit(Element* node) override {
+    VisitChildren(node);
+  }
 
-  void Visit(Text* text) override { VisitChildren(text); }
+  void Visit(Text* text) override {
+    VisitChildren(text);
+  }
 
   void VisitChildren(Node* node) {
     for (auto& child : node->children) {
@@ -199,9 +181,7 @@
   }
 };
 
-/**
- * An XML DOM visitor that will record the package name for a namespace prefix.
- */
+// An XML DOM visitor that will record the package name for a namespace prefix.
 class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
  public:
   using Visitor::Visit;
@@ -233,7 +213,9 @@
 
   T* value = nullptr;
 
-  void Visit(T* v) override { value = v; }
+  void Visit(T* v) override {
+    value = v;
+  }
 };
 
 template <typename T>
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index f0122e8..1340ada 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -16,23 +16,20 @@
 
 #include "xml/XmlDom.h"
 
-#include <sstream>
 #include <string>
 
+#include "io/StringInputStream.h"
 #include "test/Test.h"
 
+using ::aapt::io::StringInputStream;
 using ::testing::Eq;
 using ::testing::NotNull;
 using ::testing::SizeIs;
 
 namespace aapt {
 
-constexpr const char* kXmlPreamble =
-    "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
-
 TEST(XmlDomTest, Inflate) {
-  std::stringstream in(kXmlPreamble);
-  in << R"(
+  std::string input = R"(<?xml version="1.0" encoding="utf-8"?>
       <Layout xmlns:android="http://schemas.android.com/apk/res/android"
           android:layout_width="match_parent"
           android:layout_height="wrap_content">
@@ -41,9 +38,9 @@
             android:layout_height="wrap_content" />
       </Layout>)";
 
-  const Source source("test.xml");
   StdErrDiagnostics diag;
-  std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, &diag, source);
+  StringInputStream in(input);
+  std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, &diag, Source("test.xml"));
   ASSERT_THAT(doc, NotNull());
 
   xml::Namespace* ns = xml::NodeCast<xml::Namespace>(doc->root.get());
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index c2a9c82..30bdc50 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -22,14 +22,15 @@
 #include "xml/XmlPullParser.h"
 #include "xml/XmlUtil.h"
 
-using android::StringPiece;
+using ::aapt::io::InputStream;
+using ::android::StringPiece;
 
 namespace aapt {
 namespace xml {
 
 constexpr char kXmlNamespaceSep = 1;
 
-XmlPullParser::XmlPullParser(std::istream& in) : in_(in), empty_(), depth_(0) {
+XmlPullParser::XmlPullParser(InputStream* in) : in_(in), empty_(), depth_(0) {
   parser_ = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
   XML_SetUserData(parser_, this);
   XML_SetElementHandler(parser_, StartElementHandler, EndElementHandler);
@@ -40,30 +41,35 @@
   event_queue_.push(EventData{Event::kStartDocument, 0, depth_++});
 }
 
-XmlPullParser::~XmlPullParser() { XML_ParserFree(parser_); }
+XmlPullParser::~XmlPullParser() {
+  XML_ParserFree(parser_);
+}
 
 XmlPullParser::Event XmlPullParser::Next() {
   const Event currentEvent = event();
-  if (currentEvent == Event::kBadDocument ||
-      currentEvent == Event::kEndDocument) {
+  if (currentEvent == Event::kBadDocument || currentEvent == Event::kEndDocument) {
     return currentEvent;
   }
 
   event_queue_.pop();
   while (event_queue_.empty()) {
-    in_.read(buffer_, sizeof(buffer_) / sizeof(*buffer_));
+    const char* buffer = nullptr;
+    size_t buffer_size = 0;
+    bool done = false;
+    if (!in_->Next(reinterpret_cast<const void**>(&buffer), &buffer_size)) {
+      if (in_->HadError()) {
+        error_ = in_->GetError();
+        event_queue_.push(EventData{Event::kBadDocument});
+        break;
+      }
 
-    const bool done = in_.eof();
-    if (in_.bad() && !done) {
-      error_ = strerror(errno);
-      event_queue_.push(EventData{Event::kBadDocument});
-      continue;
+      done = true;
     }
 
-    if (XML_Parse(parser_, buffer_, in_.gcount(), done) == XML_STATUS_ERROR) {
+    if (XML_Parse(parser_, buffer, buffer_size, done) == XML_STATUS_ERROR) {
       error_ = XML_ErrorString(XML_GetErrorCode(parser_));
       event_queue_.push(EventData{Event::kBadDocument});
-      continue;
+      break;
     }
 
     if (done) {
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index cdeeefd..a00caa1 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -31,6 +31,7 @@
 #include "androidfw/StringPiece.h"
 
 #include "Resource.h"
+#include "io/Io.h"
 #include "process/IResourceTableConsumer.h"
 #include "util/Maybe.h"
 #include "xml/XmlUtil.h"
@@ -64,7 +65,7 @@
   static bool SkipCurrentElement(XmlPullParser* parser);
   static bool IsGoodEvent(Event event);
 
-  explicit XmlPullParser(std::istream& in);
+  explicit XmlPullParser(io::InputStream* in);
   ~XmlPullParser();
 
   /**
@@ -169,9 +170,8 @@
     std::vector<Attribute> attributes;
   };
 
-  std::istream& in_;
+  io::InputStream* in_;
   XML_Parser parser_;
-  char buffer_[16384];
   std::queue<EventData> event_queue_;
   std::string error_;
   const std::string empty_;
@@ -228,18 +228,15 @@
   return out;
 }
 
-inline bool XmlPullParser::NextChildNode(XmlPullParser* parser,
-                                         size_t start_depth) {
+inline bool XmlPullParser::NextChildNode(XmlPullParser* parser, size_t start_depth) {
   Event event;
 
   // First get back to the start depth.
-  while (IsGoodEvent(event = parser->Next()) &&
-         parser->depth() > start_depth + 1) {
+  while (IsGoodEvent(event = parser->Next()) && parser->depth() > start_depth + 1) {
   }
 
   // Now look for the first good node.
-  while ((event != Event::kEndElement || parser->depth() > start_depth) &&
-         IsGoodEvent(event)) {
+  while ((event != Event::kEndElement || parser->depth() > start_depth) && IsGoodEvent(event)) {
     switch (event) {
       case Event::kText:
       case Event::kComment:
diff --git a/tools/aapt2/xml/XmlPullParser_test.cpp b/tools/aapt2/xml/XmlPullParser_test.cpp
index 1cce485..681d9d4 100644
--- a/tools/aapt2/xml/XmlPullParser_test.cpp
+++ b/tools/aapt2/xml/XmlPullParser_test.cpp
@@ -16,21 +16,22 @@
 
 #include "xml/XmlPullParser.h"
 
-#include <sstream>
-
 #include "androidfw/StringPiece.h"
 
+#include "io/StringInputStream.h"
 #include "test/Test.h"
 
-using android::StringPiece;
+using ::aapt::io::StringInputStream;
+using ::android::StringPiece;
 
 namespace aapt {
 
 TEST(XmlPullParserTest, NextChildNodeTraversesCorrectly) {
-  std::stringstream str;
-  str << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
-         "<a><b><c xmlns:a=\"http://schema.org\"><d/></c><e/></b></a>";
-  xml::XmlPullParser parser(str);
+  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);
 
   const size_t depth_outer = parser.depth();
   ASSERT_TRUE(xml::XmlPullParser::NextChildNode(&parser, depth_outer));