AAPT2: Switch to protobuf for intermediate format

Without needing to conform to the runtime data format,
it is much easier to add new features such as debugging symbols
and carrying over product data to link time.

This also simplifies the runtime format parser and serializer,
which will change much less frequently than the protobuf intermediate
format.

Change-Id: I209787bbf087db0a58a534cb8511c51d21133e00
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index f74b93a..88b6270 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -43,6 +43,9 @@
 	link/TableMerger.cpp \
 	link/XmlReferenceLinker.cpp \
 	process/SymbolTable.cpp \
+	proto/ProtoHelpers.cpp \
+	proto/TableProtoDeserializer.cpp \
+	proto/TableProtoSerializer.cpp \
 	unflatten/BinaryResourceParser.cpp \
 	unflatten/ResChunkPullParser.cpp \
 	util/BigBuffer.cpp \
@@ -67,13 +70,14 @@
 	xml/XmlPullParser.cpp \
 	xml/XmlUtil.cpp
 
+sources += Format.proto
+
 testSources := \
 	compile/IdAssigner_test.cpp \
 	compile/PseudolocaleGenerator_test.cpp \
 	compile/Pseudolocalizer_test.cpp \
 	compile/XmlIdCollector_test.cpp \
 	filter/ConfigFilter_test.cpp \
-	flatten/FileExportWriter_test.cpp \
 	flatten/TableFlattener_test.cpp \
 	flatten/XmlFlattener_test.cpp \
 	link/AutoVersioner_test.cpp \
@@ -83,7 +87,7 @@
 	link/TableMerger_test.cpp \
 	link/XmlReferenceLinker_test.cpp \
 	process/SymbolTable_test.cpp \
-	unflatten/FileExportHeaderReader_test.cpp \
+	proto/TableProtoSerializer_test.cpp \
 	util/BigBuffer_test.cpp \
 	util/Maybe_test.cpp \
 	util/StringPiece_test.cpp \
@@ -105,6 +109,7 @@
 
 toolSources := \
 	compile/Compile.cpp \
+	dump/Dump.cpp \
 	link/Link.cpp
 
 hostLdLibs :=
@@ -119,6 +124,9 @@
 	libpng \
 	libbase
 
+hostSharedLibs := \
+	libprotobuf-cpp-lite
+
 ifneq ($(strip $(USE_MINGW)),)
 	hostStaticLibs += libz
 else
@@ -127,21 +135,23 @@
 
 cFlags := -Wall -Werror -Wno-unused-parameter -UNDEBUG
 cppFlags := -std=c++11 -Wno-missing-field-initializers -fno-exceptions -fno-rtti
+protoIncludes := $(call generated-sources-dir-for,STATIC_LIBRARIES,libaapt2,HOST)
 
 # ==========================================================
 # Build the host static library: libaapt2
 # ==========================================================
 include $(CLEAR_VARS)
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
 LOCAL_MODULE := libaapt2
 
 LOCAL_SRC_FILES := $(sources)
 LOCAL_STATIC_LIBRARIES += $(hostStaticLibs)
 LOCAL_CFLAGS += $(cFlags)
 LOCAL_CPPFLAGS += $(cppFlags)
+LOCAL_C_INCLUDES += $(protoIncludes)
 
 include $(BUILD_HOST_STATIC_LIBRARY)
 
-
 # ==========================================================
 # Build the host tests: libaapt2_tests
 # ==========================================================
@@ -152,9 +162,11 @@
 LOCAL_SRC_FILES := $(testSources)
 
 LOCAL_STATIC_LIBRARIES += libaapt2 $(hostStaticLibs)
+LOCAL_SHARED_LIBRARIES += $(hostSharedLibs)
 LOCAL_LDLIBS += $(hostLdLibs)
 LOCAL_CFLAGS += $(cFlags)
 LOCAL_CPPFLAGS += $(cppFlags)
+LOCAL_C_INCLUDES += $(protoIncludes)
 
 include $(BUILD_HOST_NATIVE_TEST)
 
@@ -167,9 +179,11 @@
 LOCAL_SRC_FILES := $(main) $(toolSources)
 
 LOCAL_STATIC_LIBRARIES += libaapt2 $(hostStaticLibs)
+LOCAL_SHARED_LIBRARIES += $(hostSharedLibs)
 LOCAL_LDLIBS += $(hostLdLibs)
 LOCAL_CFLAGS += $(cFlags)
 LOCAL_CPPFLAGS += $(cppFlags)
+LOCAL_C_INCLUDES += $(protoIncludes)
 
 include $(BUILD_HOST_EXECUTABLE)
 
diff --git a/tools/aapt2/Format.proto b/tools/aapt2/Format.proto
new file mode 100644
index 0000000..d05425c
--- /dev/null
+++ b/tools/aapt2/Format.proto
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package aapt.pb;
+
+message ConfigDescription {
+	optional bytes data = 1;
+	optional string product = 2;
+}
+
+message StringPool {
+	optional bytes data = 1;
+}
+
+message CompiledFile {
+	message Symbol {
+		optional string resource_name = 1;
+		optional uint32 line_no = 2;
+	}
+	
+	optional string resource_name = 1;
+	optional ConfigDescription config = 2;
+	optional string source_path = 3;
+	repeated Symbol exported_symbols = 4;
+}
+
+message ResourceTable {
+	optional StringPool string_pool = 1;
+	optional StringPool source_pool = 2;
+	optional StringPool symbol_pool = 3;
+	repeated Package packages = 4;
+}
+
+message Package {
+	optional uint32 package_id = 1;
+	optional string package_name = 2;
+	repeated Type types = 3;
+}
+
+message Type {	
+	optional uint32 id = 1;
+	optional string name = 2;
+	repeated Entry entries = 3;
+}
+
+message SymbolStatus {
+	enum Visibility {
+		Unknown = 0;
+		Private = 1;
+		Public = 2;
+	}
+	optional Visibility visibility = 1;
+	optional Source source = 2;
+	optional string comment = 3;
+}
+
+message Entry {
+	optional uint32 id = 1;
+	optional string name = 2;
+	optional SymbolStatus symbol_status = 3;
+	repeated ConfigValue config_values = 4;
+}
+
+message ConfigValue {
+	optional ConfigDescription config = 1;
+	optional Value value = 2;
+}
+
+message Source {
+	optional uint32 path_idx = 1;
+	optional uint32 line_no = 2;
+	optional uint32 col_no = 3;
+}
+
+message Reference {
+	enum Type {
+		Ref = 0;
+		Attr = 1;
+	}
+	optional Type type = 1;
+	optional uint32 id = 2;
+	optional uint32 symbol_idx = 3;
+	optional bool private = 4;
+}
+
+message Id {
+}
+
+message String {
+	optional uint32 idx = 1;
+}
+
+message RawString {
+	optional uint32 idx = 1;
+}
+
+message FileReference {
+	optional uint32 path_idx = 1;
+}
+
+message Primitive {
+	optional uint32 type = 1;
+	optional uint32 data = 2;
+}
+
+message Attribute {
+	message Symbol {
+		optional Source source = 1;
+		optional string comment = 2;
+		optional Reference name = 3;
+		optional uint32 value = 4;
+	}
+	optional uint32 format_flags = 1;
+	optional int32 min_int = 2;
+	optional int32 max_int = 3;
+	repeated Symbol symbols = 4;
+}
+
+message Style {
+	message Entry {
+		optional Source source = 1;
+		optional string comment = 2;
+		optional Reference key = 3;
+		optional Item item = 4;
+	}
+
+	optional Reference parent = 1;
+	optional Source parent_source = 2;
+	repeated Entry entries = 3;
+}
+
+message Styleable {
+	message Entry {
+		optional Source source = 1;
+		optional string comment = 2;
+		optional Reference attr = 3;
+	}
+	repeated Entry entries = 1;
+}
+
+message Array {
+	message Entry {
+		optional Source source = 1;
+		optional string comment = 2;
+		optional Item item = 3;
+	}
+	repeated Entry entries = 1;
+}
+
+message Plural {
+	enum Arity {
+		Zero = 0;
+		One = 1;
+		Two = 2;
+		Few = 3;
+		Many = 4;
+		Other = 5;
+	}
+		
+	message Entry {
+		optional Source source = 1;
+		optional string comment = 2;
+		optional Arity arity = 3;
+		optional Item item = 4;
+	}
+	repeated Entry entries = 1;
+}
+
+message Item {
+	optional Reference ref = 1;
+	optional String str = 2;
+	optional RawString raw_str = 3;
+	optional FileReference file = 4;
+	optional Id id = 5;
+	optional Primitive prim = 6;
+}
+
+message CompoundValue {
+	optional Attribute attr = 1;
+	optional Style style = 2;
+	optional Styleable styleable = 3;
+	optional Array array = 4;
+	optional Plural plural = 5;
+}
+
+message Value {
+	optional Source source = 1;
+	optional string comment = 2;
+	optional bool weak = 3;
+	
+	optional Item item = 4;
+	optional CompoundValue compound_value = 5;	
+}
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 248e7ad..a2fadd9 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -23,6 +23,7 @@
 
 extern int compile(const std::vector<StringPiece>& args);
 extern int link(const std::vector<StringPiece>& args);
+extern int dump(const std::vector<StringPiece>& args);
 
 } // namespace aapt
 
@@ -41,12 +42,14 @@
             return aapt::compile(args);
         } else if (command == "link" || command == "l") {
             return aapt::link(args);
+        } else if (command == "dump" || command == "d") {
+            return aapt::dump(args);
         }
         std::cerr << "unknown command '" << command << "'\n";
     } else {
         std::cerr << "no command specified\n";
     }
 
-    std::cerr << "\nusage: aapt2 [compile|link] ..." << std::endl;
+    std::cerr << "\nusage: aapt2 [compile|link|dump] ..." << std::endl;
     return 1;
 }
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 07f62af..74c48b0 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -51,6 +51,10 @@
 }
 
 bool parseResourceName(const StringPiece16& str, ResourceNameRef* outRef, bool* outPrivate) {
+    if (str.empty()) {
+        return false;
+    }
+
     size_t offset = 0;
     bool priv = false;
     if (str.data()[0] == u'*') {
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 64ca971..a0fbcc6 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -45,7 +45,8 @@
  * `outResource` set to the parsed resource name and `outPrivate` set to true if a '*' prefix
  * was present.
  */
-bool parseResourceName(const StringPiece16& str, ResourceNameRef* outResource, bool* outPrivate);
+bool parseResourceName(const StringPiece16& str, ResourceNameRef* outResource,
+                       bool* outPrivate = nullptr);
 
 /*
  * Returns true if the string was parsed as a reference (@[+][package:]type/name), with
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index c9f93e1..7425f97 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -58,6 +58,8 @@
     EXPECT_TRUE(ResourceUtils::parseResourceName(u"*android:color/foo", &actual, &actualPriv));
     EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kColor, u"foo"), actual);
     EXPECT_TRUE(actualPriv);
+
+    EXPECT_FALSE(ResourceUtils::parseResourceName(StringPiece16(), &actual, &actualPriv));
 }
 
 TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) {
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index b93e6d8..ab9c792 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -19,7 +19,6 @@
 #include "ResourceValues.h"
 #include "ValueVisitor.h"
 #include "util/Util.h"
-#include "flatten/ResourceTypeExtensions.h"
 
 #include <androidfw/ResourceTypes.h>
 #include <limits>
@@ -47,7 +46,7 @@
 }
 
 bool RawString::flatten(android::Res_value* outValue) const {
-    outValue->dataType = ExtendedTypes::TYPE_RAW_STRING;
+    outValue->dataType = android::Res_value::TYPE_STRING;
     outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
     return true;
 }
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 8e317db..dc2e28e 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -154,8 +154,8 @@
     bool privateReference = false;
 
     Reference();
-    Reference(const ResourceNameRef& n, Type type = Type::kResource);
-    Reference(const ResourceId& i, Type type = Type::kResource);
+    explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
+    explicit Reference(const ResourceId& i, Type type = Type::kResource);
 
     bool flatten(android::Res_value* outValue) const override;
     Reference* clone(StringPool* newPool) const override;
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index 94042e3..5493039 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -18,6 +18,7 @@
 #define AAPT_VALUE_VISITOR_H
 
 #include "ResourceValues.h"
+#include "ResourceTable.h"
 
 namespace aapt {
 
@@ -140,6 +141,23 @@
     return visitor.value;
 }
 
+
+inline void visitAllValuesInPackage(ResourceTablePackage* pkg, RawValueVisitor* visitor) {
+    for (auto& type : pkg->types) {
+        for (auto& entry : type->entries) {
+            for (auto& configValue : entry->values) {
+                configValue.value->accept(visitor);
+            }
+        }
+    }
+}
+
+inline void visitAllValuesInTable(ResourceTable* table, RawValueVisitor* visitor) {
+    for (auto& pkg : table->packages) {
+        visitAllValuesInPackage(pkg.get(), visitor);
+    }
+}
+
 } // namespace aapt
 
 #endif // AAPT_VALUE_VISITOR_H
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index 689ace6..1eefb82 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -24,15 +24,17 @@
 #include "compile/PseudolocaleGenerator.h"
 #include "compile/XmlIdCollector.h"
 #include "flatten/Archive.h"
-#include "flatten/FileExportWriter.h"
-#include "flatten/TableFlattener.h"
 #include "flatten/XmlFlattener.h"
+#include "proto/ProtoSerialize.h"
 #include "util/Files.h"
 #include "util/Maybe.h"
 #include "util/Util.h"
 #include "xml/XmlDom.h"
 #include "xml/XmlPullParser.h"
 
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/io/coded_stream.h>
+
 #include <dirent.h>
 #include <fstream>
 #include <string>
@@ -232,34 +234,95 @@
         }
     }
 
-    // Assign IDs to prepare the table for flattening.
-    IdAssigner idAssigner;
-    if (!idAssigner.consume(context, &table)) {
-        return false;
-    }
-
-    // Flatten the table.
-    BigBuffer buffer(1024);
-    TableFlattenerOptions tableFlattenerOptions;
-    tableFlattenerOptions.useExtendedChunks = true;
-    TableFlattener flattener(&buffer, tableFlattenerOptions);
-    if (!flattener.consume(context, &table)) {
-        return false;
-    }
-
+    // Create the file/zip entry.
     if (!writer->startEntry(outputPath, 0)) {
         context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
         return false;
     }
 
-    if (writer->writeEntry(buffer)) {
-        if (writer->finishEntry()) {
-            return true;
+    std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(&table);
+
+    // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface.
+    {
+        google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
+
+        if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
+            context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
+            return false;
         }
     }
 
-    context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
-    return false;
+    if (!writer->finishEntry()) {
+        context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to finish entry");
+        return false;
+    }
+    return true;
+}
+
+static bool writeHeaderAndBufferToWriter(const StringPiece& outputPath, const ResourceFile& file,
+                                         const BigBuffer& buffer, IArchiveWriter* writer,
+                                         IDiagnostics* diag) {
+    // Start the entry so we can write the header.
+    if (!writer->startEntry(outputPath, 0)) {
+        diag->error(DiagMessage(outputPath) << "failed to open file");
+        return false;
+    }
+
+    // Create the header.
+    std::unique_ptr<pb::CompiledFile> pbCompiledFile = serializeCompiledFileToPb(file);
+
+    {
+        // The stream must be destroyed before we finish the entry, or else
+        // some data won't be flushed.
+        // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
+        // interface.
+        google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
+        CompiledFileOutputStream outputStream(&adaptor, pbCompiledFile.get());
+        for (const BigBuffer::Block& block : buffer) {
+            if (!outputStream.Write(block.buffer.get(), block.size)) {
+                diag->error(DiagMessage(outputPath) << "failed to write data");
+                return false;
+            }
+        }
+    }
+
+    if (!writer->finishEntry()) {
+        diag->error(DiagMessage(outputPath) << "failed to finish writing data");
+        return false;
+    }
+    return true;
+}
+
+static bool writeHeaderAndMmapToWriter(const StringPiece& outputPath, const ResourceFile& file,
+                                       const android::FileMap& map, IArchiveWriter* writer,
+                                       IDiagnostics* diag) {
+    // Start the entry so we can write the header.
+    if (!writer->startEntry(outputPath, 0)) {
+        diag->error(DiagMessage(outputPath) << "failed to open file");
+        return false;
+    }
+
+    // Create the header.
+    std::unique_ptr<pb::CompiledFile> pbCompiledFile = serializeCompiledFileToPb(file);
+
+    {
+        // The stream must be destroyed before we finish the entry, or else
+        // some data won't be flushed.
+        // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
+        // interface.
+        google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
+        CompiledFileOutputStream outputStream(&adaptor, pbCompiledFile.get());
+        if (!outputStream.Write(map.getDataPtr(), map.getDataLength())) {
+            diag->error(DiagMessage(outputPath) << "failed to write data");
+            return false;
+        }
+    }
+
+    if (!writer->finishEntry()) {
+        diag->error(DiagMessage(outputPath) << "failed to finish writing data");
+        return false;
+    }
+    return true;
 }
 
 static bool compileXml(IAaptContext* context, const CompileOptions& options,
@@ -267,7 +330,6 @@
                        const std::string& outputPath) {
 
     std::unique_ptr<xml::XmlResource> xmlRes;
-
     {
         std::ifstream fin(pathData.source.path, std::ifstream::binary);
         if (!fin) {
@@ -295,30 +357,18 @@
     xmlRes->file.source = pathData.source;
 
     BigBuffer buffer(1024);
-    ChunkWriter fileExportWriter = wrapBufferWithFileExportHeader(&buffer, &xmlRes->file);
-
     XmlFlattenerOptions xmlFlattenerOptions;
     xmlFlattenerOptions.keepRawValues = true;
-    XmlFlattener flattener(fileExportWriter.getBuffer(), xmlFlattenerOptions);
+    XmlFlattener flattener(&buffer, xmlFlattenerOptions);
     if (!flattener.consume(context, xmlRes.get())) {
         return false;
     }
 
-    fileExportWriter.finish();
-
-    if (!writer->startEntry(outputPath, 0)) {
-        context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
+    if (!writeHeaderAndBufferToWriter(outputPath, xmlRes->file, buffer, writer,
+                                      context->getDiagnostics())) {
         return false;
     }
-
-    if (writer->writeEntry(buffer)) {
-        if (writer->finishEntry()) {
-            return true;
-        }
-    }
-
-    context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
-    return false;
+    return true;
 }
 
 static bool compilePng(IAaptContext* context, const CompileOptions& options,
@@ -330,8 +380,6 @@
     resFile.config = pathData.config;
     resFile.source = pathData.source;
 
-    ChunkWriter fileExportWriter = wrapBufferWithFileExportHeader(&buffer, &resFile);
-
     {
         std::ifstream fin(pathData.source.path, std::ifstream::binary);
         if (!fin) {
@@ -340,26 +388,16 @@
         }
 
         Png png(context->getDiagnostics());
-        if (!png.process(pathData.source, &fin, fileExportWriter.getBuffer(), {})) {
+        if (!png.process(pathData.source, &fin, &buffer, {})) {
             return false;
         }
     }
 
-    fileExportWriter.finish();
-
-    if (!writer->startEntry(outputPath, 0)) {
-        context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
+    if (!writeHeaderAndBufferToWriter(outputPath, resFile, buffer, writer,
+                                      context->getDiagnostics())) {
         return false;
     }
-
-    if (writer->writeEntry(buffer)) {
-        if (writer->finishEntry()) {
-            return true;
-        }
-    }
-
-    context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
-    return false;
+    return true;
 }
 
 static bool compileFile(IAaptContext* context, const CompileOptions& options,
@@ -371,8 +409,6 @@
     resFile.config = pathData.config;
     resFile.source = pathData.source;
 
-    ChunkWriter fileExportWriter = wrapBufferWithFileExportHeader(&buffer, &resFile);
-
     std::string errorStr;
     Maybe<android::FileMap> f = file::mmapPath(pathData.source.path, &errorStr);
     if (!f) {
@@ -380,35 +416,10 @@
         return false;
     }
 
-    if (!writer->startEntry(outputPath, 0)) {
-        context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
+    if (!writeHeaderAndMmapToWriter(outputPath, resFile, f.value(), writer,
+                                    context->getDiagnostics())) {
         return false;
     }
-
-    // Manually set the size and don't call finish(). This is because we are not copying from
-    // the buffer the entire file.
-    fileExportWriter.getChunkHeader()->size =
-            util::hostToDevice32(buffer.size() + f.value().getDataLength());
-
-    if (!writer->writeEntry(buffer)) {
-        context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
-        return false;
-    }
-
-    // Only write if we have something to write. This is because mmap fails with length of 0,
-    // but we still want to compile the file to get the resource ID.
-    if (f.value().getDataPtr() && f.value().getDataLength() > 0) {
-        if (!writer->writeEntry(f.value().getDataPtr(), f.value().getDataLength())) {
-            context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
-            return false;
-        }
-    }
-
-    if (!writer->finishEntry()) {
-        context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
-        return false;
-    }
-
     return true;
 }
 
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 80c6bbc..aa4a580 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -64,14 +64,12 @@
                     // Mark entry ID as taken.
                     if (!usedEntryIds.insert(entry->id.value()).second) {
                         // This ID existed before!
-                        ResourceNameRef nameRef =
-                                { package->name, type->type, entry->name };
-                        ResourceId takenId(package->id.value(), type->id.value(),
-                                           entry->id.value());
+                        ResourceNameRef nameRef(package->name, type->type, entry->name);
                         context->getDiagnostics()->error(DiagMessage()
                                                          << "resource '" << nameRef << "' "
-                                                         << "has duplicate ID '"
-                                                         << takenId << "'");
+                                                         << "has duplicate entry ID "
+                                                         << std::hex << (int) entry->id.value()
+                                                         << std::dec);
                         return false;
                     }
                 }
diff --git a/tools/aapt2/dump/Dump.cpp b/tools/aapt2/dump/Dump.cpp
new file mode 100644
index 0000000..915fae8
--- /dev/null
+++ b/tools/aapt2/dump/Dump.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2016 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 "Debug.h"
+#include "Diagnostics.h"
+#include "Flags.h"
+#include "process/IResourceTableConsumer.h"
+#include "proto/ProtoSerialize.h"
+#include "util/Files.h"
+#include "util/StringPiece.h"
+
+#include <vector>
+
+namespace aapt {
+
+//struct DumpOptions {
+//
+//};
+
+void dumpCompiledFile(const pb::CompiledFile& pbFile, const void* data, size_t len,
+                      const Source& source, IAaptContext* context) {
+    std::unique_ptr<ResourceFile> file = deserializeCompiledFileFromPb(pbFile, source,
+                                                                       context->getDiagnostics());
+    if (!file) {
+        return;
+    }
+
+    std::cout << "Resource: " << file->name << "\n"
+              << "Config:   " << file->config << "\n"
+              << "Source:   " << file->source << "\n";
+}
+
+void dumpCompiledTable(const pb::ResourceTable& pbTable, const Source& source,
+                       IAaptContext* context) {
+    std::unique_ptr<ResourceTable> table = deserializeTableFromPb(pbTable, source,
+                                                                  context->getDiagnostics());
+    if (!table) {
+        return;
+    }
+
+    Debug::printTable(table.get());
+}
+
+void tryDumpFile(IAaptContext* context, const std::string& filePath) {
+    std::string err;
+    Maybe<android::FileMap> file = file::mmapPath(filePath, &err);
+    if (!file) {
+        context->getDiagnostics()->error(DiagMessage(filePath) << err);
+        return;
+    }
+
+    android::FileMap* fileMap = &file.value();
+
+    // Try as a compiled table.
+    pb::ResourceTable pbTable;
+    if (pbTable.ParseFromArray(fileMap->getDataPtr(), fileMap->getDataLength())) {
+        dumpCompiledTable(pbTable, Source(filePath), context);
+        return;
+    }
+
+    // Try as a compiled file.
+    CompiledFileInputStream input(fileMap->getDataPtr(), fileMap->getDataLength());
+    if (const pb::CompiledFile* pbFile = input.CompiledFile()) {
+       dumpCompiledFile(*pbFile, input.data(), input.size(), Source(filePath), context);
+       return;
+    }
+}
+
+class DumpContext : public IAaptContext {
+public:
+    IDiagnostics* getDiagnostics() override {
+        return &mDiagnostics;
+    }
+
+    NameMangler* getNameMangler() override {
+        abort();
+        return nullptr;
+    }
+
+    StringPiece16 getCompilationPackage() override {
+        return {};
+    }
+
+    uint8_t getPackageId() override {
+        return 0;
+    }
+
+    ISymbolTable* getExternalSymbols() override {
+        abort();
+        return nullptr;
+    }
+
+private:
+    StdErrDiagnostics mDiagnostics;
+};
+
+/**
+ * Entry point for dump command.
+ */
+int dump(const std::vector<StringPiece>& args) {
+    //DumpOptions options;
+    Flags flags = Flags();
+    if (!flags.parse("aapt2 dump", args, &std::cerr)) {
+        return 1;
+    }
+
+    DumpContext context;
+
+    for (const std::string& arg : flags.getArgs()) {
+        tryDumpFile(&context, arg);
+    }
+    return 0;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/flatten/Archive.h b/tools/aapt2/flatten/Archive.h
index 6da1d2a..34c10ad 100644
--- a/tools/aapt2/flatten/Archive.h
+++ b/tools/aapt2/flatten/Archive.h
@@ -22,6 +22,7 @@
 #include "util/Files.h"
 #include "util/StringPiece.h"
 
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
 #include <fstream>
 #include <memory>
 #include <string>
@@ -40,13 +41,18 @@
     size_t uncompressedSize;
 };
 
-struct IArchiveWriter {
+struct IArchiveWriter : public google::protobuf::io::CopyingOutputStream {
     virtual ~IArchiveWriter() = default;
 
     virtual bool startEntry(const StringPiece& path, uint32_t flags) = 0;
     virtual bool writeEntry(const BigBuffer& buffer) = 0;
     virtual bool writeEntry(const void* data, size_t len) = 0;
     virtual bool finishEntry() = 0;
+
+    // CopyingOutputStream implementations.
+    bool Write(const void* buffer, int size) override {
+        return writeEntry(buffer, size);
+    }
 };
 
 std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(IDiagnostics* diag,
diff --git a/tools/aapt2/flatten/FileExportWriter.h b/tools/aapt2/flatten/FileExportWriter.h
deleted file mode 100644
index 7688fa7..0000000
--- a/tools/aapt2/flatten/FileExportWriter.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2015 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_FLATTEN_FILEEXPORTWRITER_H
-#define AAPT_FLATTEN_FILEEXPORTWRITER_H
-
-#include "StringPool.h"
-
-#include "flatten/ResourceTypeExtensions.h"
-#include "flatten/ChunkWriter.h"
-#include "process/IResourceTableConsumer.h"
-#include "util/BigBuffer.h"
-#include "util/Util.h"
-
-#include <androidfw/ResourceTypes.h>
-#include <utils/misc.h>
-
-namespace aapt {
-
-static ChunkWriter wrapBufferWithFileExportHeader(BigBuffer* buffer, ResourceFile* res) {
-    ChunkWriter fileExportWriter(buffer);
-    FileExport_header* fileExport = fileExportWriter.startChunk<FileExport_header>(
-            RES_FILE_EXPORT_TYPE);
-
-    ExportedSymbol* symbolRefs = nullptr;
-    if (!res->exportedSymbols.empty()) {
-        symbolRefs = fileExportWriter.nextBlock<ExportedSymbol>(
-                res->exportedSymbols.size());
-    }
-    fileExport->exportedSymbolCount = util::hostToDevice32(res->exportedSymbols.size());
-
-    StringPool symbolExportPool;
-    memcpy(fileExport->magic, "AAPT", NELEM(fileExport->magic));
-    fileExport->config = res->config;
-    fileExport->config.swapHtoD();
-    fileExport->name.index = util::hostToDevice32(symbolExportPool.makeRef(res->name.toString())
-                                                  .getIndex());
-    fileExport->source.index = util::hostToDevice32(symbolExportPool.makeRef(util::utf8ToUtf16(
-            res->source.path)).getIndex());
-
-    for (const SourcedResourceName& name : res->exportedSymbols) {
-        symbolRefs->name.index = util::hostToDevice32(symbolExportPool.makeRef(name.name.toString())
-                                                      .getIndex());
-        symbolRefs->line = util::hostToDevice32(name.line);
-        symbolRefs++;
-    }
-
-    StringPool::flattenUtf16(fileExportWriter.getBuffer(), symbolExportPool);
-    return fileExportWriter;
-}
-
-} // namespace aapt
-
-#endif /* AAPT_FLATTEN_FILEEXPORTWRITER_H */
diff --git a/tools/aapt2/flatten/FileExportWriter_test.cpp b/tools/aapt2/flatten/FileExportWriter_test.cpp
deleted file mode 100644
index 32fc203..0000000
--- a/tools/aapt2/flatten/FileExportWriter_test.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2015 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 "Resource.h"
-
-#include "flatten/FileExportWriter.h"
-#include "util/BigBuffer.h"
-#include "util/Util.h"
-
-#include "test/Common.h"
-
-#include <gtest/gtest.h>
-
-namespace aapt {
-
-TEST(FileExportWriterTest, FlattenResourceFileDataWithNoExports) {
-    ResourceFile resFile = {
-            test::parseNameOrDie(u"@android:layout/main.xml"),
-            test::parseConfigOrDie("sw600dp-v4"),
-            Source{ "res/layout/main.xml" },
-    };
-
-    BigBuffer buffer(1024);
-    ChunkWriter writer = wrapBufferWithFileExportHeader(&buffer, &resFile);
-    *writer.getBuffer()->nextBlock<uint32_t>() = 42u;
-    writer.finish();
-
-    std::unique_ptr<uint8_t[]> data = util::copy(buffer);
-
-    // There should be more data (string pool) besides the header and our data.
-    ASSERT_GT(buffer.size(), sizeof(FileExport_header) + sizeof(uint32_t));
-
-    // Write at the end of this chunk is our data.
-    uint32_t* val = (uint32_t*)(data.get() + buffer.size()) - 1;
-    EXPECT_EQ(*val, 42u);
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/flatten/ResourceTypeExtensions.h b/tools/aapt2/flatten/ResourceTypeExtensions.h
index 02bff2c..3e20ad6 100644
--- a/tools/aapt2/flatten/ResourceTypeExtensions.h
+++ b/tools/aapt2/flatten/ResourceTypeExtensions.h
@@ -22,208 +22,6 @@
 namespace aapt {
 
 /**
- * New android::ResChunk_header types defined
- * for AAPT to use.
- *
- * TODO(adamlesinski): Consider reserving these
- * enums in androidfw/ResourceTypes.h to avoid
- * future collisions.
- */
-enum {
-    /**
-     * A chunk that contains an entire file that
-     * has been compiled.
-     */
-    RES_FILE_EXPORT_TYPE = 0x000c,
-
-    RES_TABLE_PUBLIC_TYPE = 0x000d,
-
-    /**
-     * A chunk that holds the string pool
-     * for source entries (path/to/source:line).
-     */
-    RES_TABLE_SOURCE_POOL_TYPE = 0x000e,
-
-    /**
-     * A chunk holding names of externally
-     * defined symbols and offsets to where
-     * they are referenced in the table.
-     */
-    RES_TABLE_SYMBOL_TABLE_TYPE = 0x000f,
-};
-
-/**
- * New resource types that are meant to only be used
- * by AAPT and will not end up on the device.
- */
-struct ExtendedTypes {
-    enum {
-        /**
-         * A raw string value that hasn't had its escape sequences
-         * processed nor whitespace removed.
-         */
-        TYPE_RAW_STRING = 0xfe,
-    };
-};
-
-/**
- * New types for a ResTable_map.
- */
-struct ExtendedResTableMapTypes {
-    enum {
-        /**
-         * Type that contains the source path of the next item in the map.
-         */
-        ATTR_SOURCE_PATH = Res_MAKEINTERNAL(0xffff),
-
-        /**
-         * Type that contains the source line of the next item in the map.
-         */
-        ATTR_SOURCE_LINE = Res_MAKEINTERNAL(0xfffe),
-
-        /**
-         * Type that contains the comment of the next item in the map.
-         */
-        ATTR_COMMENT = Res_MAKEINTERNAL(0xfffd)
-    };
-};
-
-/**
- * Followed by exportedSymbolCount ExportedSymbol structs, followed by the string pool.
- */
-struct FileExport_header {
-    android::ResChunk_header header;
-
-    /**
-     * MAGIC value. Must be 'AAPT' (0x41415054)
-     */
-    uint8_t magic[4];
-
-    /**
-     * Version of AAPT that built this file.
-     */
-    uint32_t version;
-
-    /**
-     * The resource name.
-     */
-    android::ResStringPool_ref name;
-
-    /**
-     * Configuration of this file.
-     */
-    android::ResTable_config config;
-
-    /**
-     * Original source path of this file.
-     */
-    android::ResStringPool_ref source;
-
-    /**
-     * Number of symbols exported by this file.
-     */
-    uint32_t exportedSymbolCount;
-};
-
-struct ExportedSymbol {
-    android::ResStringPool_ref name;
-    uint32_t line;
-};
-
-struct Public_header {
-    android::ResChunk_header header;
-
-    /**
-     * The ID of the type this structure refers to.
-     */
-    uint8_t typeId;
-
-    /**
-     * Reserved. Must be 0.
-     */
-    uint8_t res0;
-
-    /**
-     * Reserved. Must be 0.
-     */
-    uint16_t res1;
-
-    /**
-     * Number of public entries.
-     */
-    uint32_t count;
-};
-
-/**
- * A structure representing source data for a resource entry.
- * Appears after an android::ResTable_entry or android::ResTable_map_entry.
- *
- * TODO(adamlesinski): This causes some issues when runtime code checks
- * the size of an android::ResTable_entry. It assumes it is an
- * android::ResTable_map_entry if the size is bigger than an android::ResTable_entry
- * which may not be true if this structure is present.
- */
-struct ResTable_entry_source {
-    /**
-     * File path reference.
-     */
-    android::ResStringPool_ref path;
-
-    /**
-     * Line number this resource was defined on.
-     */
-    uint32_t line;
-
-    /**
-     * Comment string reference.
-     */
-    android::ResStringPool_ref comment;
-};
-
-struct Public_entry {
-    uint16_t entryId;
-
-    enum : uint16_t {
-        kUndefined = 0,
-        kPublic = 1,
-        kPrivate = 2,
-    };
-
-    uint16_t state;
-    android::ResStringPool_ref key;
-    ResTable_entry_source source;
-};
-
-/**
- * A chunk with type RES_TABLE_SYMBOL_TABLE_TYPE.
- * Following the header are count number of SymbolTable_entry
- * structures, followed by an android::ResStringPool_header.
- */
-struct SymbolTable_header {
-    android::ResChunk_header header;
-
-    /**
-     * Number of SymbolTable_entry structures following
-     * this header.
-     */
-    uint32_t count;
-};
-
-struct SymbolTable_entry {
-    /**
-     * Offset from the beginning of the resource table
-     * where the symbol entry is referenced.
-     */
-    uint32_t offset;
-
-    /**
-     * The index into the string pool where the name of this
-     * symbol exists.
-     */
-    android::ResStringPool_ref name;
-};
-
-/**
  * An alternative struct to use instead of ResTable_map_entry. This one is a standard_layout
  * struct.
  */
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index 26d7c2c..71ab3db 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -51,157 +51,49 @@
     dst[i] = 0;
 }
 
+static bool cmpStyleEntries(const Style::Entry& a, const Style::Entry& b) {
+   if (a.key.id) {
+       if (b.key.id) {
+           return a.key.id.value() < b.key.id.value();
+       }
+       return true;
+   } else if (!b.key.id) {
+       return a.key.name.value() < b.key.name.value();
+   }
+   return false;
+}
+
 struct FlatEntry {
     ResourceEntry* entry;
     Value* value;
 
     // The entry string pool index to the entry's name.
     uint32_t entryKey;
-
-    // The source string pool index to the source file path.
-    uint32_t sourcePathKey;
-    uint32_t sourceLine;
-
-    // The source string pool index to the comment.
-    uint32_t commentKey;
 };
 
-class SymbolWriter {
+class MapFlattenVisitor : public RawValueVisitor {
 public:
-    struct Entry {
-        StringPool::Ref name;
-        size_t offset;
-    };
-
-    std::vector<Entry> symbols;
-
-    explicit SymbolWriter(StringPool* pool) : mPool(pool) {
-    }
-
-    void addSymbol(const Reference& ref, size_t offset) {
-        const ResourceName& name = ref.name.value();
-        std::u16string fullName;
-        if (ref.privateReference) {
-            fullName += u"*";
-        }
-
-        if (!name.package.empty()) {
-            fullName += name.package + u":";
-        }
-        fullName += toString(name.type).toString() + u"/" + name.entry;
-        symbols.push_back(Entry{ mPool->makeRef(fullName), offset });
-    }
-
-    void shiftAllOffsets(size_t offset) {
-        for (Entry& entry : symbols) {
-            entry.offset += offset;
-        }
-    }
-
-private:
-    StringPool* mPool;
-};
-
-struct MapFlattenVisitor : public RawValueVisitor {
     using RawValueVisitor::visit;
 
-    SymbolWriter* mSymbols;
-    FlatEntry* mEntry;
-    BigBuffer* mBuffer;
-    StringPool* mSourcePool;
-    StringPool* mCommentPool;
-    bool mUseExtendedChunks;
-
-    size_t mEntryCount = 0;
-    const Reference* mParent = nullptr;
-
-    MapFlattenVisitor(SymbolWriter* symbols, FlatEntry* entry, BigBuffer* buffer,
-                      StringPool* sourcePool, StringPool* commentPool,
-                      bool useExtendedChunks) :
-            mSymbols(symbols), mEntry(entry), mBuffer(buffer), mSourcePool(sourcePool),
-            mCommentPool(commentPool), mUseExtendedChunks(useExtendedChunks) {
-    }
-
-    void flattenKey(Reference* key, ResTable_map* outEntry) {
-        if (!key->id || (key->privateReference && mUseExtendedChunks)) {
-            assert(key->name && "reference must have a name");
-
-            outEntry->name.ident = util::hostToDevice32(0);
-            mSymbols->addSymbol(*key, (mBuffer->size() - sizeof(ResTable_map)) +
-                                    offsetof(ResTable_map, name));
-        } else {
-            outEntry->name.ident = util::hostToDevice32(key->id.value().id);
-        }
-    }
-
-    void flattenValue(Item* value, ResTable_map* outEntry) {
-        bool privateRef = false;
-        if (Reference* ref = valueCast<Reference>(value)) {
-            privateRef = ref->privateReference && mUseExtendedChunks;
-            if (!ref->id || privateRef) {
-                assert(ref->name && "reference must have a name");
-
-                mSymbols->addSymbol(*ref, (mBuffer->size() - sizeof(ResTable_map)) +
-                                        offsetof(ResTable_map, value) + offsetof(Res_value, data));
-            }
-        }
-
-        bool result = value->flatten(&outEntry->value);
-        if (privateRef) {
-            outEntry->value.data = 0;
-        }
-        assert(result && "flatten failed");
-    }
-
-    void flattenEntry(Reference* key, Item* value) {
-        ResTable_map* outEntry = mBuffer->nextBlock<ResTable_map>();
-        flattenKey(key, outEntry);
-        flattenValue(value, outEntry);
-        outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
-        mEntryCount++;
-    }
-
-    void flattenMetaData(Value* value) {
-        if (!mUseExtendedChunks) {
-            return;
-        }
-
-        Reference key(ResourceId{ ExtendedResTableMapTypes::ATTR_SOURCE_PATH });
-        StringPool::Ref sourcePathRef = mSourcePool->makeRef(
-                util::utf8ToUtf16(value->getSource().path));
-        BinaryPrimitive val(Res_value::TYPE_INT_DEC,
-                            static_cast<uint32_t>(sourcePathRef.getIndex()));
-        flattenEntry(&key, &val);
-
-        if (value->getSource().line) {
-            key.id = ResourceId(ExtendedResTableMapTypes::ATTR_SOURCE_LINE);
-            val.value.data = static_cast<uint32_t>(value->getSource().line.value());
-            flattenEntry(&key, &val);
-        }
-
-        if (!value->getComment().empty()) {
-            key.id = ResourceId(ExtendedResTableMapTypes::ATTR_COMMENT);
-            StringPool::Ref commentRef = mCommentPool->makeRef(value->getComment());
-            val.value.data = static_cast<uint32_t>(commentRef.getIndex());
-            flattenEntry(&key, &val);
-        }
+    MapFlattenVisitor(ResTable_entry_ext* outEntry, BigBuffer* buffer) :
+            mOutEntry(outEntry), mBuffer(buffer) {
     }
 
     void visit(Attribute* attr) override {
         {
-            Reference key(ResourceId{ ResTable_map::ATTR_TYPE });
+            Reference key = Reference(ResTable_map::ATTR_TYPE);
             BinaryPrimitive val(Res_value::TYPE_INT_DEC, attr->typeMask);
             flattenEntry(&key, &val);
         }
 
         if (attr->minInt != std::numeric_limits<int32_t>::min()) {
-            Reference key(ResourceId{ ResTable_map::ATTR_MIN });
+            Reference key = Reference(ResTable_map::ATTR_MIN);
             BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->minInt));
             flattenEntry(&key, &val);
         }
 
         if (attr->maxInt != std::numeric_limits<int32_t>::max()) {
-            Reference key(ResourceId{ ResTable_map::ATTR_MAX });
+            Reference key = Reference(ResTable_map::ATTR_MAX);
             BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->maxInt));
             flattenEntry(&key, &val);
         }
@@ -212,22 +104,11 @@
         }
     }
 
-    static bool cmpStyleEntries(const Style::Entry& a, const Style::Entry& b) {
-        if (a.key.id) {
-            if (b.key.id) {
-                return a.key.id.value() < b.key.id.value();
-            }
-            return true;
-        } else if (!b.key.id) {
-            return a.key.name.value() < b.key.name.value();
-        }
-        return false;
-    }
-
     void visit(Style* style) override {
         if (style->parent) {
-            // Parents are treated a bit differently, so record the existence and move on.
-            mParent = &style->parent.value();
+            const Reference& parentRef = style->parent.value();
+            assert(parentRef.id && "parent has no ID");
+            mOutEntry->parent.ident = util::hostToDevice32(parentRef.id.value().id);
         }
 
         // Sort the style.
@@ -235,7 +116,6 @@
 
         for (Style::Entry& entry : style->entries) {
             flattenEntry(&entry.key, entry.value.get());
-            flattenMetaData(&entry.key);
         }
     }
 
@@ -243,8 +123,8 @@
         for (auto& attrRef : styleable->entries) {
             BinaryPrimitive val(Res_value{});
             flattenEntry(&attrRef, &val);
-            flattenMetaData(&attrRef);
         }
+
     }
 
     void visit(Array* array) override {
@@ -253,7 +133,6 @@
             flattenValue(item.get(), outEntry);
             outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
             mEntryCount++;
-            flattenMetaData(item.get());
         }
     }
 
@@ -297,18 +176,45 @@
 
             Reference key(q);
             flattenEntry(&key, plural->values[i].get());
-            flattenMetaData(plural->values[i].get());
         }
     }
+
+    /**
+     * Call this after visiting a Value. This will finish any work that
+     * needs to be done to prepare the entry.
+     */
+    void finish() {
+        mOutEntry->count = util::hostToDevice32(mEntryCount);
+    }
+
+private:
+    void flattenKey(Reference* key, ResTable_map* outEntry) {
+        assert(key->id && "key has no ID");
+        outEntry->name.ident = util::hostToDevice32(key->id.value().id);
+    }
+
+    void flattenValue(Item* value, ResTable_map* outEntry) {
+        bool result = value->flatten(&outEntry->value);
+        assert(result && "flatten failed");
+    }
+
+    void flattenEntry(Reference* key, Item* value) {
+        ResTable_map* outEntry = mBuffer->nextBlock<ResTable_map>();
+        flattenKey(key, outEntry);
+        flattenValue(value, outEntry);
+        outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
+        mEntryCount++;
+    }
+
+    ResTable_entry_ext* mOutEntry;
+    BigBuffer* mBuffer;
+    size_t mEntryCount = 0;
 };
 
 class PackageFlattener {
 public:
-    PackageFlattener(IDiagnostics* diag, TableFlattenerOptions options,
-                     ResourceTablePackage* package, SymbolWriter* symbolWriter,
-                     StringPool* sourcePool) :
-            mDiag(diag), mOptions(options), mPackage(package), mSymbols(symbolWriter),
-            mSourcePool(sourcePool) {
+    PackageFlattener(IDiagnostics* diag, ResourceTablePackage* package) :
+            mDiag(diag), mPackage(package) {
     }
 
     bool flattenPackage(BigBuffer* buffer) {
@@ -337,9 +243,6 @@
         pkgHeader->keyStrings = util::hostToDevice32(pkgWriter.size());
         StringPool::flattenUtf16(pkgWriter.getBuffer(), mKeyPool);
 
-        // Add the ResTable_package header/type/key strings to the offset.
-        mSymbols->shiftAllOffsets(pkgWriter.size());
-
         // Append the types.
         buffer->appendBuffer(std::move(typeBuffer));
 
@@ -349,12 +252,9 @@
 
 private:
     IDiagnostics* mDiag;
-    TableFlattenerOptions mOptions;
     ResourceTablePackage* mPackage;
     StringPool mTypePool;
     StringPool mKeyPool;
-    SymbolWriter* mSymbols;
-    StringPool* mSourcePool;
 
     template <typename T, bool IsItem>
     T* writeEntry(FlatEntry* entry, BigBuffer* buffer) {
@@ -376,62 +276,24 @@
             outEntry->flags |= ResTable_entry::FLAG_COMPLEX;
         }
 
-        outEntry->key.index = util::hostToDevice32(entry->entryKey);
-        outEntry->size = sizeof(T);
-
-        if (mOptions.useExtendedChunks) {
-            // Write the extra source block. This will be ignored by the Android runtime.
-            ResTable_entry_source* sourceBlock = buffer->nextBlock<ResTable_entry_source>();
-            sourceBlock->path.index = util::hostToDevice32(entry->sourcePathKey);
-            sourceBlock->line = util::hostToDevice32(entry->sourceLine);
-            sourceBlock->comment.index = util::hostToDevice32(entry->commentKey);
-            outEntry->size += sizeof(*sourceBlock);
-        }
-
         outEntry->flags = util::hostToDevice16(outEntry->flags);
-        outEntry->size = util::hostToDevice16(outEntry->size);
+        outEntry->key.index = util::hostToDevice32(entry->entryKey);
+        outEntry->size = util::hostToDevice16(sizeof(T));
         return result;
     }
 
     bool flattenValue(FlatEntry* entry, BigBuffer* buffer) {
         if (Item* item = valueCast<Item>(entry->value)) {
             writeEntry<ResTable_entry, true>(entry, buffer);
-            bool privateRef = false;
-            if (Reference* ref = valueCast<Reference>(entry->value)) {
-                // If there is no ID or the reference is private and we allow extended chunks,
-                // write out a 0 and mark the symbol table with the name of the reference.
-                privateRef = (ref->privateReference && mOptions.useExtendedChunks);
-                if (!ref->id || privateRef) {
-                    assert(ref->name && "reference must have at least a name");
-                    mSymbols->addSymbol(*ref, buffer->size() + offsetof(Res_value, data));
-                }
-            }
             Res_value* outValue = buffer->nextBlock<Res_value>();
             bool result = item->flatten(outValue);
             assert(result && "flatten failed");
-            if (privateRef) {
-                // Force the value of 0 so we look up the symbol at unflatten time.
-                outValue->data = 0;
-            }
             outValue->size = util::hostToDevice16(sizeof(*outValue));
         } else {
-            const size_t beforeEntry = buffer->size();
             ResTable_entry_ext* outEntry = writeEntry<ResTable_entry_ext, false>(entry, buffer);
-            MapFlattenVisitor visitor(mSymbols, entry, buffer, mSourcePool, mSourcePool,
-                                      mOptions.useExtendedChunks);
+            MapFlattenVisitor visitor(outEntry, buffer);
             entry->value->accept(&visitor);
-            outEntry->count = util::hostToDevice32(visitor.mEntryCount);
-            if (visitor.mParent) {
-                const bool forceSymbol = visitor.mParent->privateReference &&
-                        mOptions.useExtendedChunks;
-                if (!visitor.mParent->id || forceSymbol) {
-                    assert(visitor.mParent->name && "reference must have a name");
-                    mSymbols->addSymbol(*visitor.mParent,
-                                        beforeEntry + offsetof(ResTable_entry_ext, parent));
-                } else {
-                    outEntry->parent.ident = util::hostToDevice32(visitor.mParent->id.value().id);
-                }
-            }
+            visitor.finish();
         }
         return true;
     }
@@ -480,7 +342,7 @@
     std::vector<ResourceTableType*> collectAndSortTypes() {
         std::vector<ResourceTableType*> sortedTypes;
         for (auto& type : mPackage->types) {
-            if (type->type == ResourceType::kStyleable && !mOptions.useExtendedChunks) {
+            if (type->type == ResourceType::kStyleable) {
                 // Styleables aren't real Resource Types, they are represented in the R.java
                 // file.
                 continue;
@@ -551,52 +413,6 @@
         return true;
     }
 
-    bool flattenPublic(ResourceTableType* type, std::vector<ResourceEntry*>* sortedEntries,
-                       BigBuffer* buffer) {
-        ChunkWriter publicWriter(buffer);
-        Public_header* publicHeader = publicWriter.startChunk<Public_header>(RES_TABLE_PUBLIC_TYPE);
-        publicHeader->typeId = type->id.value();
-
-        for (ResourceEntry* entry : *sortedEntries) {
-            if (entry->symbolStatus.state != SymbolState::kUndefined) {
-                // Write the public status of this entry.
-                Public_entry* publicEntry = publicWriter.nextBlock<Public_entry>();
-                publicEntry->entryId = util::hostToDevice32(entry->id.value());
-                publicEntry->key.index = util::hostToDevice32(mKeyPool.makeRef(
-                        entry->name).getIndex());
-                publicEntry->source.path.index = util::hostToDevice32(mSourcePool->makeRef(
-                        util::utf8ToUtf16(entry->symbolStatus.source.path)).getIndex());
-                if (entry->symbolStatus.source.line) {
-                    publicEntry->source.line = util::hostToDevice32(
-                            entry->symbolStatus.source.line.value());
-                }
-                publicEntry->source.comment.index = util::hostToDevice32(mSourcePool->makeRef(
-                        entry->symbolStatus.comment).getIndex());
-
-                switch (entry->symbolStatus.state) {
-                case SymbolState::kPrivate:
-                    publicEntry->state = Public_entry::kPrivate;
-                    break;
-
-                case SymbolState::kPublic:
-                    publicEntry->state = Public_entry::kPublic;
-                    break;
-
-                case SymbolState::kUndefined:
-                    publicEntry->state = Public_entry::kUndefined;
-                    break;
-                }
-
-                // Don't hostToDevice until the last step.
-                publicHeader->count += 1;
-            }
-        }
-
-        publicHeader->count = util::hostToDevice32(publicHeader->count);
-        publicWriter.finish();
-        return true;
-    }
-
     bool flattenTypes(BigBuffer* buffer) {
         // Sort the types by their IDs. They will be inserted into the StringPool in this order.
         std::vector<ResourceTableType*> sortedTypes = collectAndSortTypes();
@@ -620,12 +436,6 @@
                 return false;
             }
 
-            if (mOptions.useExtendedChunks) {
-                if (!flattenPublic(type, &sortedEntries, buffer)) {
-                    return false;
-                }
-            }
-
             // The binary resource table lists resource entries for each configuration.
             // We store them inverted, where a resource entry lists the values for each
             // configuration available. Here we reverse this to match the binary table.
@@ -635,26 +445,8 @@
 
                 // Group values by configuration.
                 for (auto& configValue : entry->values) {
-                    Value* value = configValue.value.get();
-
-                    const StringPool::Ref sourceRef = mSourcePool->makeRef(
-                            util::utf8ToUtf16(value->getSource().path));
-
-                    uint32_t lineNumber = 0;
-                    if (value->getSource().line) {
-                        lineNumber = value->getSource().line.value();
-                    }
-
-                    const StringPool::Ref commentRef = mSourcePool->makeRef(value->getComment());
-
-                    configToEntryListMap[configValue.config]
-                            .push_back(FlatEntry{
-                                    entry,
-                                    value,
-                                    keyIndex,
-                                    (uint32_t) sourceRef.getIndex(),
-                                    lineNumber,
-                                    (uint32_t) commentRef.getIndex() });
+                    configToEntryListMap[configValue.config].push_back(FlatEntry{
+                            entry, configValue.value.get(), keyIndex });
                 }
             }
 
@@ -692,86 +484,18 @@
     // Flatten the values string pool.
     StringPool::flattenUtf8(tableWriter.getBuffer(), table->stringPool);
 
-    // If we have a reference to a symbol that doesn't exist, we don't know its resource ID.
-    // We encode the name of the symbol along with the offset of where to include the resource ID
-    // once it is found.
-    StringPool symbolPool;
-    std::vector<SymbolWriter::Entry> symbolOffsets;
-
-    // String pool holding the source paths of each value.
-    StringPool sourcePool;
-
     BigBuffer packageBuffer(1024);
 
     // Flatten each package.
     for (auto& package : table->packages) {
-        const size_t beforePackageSize = packageBuffer.size();
-
-        // All packages will share a single global symbol pool.
-        SymbolWriter packageSymbolWriter(&symbolPool);
-
-        PackageFlattener flattener(context->getDiagnostics(), mOptions, package.get(),
-                                   &packageSymbolWriter, &sourcePool);
+        PackageFlattener flattener(context->getDiagnostics(), package.get());
         if (!flattener.flattenPackage(&packageBuffer)) {
             return false;
         }
-
-        // The symbols are offset only from their own Package start. Offset them from the
-        // start of the packageBuffer.
-        packageSymbolWriter.shiftAllOffsets(beforePackageSize);
-
-        // Extract all the symbols to offset
-        symbolOffsets.insert(symbolOffsets.end(),
-                             std::make_move_iterator(packageSymbolWriter.symbols.begin()),
-                             std::make_move_iterator(packageSymbolWriter.symbols.end()));
     }
 
-    SymbolTable_entry* symbolEntryData = nullptr;
-    if (mOptions.useExtendedChunks) {
-        if (!symbolOffsets.empty()) {
-            // Sort the offsets so we can scan them linearly.
-            std::sort(symbolOffsets.begin(), symbolOffsets.end(),
-                      [](const SymbolWriter::Entry& a, const SymbolWriter::Entry& b) -> bool {
-                          return a.offset < b.offset;
-                      });
-
-            // Write the Symbol header.
-            ChunkWriter symbolWriter(tableWriter.getBuffer());
-            SymbolTable_header* symbolHeader = symbolWriter.startChunk<SymbolTable_header>(
-                    RES_TABLE_SYMBOL_TABLE_TYPE);
-            symbolHeader->count = util::hostToDevice32(symbolOffsets.size());
-
-            symbolEntryData = symbolWriter.nextBlock<SymbolTable_entry>(symbolOffsets.size());
-            StringPool::flattenUtf8(symbolWriter.getBuffer(), symbolPool);
-            symbolWriter.finish();
-        }
-
-        if (sourcePool.size() > 0) {
-            // Write out source pool.
-            ChunkWriter srcWriter(tableWriter.getBuffer());
-            srcWriter.startChunk<ResChunk_header>(RES_TABLE_SOURCE_POOL_TYPE);
-            StringPool::flattenUtf8(srcWriter.getBuffer(), sourcePool);
-            srcWriter.finish();
-        }
-    }
-
-    const size_t beforePackagesSize = tableWriter.size();
-
     // Finally merge all the packages into the main buffer.
     tableWriter.getBuffer()->appendBuffer(std::move(packageBuffer));
-
-    // Update the offsets to their final values.
-    if (symbolEntryData) {
-        for (SymbolWriter::Entry& entry : symbolOffsets) {
-            symbolEntryData->name.index = util::hostToDevice32(entry.name.getIndex());
-
-            // The symbols were all calculated with the packageBuffer offset. We need to
-            // add the beginning of the output buffer.
-            symbolEntryData->offset = util::hostToDevice32(entry.offset + beforePackagesSize);
-            symbolEntryData++;
-        }
-    }
-
     tableWriter.finish();
     return true;
 }
diff --git a/tools/aapt2/flatten/TableFlattener.h b/tools/aapt2/flatten/TableFlattener.h
index 901b129..0ab0197 100644
--- a/tools/aapt2/flatten/TableFlattener.h
+++ b/tools/aapt2/flatten/TableFlattener.h
@@ -24,28 +24,15 @@
 class BigBuffer;
 class ResourceTable;
 
-struct TableFlattenerOptions {
-    /**
-     * Specifies whether to output extended chunks, like
-     * source information and missing symbol entries. Default
-     * is false.
-     *
-     * Set this to true when emitting intermediate resource table.
-     */
-    bool useExtendedChunks = false;
-};
-
 class TableFlattener : public IResourceTableConsumer {
 public:
-    TableFlattener(BigBuffer* buffer, TableFlattenerOptions options) :
-            mBuffer(buffer), mOptions(options) {
+    TableFlattener(BigBuffer* buffer) : mBuffer(buffer) {
     }
 
     bool consume(IAaptContext* context, ResourceTable* table) override;
 
 private:
     BigBuffer* mBuffer;
-    TableFlattenerOptions mOptions;
 };
 
 } // namespace aapt
diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/flatten/TableFlattener_test.cpp
index 7030603..39c4fd3 100644
--- a/tools/aapt2/flatten/TableFlattener_test.cpp
+++ b/tools/aapt2/flatten/TableFlattener_test.cpp
@@ -38,9 +38,7 @@
 
     ::testing::AssertionResult flatten(ResourceTable* table, ResTable* outTable) {
         BigBuffer buffer(1024);
-        TableFlattenerOptions options = {};
-        options.useExtendedChunks = true;
-        TableFlattener flattener(&buffer, options);
+        TableFlattener flattener(&buffer);
         if (!flattener.consume(mContext.get(), table)) {
             return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
         }
@@ -54,9 +52,7 @@
 
     ::testing::AssertionResult flatten(ResourceTable* table, ResourceTable* outTable) {
         BigBuffer buffer(1024);
-        TableFlattenerOptions options = {};
-        options.useExtendedChunks = true;
-        TableFlattener flattener(&buffer, options);
+        TableFlattener flattener(&buffer);
         if (!flattener.consume(mContext.get(), table)) {
             return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
         }
@@ -210,58 +206,6 @@
                            Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
 }
 
-TEST_F(TableFlattenerTest, FlattenUnlinkedTable) {
-    std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
-            .setPackageId(u"com.app.test", 0x7f)
-            .addValue(u"@com.app.test:integer/one", ResourceId(0x7f020000),
-                      test::buildReference(u"@android:integer/foo"))
-            .addValue(u"@com.app.test:style/Theme", ResourceId(0x7f030000), test::StyleBuilder()
-                    .setParent(u"@android:style/Theme.Material")
-                    .addItem(u"@android:attr/background", {})
-                    .addItem(u"@android:attr/colorAccent",
-                             test::buildReference(u"@com.app.test:color/green"))
-                    .build())
-            .build();
-
-    {
-        // Need access to stringPool to make RawString.
-        Style* style = test::getValue<Style>(table.get(), u"@com.app.test:style/Theme");
-        style->entries[0].value = util::make_unique<RawString>(table->stringPool.makeRef(u"foo"));
-    }
-
-    ResourceTable finalTable;
-    ASSERT_TRUE(flatten(table.get(), &finalTable));
-
-    Reference* ref = test::getValue<Reference>(&finalTable, u"@com.app.test:integer/one");
-    ASSERT_NE(ref, nullptr);
-    AAPT_ASSERT_TRUE(ref->name);
-    EXPECT_EQ(ref->name.value(), test::parseNameOrDie(u"@android:integer/foo"));
-
-    Style* style = test::getValue<Style>(&finalTable, u"@com.app.test:style/Theme");
-    ASSERT_NE(style, nullptr);
-    AAPT_ASSERT_TRUE(style->parent);
-    AAPT_ASSERT_TRUE(style->parent.value().name);
-    EXPECT_EQ(style->parent.value().name.value(),
-              test::parseNameOrDie(u"@android:style/Theme.Material"));
-
-    ASSERT_EQ(2u, style->entries.size());
-
-    AAPT_ASSERT_TRUE(style->entries[0].key.name);
-    EXPECT_EQ(style->entries[0].key.name.value(),
-              test::parseNameOrDie(u"@android:attr/background"));
-    RawString* raw = valueCast<RawString>(style->entries[0].value.get());
-    ASSERT_NE(raw, nullptr);
-    EXPECT_EQ(*raw->value, u"foo");
-
-    AAPT_ASSERT_TRUE(style->entries[1].key.name);
-    EXPECT_EQ(style->entries[1].key.name.value(),
-              test::parseNameOrDie(u"@android:attr/colorAccent"));
-    ref = valueCast<Reference>(style->entries[1].value.get());
-    ASSERT_NE(ref, nullptr);
-    AAPT_ASSERT_TRUE(ref->name);
-    EXPECT_EQ(ref->name.value(), test::parseNameOrDie(u"@com.app.test:color/green"));
-}
-
 TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) {
     Attribute attr(false);
     attr.typeMask = android::ResTable_map::TYPE_INTEGER;
@@ -284,33 +228,4 @@
     EXPECT_EQ(attr.maxInt, actualAttr->maxInt);
 }
 
-TEST_F(TableFlattenerTest, FlattenSourceAndCommentsForChildrenOfCompoundValues) {
-    Style style;
-    Reference key(test::parseNameOrDie(u"@android:attr/foo"));
-    key.id = ResourceId(0x01010000);
-    key.setSource(Source("test").withLine(2));
-    key.setComment(StringPiece16(u"comment"));
-    style.entries.push_back(Style::Entry{ key, util::make_unique<Id>() });
-
-    test::ResourceTableBuilder builder = test::ResourceTableBuilder();
-    std::unique_ptr<ResourceTable> table = builder
-            .setPackageId(u"android", 0x01)
-            .addValue(u"@android:attr/foo", ResourceId(0x01010000),
-                      test::AttributeBuilder().build())
-            .addValue(u"@android:style/foo", ResourceId(0x01020000),
-                      std::unique_ptr<Style>(style.clone(builder.getStringPool())))
-            .build();
-
-    ResourceTable result;
-    ASSERT_TRUE(flatten(table.get(), &result));
-
-    Style* actualStyle = test::getValue<Style>(&result, u"@android:style/foo");
-    ASSERT_NE(nullptr, actualStyle);
-    ASSERT_EQ(1u, actualStyle->entries.size());
-
-    Reference* actualKey = &actualStyle->entries[0].key;
-    EXPECT_EQ(key.getSource(), actualKey->getSource());
-    EXPECT_EQ(key.getComment(), actualKey->getComment());
-}
-
 } // namespace aapt
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index fd76e88..8e32179 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -19,6 +19,7 @@
 #include "Flags.h"
 #include "Locale.h"
 #include "NameMangler.h"
+#include "ResourceUtils.h"
 #include "compile/IdAssigner.h"
 #include "filter/ConfigFilter.h"
 #include "flatten/Archive.h"
@@ -35,12 +36,14 @@
 #include "link/TableMerger.h"
 #include "process/IResourceTableConsumer.h"
 #include "process/SymbolTable.h"
+#include "proto/ProtoSerialize.h"
 #include "unflatten/BinaryResourceParser.h"
-#include "unflatten/FileExportHeaderReader.h"
 #include "util/Files.h"
 #include "util/StringPiece.h"
 #include "xml/XmlDom.h"
 
+#include <google/protobuf/io/coded_stream.h>
+
 #include <fstream>
 #include <sys/stat.h>
 #include <vector>
@@ -144,6 +147,22 @@
         return table;
     }
 
+    std::unique_ptr<ResourceTable> loadTableFromPb(const Source& source,
+                                                   const void* data, size_t len) {
+        pb::ResourceTable pbTable;
+        if (!pbTable.ParseFromArray(data, len)) {
+            mContext->getDiagnostics()->error(DiagMessage(source) << "invalid compiled table");
+            return {};
+        }
+
+        std::unique_ptr<ResourceTable> table = deserializeTableFromPb(pbTable, source,
+                                                                      mContext->getDiagnostics());
+        if (!table) {
+            return {};
+        }
+        return table;
+    }
+
     /**
      * Inflates an XML file from the source path.
      */
@@ -161,18 +180,16 @@
             const Source& source,
             const void* data, size_t len,
             IDiagnostics* diag) {
-        std::string errorStr;
-        ssize_t offset = getWrappedDataOffset(data, len, &errorStr);
-        if (offset < 0) {
-            diag->error(DiagMessage(source) << errorStr);
+        CompiledFileInputStream inputStream(data, len);
+        if (!inputStream.CompiledFile()) {
+            diag->error(DiagMessage(source) << "invalid compiled file header");
             return {};
         }
 
-        std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(
-                reinterpret_cast<const uint8_t*>(data) + static_cast<size_t>(offset),
-                len - static_cast<size_t>(offset),
-                diag,
-                source);
+        const uint8_t* xmlData = reinterpret_cast<const uint8_t*>(inputStream.data());
+        const size_t xmlDataLen = inputStream.size();
+
+        std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(xmlData, xmlDataLen, diag, source);
         if (!xmlRes) {
             return {};
         }
@@ -182,11 +199,16 @@
     static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source,
                                                               const void* data, size_t len,
                                                               IDiagnostics* diag) {
-        std::unique_ptr<ResourceFile> resFile = util::make_unique<ResourceFile>();
-        std::string errorStr;
-        ssize_t offset = unwrapFileExportHeader(data, len, resFile.get(), &errorStr);
-        if (offset < 0) {
-            diag->error(DiagMessage(source) << errorStr);
+        CompiledFileInputStream inputStream(data, len);
+        const pb::CompiledFile* pbFile = inputStream.CompiledFile();
+        if (!pbFile) {
+            diag->error(DiagMessage(source) << "invalid compiled file header");
+            return {};
+        }
+
+        std::unique_ptr<ResourceFile> resFile = deserializeCompiledFileFromPb(*pbFile, source,
+                                                                              diag);
+        if (!resFile) {
             return {};
         }
         return resFile;
@@ -214,16 +236,16 @@
             return false;
         }
 
-        std::string errorStr;
-        ssize_t offset = getWrappedDataOffset(data->data(), data->size(), &errorStr);
-        if (offset < 0) {
-            mContext->getDiagnostics()->error(DiagMessage(file->getSource()) << errorStr);
+        CompiledFileInputStream inputStream(data->data(), data->size());
+        if (!inputStream.CompiledFile()) {
+            mContext->getDiagnostics()->error(DiagMessage(file->getSource())
+                                              << "invalid compiled file header");
             return false;
         }
 
         if (writer->startEntry(outPath, getCompressionFlags(outPath))) {
-            if (writer->writeEntry(reinterpret_cast<const uint8_t*>(data->data()) + offset,
-                                   data->size() - static_cast<size_t>(offset))) {
+            if (writer->writeEntry(reinterpret_cast<const uint8_t*>(inputStream.data()),
+                                   inputStream.size())) {
                 if (writer->finishEntry()) {
                     return true;
                 }
@@ -307,9 +329,7 @@
 
     bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
         BigBuffer buffer(1024);
-        TableFlattenerOptions options = {};
-        options.useExtendedChunks = mOptions.staticLib;
-        TableFlattener flattener(&buffer, options);
+        TableFlattener flattener(&buffer);
         if (!flattener.consume(mContext, table)) {
             return false;
         }
@@ -445,8 +465,8 @@
             return false;
         }
 
-        std::unique_ptr<ResourceTable> table = loadTable(file->getSource(), data->data(),
-                                                         data->size());
+        std::unique_ptr<ResourceTable> table = loadTableFromPb(file->getSource(), data->data(),
+                                                               data->size());
         if (!table) {
             return false;
         }
diff --git a/tools/aapt2/proto/ProtoHelpers.cpp b/tools/aapt2/proto/ProtoHelpers.cpp
new file mode 100644
index 0000000..99981c5
--- /dev/null
+++ b/tools/aapt2/proto/ProtoHelpers.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2016 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 "proto/ProtoHelpers.h"
+
+namespace aapt {
+
+void serializeStringPoolToPb(const StringPool& pool, pb::StringPool* outPbPool) {
+    BigBuffer buffer(1024);
+    StringPool::flattenUtf8(&buffer, pool);
+
+    std::string* data = outPbPool->mutable_data();
+    data->reserve(buffer.size());
+
+    size_t offset = 0;
+    for (const BigBuffer::Block& block : buffer) {
+        data->insert(data->begin() + offset, block.buffer.get(), block.buffer.get() + block.size);
+        offset += block.size;
+    }
+}
+
+void serializeSourceToPb(const Source& source, StringPool* srcPool, pb::Source* outPbSource) {
+    StringPool::Ref ref = srcPool->makeRef(util::utf8ToUtf16(source.path));
+    outPbSource->set_path_idx(static_cast<uint32_t>(ref.getIndex()));
+    if (source.line) {
+        outPbSource->set_line_no(static_cast<uint32_t>(source.line.value()));
+    }
+}
+
+void deserializeSourceFromPb(const pb::Source& pbSource, const android::ResStringPool& srcPool,
+                             Source* outSource) {
+    if (pbSource.has_path_idx()) {
+        outSource->path = util::getString8(srcPool, pbSource.path_idx()).toString();
+    }
+
+    if (pbSource.has_line_no()) {
+        outSource->line = static_cast<size_t>(pbSource.line_no());
+    }
+}
+
+pb::SymbolStatus_Visibility serializeVisibilityToPb(SymbolState state) {
+    switch (state) {
+    case SymbolState::kPrivate: return pb::SymbolStatus_Visibility_Private;
+    case SymbolState::kPublic: return pb::SymbolStatus_Visibility_Public;
+    default: break;
+    }
+    return pb::SymbolStatus_Visibility_Unknown;
+}
+
+SymbolState deserializeVisibilityFromPb(pb::SymbolStatus_Visibility pbVisibility) {
+    switch (pbVisibility) {
+    case pb::SymbolStatus_Visibility_Private: return SymbolState::kPrivate;
+    case pb::SymbolStatus_Visibility_Public: return SymbolState::kPublic;
+    default: break;
+    }
+    return SymbolState::kUndefined;
+}
+
+void serializeConfig(const ConfigDescription& config, pb::ConfigDescription* outPbConfig) {
+    android::ResTable_config flatConfig = config;
+    flatConfig.size = sizeof(flatConfig);
+    flatConfig.swapHtoD();
+    outPbConfig->set_data(&flatConfig, sizeof(flatConfig));
+}
+
+bool deserializeConfigDescriptionFromPb(const pb::ConfigDescription& pbConfig,
+                                        ConfigDescription* outConfig) {
+    if (!pbConfig.has_data()) {
+        return false;
+    }
+
+    const android::ResTable_config* config;
+    if (pbConfig.data().size() > sizeof(*config)) {
+        return false;
+    }
+
+    config = reinterpret_cast<const android::ResTable_config*>(pbConfig.data().data());
+    outConfig->copyFromDtoH(*config);
+    return true;
+}
+
+pb::Reference_Type serializeReferenceTypeToPb(Reference::Type type) {
+    switch (type) {
+    case Reference::Type::kResource:  return pb::Reference_Type_Ref;
+    case Reference::Type::kAttribute: return pb::Reference_Type_Attr;
+    default: break;
+    }
+    return pb::Reference_Type_Ref;
+}
+
+Reference::Type deserializeReferenceTypeFromPb(pb::Reference_Type pbType) {
+    switch (pbType) {
+    case pb::Reference_Type_Ref:  return Reference::Type::kResource;
+    case pb::Reference_Type_Attr: return Reference::Type::kAttribute;
+    default: break;
+    }
+    return Reference::Type::kResource;
+}
+
+pb::Plural_Arity serializePluralEnumToPb(size_t pluralIdx) {
+    switch (pluralIdx) {
+    case Plural::Zero:  return pb::Plural_Arity_Zero;
+    case Plural::One:   return pb::Plural_Arity_One;
+    case Plural::Two:   return pb::Plural_Arity_Two;
+    case Plural::Few:   return pb::Plural_Arity_Few;
+    case Plural::Many:  return pb::Plural_Arity_Many;
+    default: break;
+    }
+    return pb::Plural_Arity_Other;
+}
+
+size_t deserializePluralEnumFromPb(pb::Plural_Arity arity) {
+    switch (arity) {
+    case pb::Plural_Arity_Zero: return Plural::Zero;
+    case pb::Plural_Arity_One:  return Plural::One;
+    case pb::Plural_Arity_Two:  return Plural::Two;
+    case pb::Plural_Arity_Few:  return Plural::Few;
+    case pb::Plural_Arity_Many: return Plural::Many;
+    default: break;
+    }
+    return Plural::Other;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/proto/ProtoHelpers.h b/tools/aapt2/proto/ProtoHelpers.h
new file mode 100644
index 0000000..02e67f1
--- /dev/null
+++ b/tools/aapt2/proto/ProtoHelpers.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 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_PROTO_PROTOHELPERS_H
+#define AAPT_PROTO_PROTOHELPERS_H
+
+#include "ConfigDescription.h"
+#include "ResourceTable.h"
+#include "Source.h"
+#include "StringPool.h"
+
+#include "proto/frameworks/base/tools/aapt2/Format.pb.h"
+
+#include <androidfw/ResourceTypes.h>
+
+namespace aapt {
+
+void serializeStringPoolToPb(const StringPool& pool, pb::StringPool* outPbPool);
+
+void serializeSourceToPb(const Source& source, StringPool* srcPool, pb::Source* outPbSource);
+void deserializeSourceFromPb(const pb::Source& pbSource, const android::ResStringPool& srcPool,
+                             Source* outSource);
+
+pb::SymbolStatus_Visibility serializeVisibilityToPb(SymbolState state);
+SymbolState deserializeVisibilityFromPb(pb::SymbolStatus_Visibility pbVisibility);
+
+void serializeConfig(const ConfigDescription& config, pb::ConfigDescription* outPbConfig);
+bool deserializeConfigDescriptionFromPb(const pb::ConfigDescription& pbConfig,
+                                        ConfigDescription* outConfig);
+
+pb::Reference_Type serializeReferenceTypeToPb(Reference::Type type);
+Reference::Type deserializeReferenceTypeFromPb(pb::Reference_Type pbType);
+
+pb::Plural_Arity serializePluralEnumToPb(size_t pluralIdx);
+size_t deserializePluralEnumFromPb(pb::Plural_Arity arity);
+
+} // namespace aapt
+
+#endif /* AAPT_PROTO_PROTOHELPERS_H */
diff --git a/tools/aapt2/proto/ProtoSerialize.h b/tools/aapt2/proto/ProtoSerialize.h
new file mode 100644
index 0000000..6e224ab
--- /dev/null
+++ b/tools/aapt2/proto/ProtoSerialize.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 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_FLATTEN_TABLEPROTOSERIALIZER_H
+#define AAPT_FLATTEN_TABLEPROTOSERIALIZER_H
+
+#include "Diagnostics.h"
+#include "ResourceTable.h"
+#include "Source.h"
+#include "proto/ProtoHelpers.h"
+
+#include <android-base/macros.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+
+namespace aapt {
+
+std::unique_ptr<pb::ResourceTable> serializeTableToPb(ResourceTable* table);
+std::unique_ptr<ResourceTable> deserializeTableFromPb(const pb::ResourceTable& pbTable,
+                                                      const Source& source,
+                                                      IDiagnostics* diag);
+
+std::unique_ptr<pb::CompiledFile> serializeCompiledFileToPb(const ResourceFile& file);
+std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFile& pbFile,
+                                                            const Source& source,
+                                                            IDiagnostics* diag);
+
+class CompiledFileOutputStream : public google::protobuf::io::CopyingOutputStream {
+public:
+    CompiledFileOutputStream(google::protobuf::io::ZeroCopyOutputStream* out,
+                             pb::CompiledFile* pbFile);
+    bool Write(const void* data, int size) override;
+    bool Finish();
+
+private:
+    bool ensureFileWritten();
+
+    google::protobuf::io::CodedOutputStream mOut;
+    pb::CompiledFile* mPbFile;
+
+    DISALLOW_COPY_AND_ASSIGN(CompiledFileOutputStream);
+};
+
+class CompiledFileInputStream {
+public:
+    CompiledFileInputStream(const void* data, size_t size);
+
+    const pb::CompiledFile* CompiledFile();
+
+    const void* data();
+    size_t size();
+
+private:
+    google::protobuf::io::CodedInputStream mIn;
+    std::unique_ptr<pb::CompiledFile> mPbFile;
+    const uint8_t* mData;
+    size_t mSize;
+
+    DISALLOW_COPY_AND_ASSIGN(CompiledFileInputStream);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_FLATTEN_TABLEPROTOSERIALIZER_H */
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
new file mode 100644
index 0000000..1310aa6
--- /dev/null
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) 2016 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 "ResourceTable.h"
+#include "ResourceUtils.h"
+#include "ValueVisitor.h"
+#include "proto/ProtoHelpers.h"
+#include "proto/ProtoSerialize.h"
+#include "util/Comparators.h"
+
+#include <androidfw/ResourceTypes.h>
+
+namespace aapt {
+
+namespace {
+
+class ReferenceIdToNameVisitor : public ValueVisitor {
+public:
+    using ValueVisitor::visit;
+
+    ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping) :
+            mMapping(mapping) {
+        assert(mMapping);
+    }
+
+    void visit(Reference* reference) override {
+        if (!reference->id || !reference->id.value().isValid()) {
+            return;
+        }
+
+        ResourceId id = reference->id.value();
+        auto cacheIter = mMapping->find(id);
+        if (cacheIter != mMapping->end()) {
+            reference->name = cacheIter->second.toResourceName();
+        }
+    }
+
+private:
+    const std::map<ResourceId, ResourceNameRef>* mMapping;
+};
+
+class PackagePbDeserializer {
+public:
+    PackagePbDeserializer(const android::ResStringPool* valuePool,
+                          const android::ResStringPool* sourcePool,
+                          const android::ResStringPool* symbolPool,
+                          const Source& source, IDiagnostics* diag) :
+            mValuePool(valuePool), mSourcePool(sourcePool), mSymbolPool(symbolPool),
+            mSource(source), mDiag(diag) {
+    }
+
+public:
+    bool deserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) {
+        Maybe<uint8_t> id;
+        if (pbPackage.has_package_id()) {
+            id = static_cast<uint8_t>(pbPackage.package_id());
+        }
+
+        std::map<ResourceId, ResourceNameRef> idIndex;
+
+        ResourceTablePackage* pkg = table->createPackage(
+                util::utf8ToUtf16(pbPackage.package_name()), id);
+        for (const pb::Type& pbType : pbPackage.types()) {
+            const ResourceType* resType = parseResourceType(util::utf8ToUtf16(pbType.name()));
+            if (!resType) {
+                mDiag->error(DiagMessage(mSource) << "unknown type '" << pbType.name() << "'");
+                return {};
+            }
+
+            ResourceTableType* type = pkg->findOrCreateType(*resType);
+
+            for (const pb::Entry& pbEntry : pbType.entries()) {
+                ResourceEntry* entry = type->findOrCreateEntry(util::utf8ToUtf16(pbEntry.name()));
+
+                // Deserialize the symbol status (public/private with source and comments).
+                if (pbEntry.has_symbol_status()) {
+                    const pb::SymbolStatus& pbStatus = pbEntry.symbol_status();
+                    if (pbStatus.has_source()) {
+                        deserializeSourceFromPb(pbStatus.source(), *mSourcePool,
+                                                &entry->symbolStatus.source);
+                    }
+
+                    if (pbStatus.has_comment()) {
+                        entry->symbolStatus.comment = util::utf8ToUtf16(pbStatus.comment());
+                    }
+
+                    SymbolState visibility = deserializeVisibilityFromPb(pbStatus.visibility());
+                    entry->symbolStatus.state = visibility;
+
+                    if (visibility == SymbolState::kPublic) {
+                        // This is a public symbol, we must encode the ID now if there is one.
+                        if (pbEntry.has_id()) {
+                            entry->id = static_cast<uint16_t>(pbEntry.id());
+                        }
+
+                        if (type->symbolStatus.state != SymbolState::kPublic) {
+                            // If the type has not been made public, do so now.
+                            type->symbolStatus.state = SymbolState::kPublic;
+                            if (pbType.has_id()) {
+                                type->id = static_cast<uint8_t>(pbType.id());
+                            }
+                        }
+                    } else if (visibility == SymbolState::kPrivate) {
+                        if (type->symbolStatus.state == SymbolState::kUndefined) {
+                            type->symbolStatus.state = SymbolState::kPrivate;
+                        }
+                    }
+                }
+
+                ResourceId resId(pbPackage.package_id(), pbType.id(), pbEntry.id());
+                if (resId.isValid()) {
+                    idIndex[resId] = ResourceNameRef(pkg->name, type->type, entry->name);
+                }
+
+                for (const pb::ConfigValue& pbConfigValue : pbEntry.config_values()) {
+                    const pb::ConfigDescription& pbConfig = pbConfigValue.config();
+
+                    ConfigDescription config;
+                    if (!deserializeConfigDescriptionFromPb(pbConfig, &config)) {
+                        mDiag->error(DiagMessage(mSource) << "invalid configuration");
+                        return {};
+                    }
+
+                    auto iter = std::lower_bound(entry->values.begin(), entry->values.end(),
+                                                 config, cmp::lessThanConfig);
+                    if (iter != entry->values.end() && iter->config == config) {
+                        // Duplicate config.
+                        mDiag->error(DiagMessage(mSource) << "duplicate configuration");
+                        return {};
+                    }
+
+                    std::unique_ptr<Value> value = deserializeValueFromPb(pbConfigValue.value(),
+                                                                          config,
+                                                                          &table->stringPool);
+                    if (!value) {
+                        return {};
+                    }
+                    entry->values.insert(iter, ResourceConfigValue{ config, std::move(value) });
+                }
+            }
+        }
+
+        ReferenceIdToNameVisitor visitor(&idIndex);
+        visitAllValuesInPackage(pkg, &visitor);
+        return true;
+    }
+
+private:
+    std::unique_ptr<Item> deserializeItemFromPb(const pb::Item& pbItem,
+                                                const ConfigDescription& config,
+                                                StringPool* pool) {
+        if (pbItem.has_ref()) {
+            const pb::Reference& pbRef = pbItem.ref();
+            std::unique_ptr<Reference> ref = util::make_unique<Reference>();
+            if (!deserializeReferenceFromPb(pbRef, ref.get())) {
+                return {};
+            }
+            return std::move(ref);
+
+        } else if (pbItem.has_prim()) {
+            const pb::Primitive& pbPrim = pbItem.prim();
+            android::Res_value prim = {};
+            prim.dataType = static_cast<uint8_t>(pbPrim.type());
+            prim.data = pbPrim.data();
+            return util::make_unique<BinaryPrimitive>(prim);
+
+        } else if (pbItem.has_id()) {
+            return util::make_unique<Id>();
+
+        } else if (pbItem.has_str()) {
+            const uint32_t idx = pbItem.str().idx();
+            StringPiece16 str = util::getString(*mValuePool, idx);
+
+            const android::ResStringPool_span* spans = mValuePool->styleAt(idx);
+            if (spans && spans->name.index != android::ResStringPool_span::END) {
+                StyleString styleStr = { str.toString() };
+                while (spans->name.index != android::ResStringPool_span::END) {
+                    styleStr.spans.push_back(Span{
+                            util::getString(*mValuePool, spans->name.index).toString(),
+                            spans->firstChar,
+                            spans->lastChar
+                    });
+                    spans++;
+                }
+                return util::make_unique<StyledString>(
+                        pool->makeRef(styleStr, StringPool::Context{ 1, config }));
+            }
+            return util::make_unique<String>(
+                    pool->makeRef(str, StringPool::Context{ 1, config }));
+
+        } else if (pbItem.has_raw_str()) {
+            const uint32_t idx = pbItem.raw_str().idx();
+            StringPiece16 str = util::getString(*mValuePool, idx);
+            return util::make_unique<RawString>(
+                    pool->makeRef(str, StringPool::Context{ 1, config }));
+
+        } else if (pbItem.has_file()) {
+            const uint32_t idx = pbItem.file().path_idx();
+            StringPiece16 str = util::getString(*mValuePool, idx);
+            return util::make_unique<FileReference>(
+                    pool->makeRef(str, StringPool::Context{ 0, config }));
+
+        } else {
+            mDiag->error(DiagMessage(mSource) << "unknown item");
+        }
+        return {};
+    }
+
+    std::unique_ptr<Value> deserializeValueFromPb(const pb::Value& pbValue,
+                                                  const ConfigDescription& config,
+                                                  StringPool* pool) {
+        const bool isWeak = pbValue.has_weak() ? pbValue.weak() : false;
+
+        std::unique_ptr<Value> value;
+        if (pbValue.has_item()) {
+            value = deserializeItemFromPb(pbValue.item(), config, pool);
+            if (!value) {
+                return {};
+            }
+
+        } else if (pbValue.has_compound_value()) {
+            const pb::CompoundValue pbCompoundValue = pbValue.compound_value();
+            if (pbCompoundValue.has_attr()) {
+                const pb::Attribute& pbAttr = pbCompoundValue.attr();
+                std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
+                attr->typeMask = pbAttr.format_flags();
+                for (const pb::Attribute_Symbol& pbSymbol : pbAttr.symbols()) {
+                    Attribute::Symbol symbol;
+                    deserializeItemCommon(pbSymbol, &symbol.symbol);
+                    if (!deserializeReferenceFromPb(pbSymbol.name(), &symbol.symbol)) {
+                        return {};
+                    }
+                    symbol.value = pbSymbol.value();
+                    attr->symbols.push_back(std::move(symbol));
+                }
+                value = std::move(attr);
+
+            } else if (pbCompoundValue.has_style()) {
+                const pb::Style& pbStyle = pbCompoundValue.style();
+                std::unique_ptr<Style> style = util::make_unique<Style>();
+                if (pbStyle.has_parent()) {
+                    style->parent = Reference();
+                    if (!deserializeReferenceFromPb(pbStyle.parent(), &style->parent.value())) {
+                        return {};
+                    }
+
+                    if (pbStyle.has_parent_source()) {
+                        Source parentSource;
+                        deserializeSourceFromPb(pbStyle.parent_source(), *mSourcePool,
+                                                &parentSource);
+                        style->parent.value().setSource(std::move(parentSource));
+                    }
+                }
+
+                for (const pb::Style_Entry& pbEntry : pbStyle.entries()) {
+                    Style::Entry entry;
+                    deserializeItemCommon(pbEntry, &entry.key);
+                    if (!deserializeReferenceFromPb(pbEntry.key(), &entry.key)) {
+                        return {};
+                    }
+
+                    entry.value = deserializeItemFromPb(pbEntry.item(), config, pool);
+                    if (!entry.value) {
+                        return {};
+                    }
+
+                    deserializeItemCommon(pbEntry, entry.value.get());
+                    style->entries.push_back(std::move(entry));
+                }
+                value = std::move(style);
+
+            } else if (pbCompoundValue.has_styleable()) {
+                const pb::Styleable& pbStyleable = pbCompoundValue.styleable();
+                std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+                for (const pb::Styleable_Entry& pbEntry : pbStyleable.entries()) {
+                    Reference attrRef;
+                    deserializeItemCommon(pbEntry, &attrRef);
+                    deserializeReferenceFromPb(pbEntry.attr(), &attrRef);
+                    styleable->entries.push_back(std::move(attrRef));
+                }
+                value = std::move(styleable);
+
+            } else if (pbCompoundValue.has_array()) {
+                const pb::Array& pbArray = pbCompoundValue.array();
+                std::unique_ptr<Array> array = util::make_unique<Array>();
+                for (const pb::Array_Entry& pbEntry : pbArray.entries()) {
+                    std::unique_ptr<Item> item = deserializeItemFromPb(pbEntry.item(), config,
+                                                                       pool);
+                    if (!item) {
+                        return {};
+                    }
+
+                    deserializeItemCommon(pbEntry, item.get());
+                    array->items.push_back(std::move(item));
+                }
+                value = std::move(array);
+
+            } else if (pbCompoundValue.has_plural()) {
+                const pb::Plural& pbPlural = pbCompoundValue.plural();
+                std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+                for (const pb::Plural_Entry& pbEntry : pbPlural.entries()) {
+                    size_t pluralIdx = deserializePluralEnumFromPb(pbEntry.arity());
+                    plural->values[pluralIdx] = deserializeItemFromPb(pbEntry.item(), config,
+                                                                      pool);
+                    if (!plural->values[pluralIdx]) {
+                        return {};
+                    }
+
+                    deserializeItemCommon(pbEntry, plural->values[pluralIdx].get());
+                }
+                value = std::move(plural);
+
+            } else {
+                mDiag->error(DiagMessage(mSource) << "unknown compound value");
+                return {};
+            }
+        } else {
+            mDiag->error(DiagMessage(mSource) << "unknown value");
+            return {};
+        }
+
+        assert(value && "forgot to set value");
+
+        value->setWeak(isWeak);
+        deserializeItemCommon(pbValue, value.get());
+        return value;
+    }
+
+    bool deserializeReferenceFromPb(const pb::Reference& pbRef, Reference* outRef) {
+        outRef->referenceType = deserializeReferenceTypeFromPb(pbRef.type());
+        outRef->privateReference = pbRef.private_();
+
+        if (!pbRef.has_id() && !pbRef.has_symbol_idx()) {
+            return false;
+        }
+
+        if (pbRef.has_id()) {
+            outRef->id = ResourceId(pbRef.id());
+        }
+
+        if (pbRef.has_symbol_idx()) {
+            StringPiece16 strSymbol = util::getString(*mSymbolPool, pbRef.symbol_idx());
+            ResourceNameRef nameRef;
+            if (!ResourceUtils::parseResourceName(strSymbol, &nameRef, nullptr)) {
+                mDiag->error(DiagMessage(mSource) << "invalid reference name '"
+                             << strSymbol << "'");
+                return false;
+            }
+
+            outRef->name = nameRef.toResourceName();
+        }
+        return true;
+    }
+
+    template <typename T>
+    void deserializeItemCommon(const T& pbItem, Value* outValue) {
+        if (pbItem.has_source()) {
+            Source source;
+            deserializeSourceFromPb(pbItem.source(), *mSourcePool, &source);
+            outValue->setSource(std::move(source));
+        }
+
+        if (pbItem.has_comment()) {
+            outValue->setComment(util::utf8ToUtf16(pbItem.comment()));
+        }
+    }
+
+private:
+    const android::ResStringPool* mValuePool;
+    const android::ResStringPool* mSourcePool;
+    const android::ResStringPool* mSymbolPool;
+    const Source mSource;
+    IDiagnostics* mDiag;
+};
+
+} // namespace
+
+std::unique_ptr<ResourceTable> deserializeTableFromPb(const pb::ResourceTable& pbTable,
+                                                      const Source& source,
+                                                      IDiagnostics* diag) {
+    std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
+
+    if (!pbTable.has_string_pool()) {
+        diag->error(DiagMessage(source) << "no string pool found");
+        return {};
+    }
+
+    android::ResStringPool valuePool;
+    android::status_t result = valuePool.setTo(pbTable.string_pool().data().data(),
+                                               pbTable.string_pool().data().size());
+    if (result != android::NO_ERROR) {
+        diag->error(DiagMessage(source) << "invalid string pool");
+        return {};
+    }
+
+    android::ResStringPool sourcePool;
+    if (pbTable.has_source_pool()) {
+        result = sourcePool.setTo(pbTable.source_pool().data().data(),
+                                  pbTable.source_pool().data().size());
+        if (result != android::NO_ERROR) {
+            diag->error(DiagMessage(source) << "invalid source pool");
+            return {};
+        }
+    }
+
+    android::ResStringPool symbolPool;
+    if (pbTable.has_symbol_pool()) {
+        result = symbolPool.setTo(pbTable.symbol_pool().data().data(),
+                                  pbTable.symbol_pool().data().size());
+        if (result != android::NO_ERROR) {
+            diag->error(DiagMessage(source) << "invalid symbol pool");
+            return {};
+        }
+    }
+
+    PackagePbDeserializer packagePbDeserializer(&valuePool, &sourcePool, &symbolPool, source, diag);
+    for (const pb::Package& pbPackage : pbTable.packages()) {
+        if (!packagePbDeserializer.deserializeFromPb(pbPackage, table.get())) {
+            return {};
+        }
+    }
+    return table;
+}
+
+std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFile& pbFile,
+                                                            const Source& source,
+                                                            IDiagnostics* diag) {
+    std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
+
+    ResourceNameRef nameRef;
+
+    // Need to create an lvalue here so that nameRef can point to something real.
+    std::u16string utf16Name = util::utf8ToUtf16(pbFile.resource_name());
+    if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) {
+        diag->error(DiagMessage(source) << "invalid resource name in compiled file header: "
+                    << pbFile.resource_name());
+        return {};
+    }
+    file->name = nameRef.toResourceName();
+    file->source.path = pbFile.source_path();
+    deserializeConfigDescriptionFromPb(pbFile.config(), &file->config);
+
+    for (const pb::CompiledFile_Symbol& pbSymbol : pbFile.exported_symbols()) {
+        // Need to create an lvalue here so that nameRef can point to something real.
+        utf16Name = util::utf8ToUtf16(pbSymbol.resource_name());
+        if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) {
+            diag->error(DiagMessage(source) << "invalid resource name for exported symbol in "
+                                               "compiled file header: "
+                                            << pbFile.resource_name());
+            return {};
+        }
+        file->exportedSymbols.push_back(
+                SourcedResourceName{ nameRef.toResourceName(), pbSymbol.line_no() });
+    }
+    return file;
+}
+
+CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size) :
+        mIn(static_cast<const uint8_t*>(data), size), mPbFile(),
+        mData(static_cast<const uint8_t*>(data)), mSize(size) {
+}
+
+const pb::CompiledFile* CompiledFileInputStream::CompiledFile() {
+    if (!mPbFile) {
+        std::unique_ptr<pb::CompiledFile> pbFile = util::make_unique<pb::CompiledFile>();
+        uint64_t pbSize = 0u;
+        if (!mIn.ReadLittleEndian64(&pbSize)) {
+            return nullptr;
+        }
+        mIn.PushLimit(static_cast<int>(pbSize));
+        if (!pbFile->ParsePartialFromCodedStream(&mIn)) {
+            return nullptr;
+        }
+
+        const size_t padding = 4 - (pbSize & 0x03);
+        mData += sizeof(uint64_t) + pbSize + padding;
+        mSize -= sizeof(uint64_t) + pbSize + padding;
+        mPbFile = std::move(pbFile);
+    }
+    return mPbFile.get();
+}
+
+const void* CompiledFileInputStream::data() {
+    if (!mPbFile) {
+        if (!CompiledFile()) {
+            return nullptr;
+        }
+    }
+    return mData;
+}
+
+size_t CompiledFileInputStream::size() {
+    if (!mPbFile) {
+        if (!CompiledFile()) {
+            return 0;
+        }
+    }
+    return mSize;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
new file mode 100644
index 0000000..4a2176d
--- /dev/null
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2016 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 "Resource.h"
+#include "ResourceTable.h"
+#include "StringPool.h"
+#include "ValueVisitor.h"
+#include "proto/ProtoHelpers.h"
+#include "proto/ProtoSerialize.h"
+#include "util/BigBuffer.h"
+
+namespace aapt {
+
+namespace {
+
+class PbSerializerVisitor : public RawValueVisitor {
+public:
+    using RawValueVisitor::visit;
+
+    /**
+     * Constructor to use when expecting to serialize any value.
+     */
+    PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool, pb::Value* outPbValue) :
+            mSourcePool(sourcePool), mSymbolPool(symbolPool), mOutPbValue(outPbValue),
+            mOutPbItem(nullptr) {
+    }
+
+    /**
+     * Constructor to use when expecting to serialize an Item.
+     */
+    PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool, pb::Item* outPbItem) :
+            mSourcePool(sourcePool), mSymbolPool(symbolPool), mOutPbValue(nullptr),
+            mOutPbItem(outPbItem) {
+    }
+
+    void visit(Reference* ref) override {
+        serializeReferenceToPb(*ref, getPbItem()->mutable_ref());
+    }
+
+    void visit(String* str) override {
+        getPbItem()->mutable_str()->set_idx(str->value.getIndex());
+    }
+
+    void visit(StyledString* str) override {
+        getPbItem()->mutable_str()->set_idx(str->value.getIndex());
+    }
+
+    void visit(FileReference* file) override {
+        getPbItem()->mutable_file()->set_path_idx(file->path.getIndex());
+    }
+
+    void visit(Id* id) override {
+        getPbItem()->mutable_id();
+    }
+
+    void visit(RawString* rawStr) override {
+        getPbItem()->mutable_raw_str()->set_idx(rawStr->value.getIndex());
+    }
+
+    void visit(BinaryPrimitive* prim) override {
+        android::Res_value val = {};
+        prim->flatten(&val);
+
+        pb::Primitive* pbPrim = getPbItem()->mutable_prim();
+        pbPrim->set_type(val.dataType);
+        pbPrim->set_data(val.data);
+    }
+
+    void visitItem(Item* item) override {
+        assert(false && "unimplemented item");
+    }
+
+    void visit(Attribute* attr) override {
+        pb::Attribute* pbAttr = getPbCompoundValue()->mutable_attr();
+        pbAttr->set_format_flags(attr->typeMask);
+        pbAttr->set_min_int(attr->minInt);
+        pbAttr->set_max_int(attr->maxInt);
+
+        for (auto& symbol : attr->symbols) {
+            pb::Attribute_Symbol* pbSymbol = pbAttr->add_symbols();
+            serializeItemCommonToPb(symbol.symbol, pbSymbol);
+            serializeReferenceToPb(symbol.symbol, pbSymbol->mutable_name());
+            pbSymbol->set_value(symbol.value);
+        }
+    }
+
+    void visit(Style* style) override {
+        pb::Style* pbStyle = getPbCompoundValue()->mutable_style();
+        if (style->parent) {
+            serializeReferenceToPb(style->parent.value(), pbStyle->mutable_parent());
+            serializeSourceToPb(style->parent.value().getSource(),
+                                mSourcePool,
+                                pbStyle->mutable_parent_source());
+        }
+
+        for (Style::Entry& entry : style->entries) {
+            pb::Style_Entry* pbEntry = pbStyle->add_entries();
+            serializeReferenceToPb(entry.key, pbEntry->mutable_key());
+
+            pb::Item* pbItem = pbEntry->mutable_item();
+            serializeItemCommonToPb(*entry.value, pbEntry);
+            PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbItem);
+            entry.value->accept(&subVisitor);
+        }
+    }
+
+    void visit(Styleable* styleable) override {
+        pb::Styleable* pbStyleable = getPbCompoundValue()->mutable_styleable();
+        for (Reference& entry : styleable->entries) {
+            pb::Styleable_Entry* pbEntry = pbStyleable->add_entries();
+            serializeItemCommonToPb(entry, pbEntry);
+            serializeReferenceToPb(entry, pbEntry->mutable_attr());
+        }
+    }
+
+    void visit(Array* array) override {
+        pb::Array* pbArray = getPbCompoundValue()->mutable_array();
+        for (auto& value : array->items) {
+            pb::Array_Entry* pbEntry = pbArray->add_entries();
+            serializeItemCommonToPb(*value, pbEntry);
+            PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbEntry->mutable_item());
+            value->accept(&subVisitor);
+        }
+    }
+
+    void visit(Plural* plural) override {
+        pb::Plural* pbPlural = getPbCompoundValue()->mutable_plural();
+        const size_t count = plural->values.size();
+        for (size_t i = 0; i < count; i++) {
+            if (!plural->values[i]) {
+                // No plural value set here.
+                continue;
+            }
+
+            pb::Plural_Entry* pbEntry = pbPlural->add_entries();
+            pbEntry->set_arity(serializePluralEnumToPb(i));
+            pb::Item* pbElement = pbEntry->mutable_item();
+            serializeItemCommonToPb(*plural->values[i], pbEntry);
+            PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbElement);
+            plural->values[i]->accept(&subVisitor);
+        }
+    }
+
+private:
+    pb::Item* getPbItem() {
+        if (mOutPbValue) {
+            return mOutPbValue->mutable_item();
+        }
+        return mOutPbItem;
+    }
+
+    pb::CompoundValue* getPbCompoundValue() {
+        assert(mOutPbValue);
+        return mOutPbValue->mutable_compound_value();
+    }
+
+    template <typename T>
+    void serializeItemCommonToPb(const Item& item, T* pbItem) {
+        serializeSourceToPb(item.getSource(), mSourcePool, pbItem->mutable_source());
+        if (!item.getComment().empty()) {
+            pbItem->set_comment(util::utf16ToUtf8(item.getComment()));
+        }
+    }
+
+    void serializeReferenceToPb(const Reference& ref, pb::Reference* pbRef) {
+        if (ref.id) {
+            pbRef->set_id(ref.id.value().id);
+        } else if (ref.name) {
+            StringPool::Ref symbolRef = mSymbolPool->makeRef(ref.name.value().toString());
+            pbRef->set_symbol_idx(static_cast<uint32_t>(symbolRef.getIndex()));
+        }
+        pbRef->set_private_(ref.privateReference);
+        pbRef->set_type(serializeReferenceTypeToPb(ref.referenceType));
+    }
+
+    StringPool* mSourcePool;
+    StringPool* mSymbolPool;
+    pb::Value* mOutPbValue;
+    pb::Item* mOutPbItem;
+};
+
+} // namespace
+
+std::unique_ptr<pb::ResourceTable> serializeTableToPb(ResourceTable* table) {
+    // We must do this before writing the resources, since the string pool IDs may change.
+    table->stringPool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+        int diff = a.context.priority - b.context.priority;
+        if (diff < 0) return true;
+        if (diff > 0) return false;
+        diff = a.context.config.compare(b.context.config);
+        if (diff < 0) return true;
+        if (diff > 0) return false;
+        return a.value < b.value;
+    });
+    table->stringPool.prune();
+
+    std::unique_ptr<pb::ResourceTable> pbTable = util::make_unique<pb::ResourceTable>();
+    serializeStringPoolToPb(table->stringPool, pbTable->mutable_string_pool());
+
+    StringPool sourcePool, symbolPool;
+
+    for (auto& package : table->packages) {
+        pb::Package* pbPackage = pbTable->add_packages();
+        if (package->id) {
+            pbPackage->set_package_id(package->id.value());
+        }
+        pbPackage->set_package_name(util::utf16ToUtf8(package->name));
+
+        for (auto& type : package->types) {
+            pb::Type* pbType = pbPackage->add_types();
+            if (type->id) {
+                pbType->set_id(type->id.value());
+            }
+            pbType->set_name(util::utf16ToUtf8(toString(type->type)));
+
+            for (auto& entry : type->entries) {
+                pb::Entry* pbEntry = pbType->add_entries();
+                if (entry->id) {
+                    pbEntry->set_id(entry->id.value());
+                }
+                pbEntry->set_name(util::utf16ToUtf8(entry->name));
+
+                // Write the SymbolStatus struct.
+                pb::SymbolStatus* pbStatus = pbEntry->mutable_symbol_status();
+                pbStatus->set_visibility(serializeVisibilityToPb(entry->symbolStatus.state));
+                serializeSourceToPb(entry->symbolStatus.source, &sourcePool,
+                                    pbStatus->mutable_source());
+                pbStatus->set_comment(util::utf16ToUtf8(entry->symbolStatus.comment));
+
+                for (auto& configValue : entry->values) {
+                    pb::ConfigValue* pbConfigValue = pbEntry->add_config_values();
+                    serializeConfig(configValue.config, pbConfigValue->mutable_config());
+
+                    pb::Value* pbValue = pbConfigValue->mutable_value();
+                    serializeSourceToPb(configValue.value->getSource(), &sourcePool,
+                                        pbValue->mutable_source());
+                    if (!configValue.value->getComment().empty()) {
+                        pbValue->set_comment(util::utf16ToUtf8(configValue.value->getComment()));
+                    }
+
+                    if (configValue.value->isWeak()) {
+                        pbValue->set_weak(true);
+                    }
+
+                    PbSerializerVisitor visitor(&sourcePool, &symbolPool, pbValue);
+                    configValue.value->accept(&visitor);
+                }
+            }
+        }
+    }
+
+    serializeStringPoolToPb(sourcePool, pbTable->mutable_source_pool());
+    serializeStringPoolToPb(symbolPool, pbTable->mutable_symbol_pool());
+    return pbTable;
+}
+
+std::unique_ptr<pb::CompiledFile> serializeCompiledFileToPb(const ResourceFile& file) {
+    std::unique_ptr<pb::CompiledFile> pbFile = util::make_unique<pb::CompiledFile>();
+    pbFile->set_resource_name(util::utf16ToUtf8(file.name.toString()));
+    pbFile->set_source_path(file.source.path);
+    serializeConfig(file.config, pbFile->mutable_config());
+
+    for (const SourcedResourceName& exported : file.exportedSymbols) {
+        pb::CompiledFile_Symbol* pbSymbol = pbFile->add_exported_symbols();
+        pbSymbol->set_resource_name(util::utf16ToUtf8(exported.name.toString()));
+        pbSymbol->set_line_no(exported.line);
+    }
+    return pbFile;
+}
+
+CompiledFileOutputStream::CompiledFileOutputStream(google::protobuf::io::ZeroCopyOutputStream* out,
+                                                   pb::CompiledFile* pbFile) :
+        mOut(out), mPbFile(pbFile) {
+}
+
+bool CompiledFileOutputStream::ensureFileWritten() {
+    if (mPbFile) {
+        const uint64_t pbSize = mPbFile->ByteSize();
+        mOut.WriteLittleEndian64(pbSize);
+        mPbFile->SerializeWithCachedSizes(&mOut);
+        const size_t padding = 4 - (pbSize & 0x03);
+        if (padding > 0) {
+            uint32_t zero = 0u;
+            mOut.WriteRaw(&zero, padding);
+        }
+        mPbFile = nullptr;
+    }
+    return !mOut.HadError();
+}
+
+bool CompiledFileOutputStream::Write(const void* data, int size) {
+    if (!ensureFileWritten()) {
+        return false;
+    }
+    mOut.WriteRaw(data, size);
+    return !mOut.HadError();
+}
+
+bool CompiledFileOutputStream::Finish() {
+    return ensureFileWritten();
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp
new file mode 100644
index 0000000..1061b8f
--- /dev/null
+++ b/tools/aapt2/proto/TableProtoSerializer_test.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 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 "ResourceTable.h"
+#include "proto/ProtoSerialize.h"
+#include "test/Builders.h"
+#include "test/Common.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(TableProtoSerializer, SerializeSinglePackage) {
+    std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+    std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+            .setPackageId(u"com.app.a", 0x7f)
+            .addFileReference(u"@com.app.a:layout/main", ResourceId(0x7f020000),
+                              u"res/layout/main.xml")
+            .addReference(u"@com.app.a:layout/other", ResourceId(0x7f020001),
+                          u"@com.app.a:layout/main")
+            .addString(u"@com.app.a:string/text", {}, u"hi")
+            .addValue(u"@com.app.a:id/foo", {}, util::make_unique<Id>())
+            .build();
+
+    Symbol publicSymbol;
+    publicSymbol.state = SymbolState::kPublic;
+    ASSERT_TRUE(table->setSymbolState(test::parseNameOrDie(u"@com.app.a:layout/main"),
+                                      ResourceId(0x7f020000),
+                                      publicSymbol, context->getDiagnostics()));
+
+    Id* id = test::getValue<Id>(table.get(), u"@com.app.a:id/foo");
+    ASSERT_NE(nullptr, id);
+
+    // Make a plural.
+    std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+    plural->values[Plural::One] = util::make_unique<String>(table->stringPool.makeRef(u"one"));
+    ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:plurals/hey"),
+                                   ConfigDescription{}, std::move(plural),
+                                   context->getDiagnostics()));
+
+    std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table.get());
+    ASSERT_NE(nullptr, pbTable);
+
+    std::unique_ptr<ResourceTable> newTable = deserializeTableFromPb(*pbTable,
+                                                                     Source{ "test" },
+                                                                     context->getDiagnostics());
+    ASSERT_NE(nullptr, newTable);
+
+    Id* newId = test::getValue<Id>(newTable.get(), u"@com.app.a:id/foo");
+    ASSERT_NE(nullptr, newId);
+    EXPECT_EQ(id->isWeak(), newId->isWeak());
+
+    Maybe<ResourceTable::SearchResult> result = newTable->findResource(
+            test::parseNameOrDie(u"@com.app.a:layout/main"));
+    AAPT_ASSERT_TRUE(result);
+    EXPECT_EQ(SymbolState::kPublic, result.value().type->symbolStatus.state);
+    EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state);
+}
+
+TEST(TableProtoSerializer, SerializeFileHeader) {
+    std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+    ResourceFile f;
+    f.config = test::parseConfigOrDie("hdpi-v9");
+    f.name = test::parseNameOrDie(u"@com.app.a:layout/main");
+    f.source.path = "res/layout-hdpi-v9/main.xml";
+    f.exportedSymbols.push_back(SourcedResourceName{ test::parseNameOrDie(u"@+id/unchecked"), 23u });
+
+    const std::string expectedData = "1234";
+
+    std::unique_ptr<pb::CompiledFile> pbFile = serializeCompiledFileToPb(f);
+
+    std::string outputStr;
+    {
+        google::protobuf::io::StringOutputStream outStream(&outputStr);
+        CompiledFileOutputStream outFileStream(&outStream, pbFile.get());
+
+        ASSERT_TRUE(outFileStream.Write(expectedData.data(), expectedData.size()));
+        ASSERT_TRUE(outFileStream.Finish());
+    }
+
+    CompiledFileInputStream inFileStream(outputStr.data(), outputStr.size());
+    const pb::CompiledFile* newPbFile = inFileStream.CompiledFile();
+    ASSERT_NE(nullptr, newPbFile);
+
+    std::unique_ptr<ResourceFile> file = deserializeCompiledFileFromPb(*newPbFile, Source{ "test" },
+                                                                       context->getDiagnostics());
+    ASSERT_NE(nullptr, file);
+
+    std::string actualData((const char*)inFileStream.data(), inFileStream.size());
+    EXPECT_EQ(expectedData, actualData);
+    EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(inFileStream.data()) & 0x03);
+
+    ASSERT_EQ(1u, file->exportedSymbols.size());
+    EXPECT_EQ(test::parseNameOrDie(u"@+id/unchecked"), file->exportedSymbols[0].name);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 6b7a63cf..3417703 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -19,8 +19,6 @@
 #include "ResourceValues.h"
 #include "Source.h"
 #include "ValueVisitor.h"
-
-#include "flatten/ResourceTypeExtensions.h"
 #include "unflatten/BinaryResourceParser.h"
 #include "unflatten/ResChunkPullParser.h"
 #include "util/Util.h"
@@ -36,6 +34,8 @@
 
 using namespace android;
 
+namespace {
+
 /*
  * Visitor that converts a reference's resource ID to a resource name,
  * given a mapping from resource ID to resource name.
@@ -66,6 +66,8 @@
     }
 };
 
+} // namespace
+
 BinaryResourceParser::BinaryResourceParser(IAaptContext* context, ResourceTable* table,
                                            const Source& source, const void* data, size_t len) :
         mContext(context), mTable(table), mSource(source), mData(data), mDataLen(len) {
@@ -97,106 +99,6 @@
     return !error;
 }
 
-Maybe<Reference> BinaryResourceParser::getSymbol(const void* data) {
-    if (!mSymbolEntries || mSymbolEntryCount == 0) {
-        return {};
-    }
-
-    if ((uintptr_t) data < (uintptr_t) mData) {
-        return {};
-    }
-
-    // We only support 32 bit offsets right now.
-    const uintptr_t offset = (uintptr_t) data - (uintptr_t) mData;
-    if (offset > std::numeric_limits<uint32_t>::max()) {
-        return {};
-    }
-
-    for (size_t i = 0; i < mSymbolEntryCount; i++) {
-        if (util::deviceToHost32(mSymbolEntries[i].offset) == offset) {
-            // This offset is a symbol!
-            const StringPiece16 str = util::getString(
-                    mSymbolPool, util::deviceToHost32(mSymbolEntries[i].name.index));
-
-            ResourceNameRef nameRef;
-            bool privateRef = false;
-            if (!ResourceUtils::parseResourceName(str, &nameRef, &privateRef)) {
-                return {};
-            }
-
-            // Since we scan the symbol table in order, we can start looking for the
-            // next symbol from this point.
-            mSymbolEntryCount -= i + 1;
-            mSymbolEntries += i + 1;
-
-            Reference ref(nameRef);
-            ref.privateReference = privateRef;
-            return Maybe<Reference>(std::move(ref));
-        }
-    }
-    return {};
-}
-
-/**
- * Parses the SymbolTable_header, which is present on non-final resource tables
- * after the compile phase.
- *
- * | SymbolTable_header |
- * |--------------------|
- * |SymbolTable_entry 0 |
- * |SymbolTable_entry 1 |
- * | ...                |
- * |SymbolTable_entry n |
- * |--------------------|
- *
- */
-bool BinaryResourceParser::parseSymbolTable(const ResChunk_header* chunk) {
-    const SymbolTable_header* header = convertTo<SymbolTable_header>(chunk);
-    if (!header) {
-        mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                          << "corrupt SymbolTable_header");
-        return false;
-    }
-
-    const uint32_t entrySizeBytes =
-            util::deviceToHost32(header->count) * sizeof(SymbolTable_entry);
-    if (entrySizeBytes > getChunkDataLen(&header->header)) {
-        mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                          << "SymbolTable_header data section too long");
-        return false;
-    }
-
-    mSymbolEntries = (const SymbolTable_entry*) getChunkData(&header->header);
-    mSymbolEntryCount = util::deviceToHost32(header->count);
-
-    // Skip over the symbol entries and parse the StringPool chunk that should be next.
-    ResChunkPullParser parser(getChunkData(&header->header) + entrySizeBytes,
-                              getChunkDataLen(&header->header) - entrySizeBytes);
-    if (!ResChunkPullParser::isGoodEvent(parser.next())) {
-        mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                          << "failed to parse chunk in SymbolTable: "
-                                          << parser.getLastError());
-        return false;
-    }
-
-    const ResChunk_header* nextChunk = parser.getChunk();
-    if (util::deviceToHost16(nextChunk->type) != android::RES_STRING_POOL_TYPE) {
-        mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                          << "expected string pool in SymbolTable but got "
-                                          << "chunk of type "
-                                          << (int) util::deviceToHost16(nextChunk->type));
-        return false;
-    }
-
-    if (mSymbolPool.setTo(nextChunk, util::deviceToHost32(nextChunk->size)) != NO_ERROR) {
-        mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                          << "corrupt string pool in SymbolTable: "
-                                          << mSymbolPool.getError());
-        return false;
-    }
-    return true;
-}
-
 /**
  * Parses the resource table, which contains all the packages, types, and entries.
  */
@@ -230,24 +132,6 @@
             }
             break;
 
-        case RES_TABLE_SYMBOL_TABLE_TYPE:
-            if (!parseSymbolTable(parser.getChunk())) {
-                return false;
-            }
-            break;
-
-        case RES_TABLE_SOURCE_POOL_TYPE: {
-            status_t err = mSourcePool.setTo(getChunkData(parser.getChunk()),
-                                             getChunkDataLen(parser.getChunk()));
-            if (err != NO_ERROR) {
-                mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                                  << "corrupt source string pool in ResTable: "
-                                                  << mSourcePool.getError());
-                return false;
-            }
-            break;
-        }
-
         case android::RES_TABLE_PACKAGE_TYPE:
             if (!parsePackage(parser.getChunk())) {
                 return false;
@@ -350,12 +234,6 @@
             }
             break;
 
-        case RES_TABLE_PUBLIC_TYPE:
-            if (!parsePublic(package, parser.getChunk())) {
-                return false;
-            }
-            break;
-
         default:
             mContext->getDiagnostics()
                     ->warn(DiagMessage(mSource)
@@ -375,97 +253,7 @@
     // Now go through the table and change local resource ID references to
     // symbolic references.
     ReferenceIdToNameVisitor visitor(&mIdIndex);
-    for (auto& package : mTable->packages) {
-        for (auto& type : package->types) {
-            for (auto& entry : type->entries) {
-                for (auto& configValue : entry->values) {
-                    configValue.value->accept(&visitor);
-                }
-            }
-        }
-    }
-    return true;
-}
-
-bool BinaryResourceParser::parsePublic(const ResourceTablePackage* package,
-                                       const ResChunk_header* chunk) {
-    const Public_header* header = convertTo<Public_header>(chunk);
-    if (!header) {
-        mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                          << "corrupt Public_header chunk");
-        return false;
-    }
-
-    if (header->typeId == 0) {
-        mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                          << "invalid type ID "
-                                          << (int) header->typeId);
-        return false;
-    }
-
-    StringPiece16 typeStr16 = util::getString(mTypePool, header->typeId - 1);
-    const ResourceType* parsedType = parseResourceType(typeStr16);
-    if (!parsedType) {
-        mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                          << "invalid type '" << typeStr16 << "'");
-        return false;
-    }
-
-    const uintptr_t chunkEnd = (uintptr_t) chunk + util::deviceToHost32(chunk->size);
-    const Public_entry* entry = (const Public_entry*) getChunkData(&header->header);
-    for (uint32_t i = 0; i < util::deviceToHost32(header->count); i++) {
-        if ((uintptr_t) entry + sizeof(*entry) > chunkEnd) {
-            mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                              << "Public_entry data section is too long");
-            return false;
-        }
-
-        const ResourceId resId(package->id.value(), header->typeId,
-                               util::deviceToHost16(entry->entryId));
-
-        const ResourceName name(package->name, *parsedType,
-                                util::getString(mKeyPool, entry->key.index).toString());
-
-        Symbol symbol;
-        if (mSourcePool.getError() == NO_ERROR) {
-            symbol.source.path = util::utf16ToUtf8(util::getString(
-                    mSourcePool, util::deviceToHost32(entry->source.path.index)));
-            symbol.source.line = util::deviceToHost32(entry->source.line);
-        }
-
-        StringPiece16 comment = util::getString(mSourcePool,
-                                                util::deviceToHost32(entry->source.comment.index));
-        if (!comment.empty()) {
-            symbol.comment = comment.toString();
-        }
-
-        switch (util::deviceToHost16(entry->state)) {
-        case Public_entry::kPrivate:
-            symbol.state = SymbolState::kPrivate;
-            break;
-
-        case Public_entry::kPublic:
-            symbol.state = SymbolState::kPublic;
-            break;
-
-        case Public_entry::kUndefined:
-            symbol.state = SymbolState::kUndefined;
-            break;
-        }
-
-        if (!mTable->setSymbolStateAllowMangled(name, resId, symbol, mContext->getDiagnostics())) {
-            return false;
-        }
-
-        // Add this resource name->id mapping to the index so
-        // that we can resolve all ID references to name references.
-        auto cacheIter = mIdIndex.find(resId);
-        if (cacheIter == mIdIndex.end()) {
-            mIdIndex.insert({ resId, name });
-        }
-
-        entry++;
-    }
+    visitAllValuesInTable(mTable, &visitor);
     return true;
 }
 
@@ -545,25 +333,12 @@
         const ResourceId resId(package->id.value(), type->id, static_cast<uint16_t>(it.index()));
 
         std::unique_ptr<Value> resourceValue;
-        const ResTable_entry_source* sourceBlock = nullptr;
-
         if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
             const ResTable_map_entry* mapEntry = static_cast<const ResTable_map_entry*>(entry);
-            if (util::deviceToHost32(mapEntry->size) - sizeof(*mapEntry) == sizeof(*sourceBlock)) {
-                const uint8_t* data = (const uint8_t*) mapEntry;
-                data += util::deviceToHost32(mapEntry->size) - sizeof(*sourceBlock);
-                sourceBlock = (const ResTable_entry_source*) data;
-            }
 
             // TODO(adamlesinski): Check that the entry count is valid.
             resourceValue = parseMapEntry(name, config, mapEntry);
         } else {
-            if (util::deviceToHost32(entry->size) - sizeof(*entry) == sizeof(*sourceBlock)) {
-                const uint8_t* data = (const uint8_t*) entry;
-                data += util::deviceToHost32(entry->size) - sizeof(*sourceBlock);
-                sourceBlock = (const ResTable_entry_source*) data;
-            }
-
             const Res_value* value = (const Res_value*)(
                     (const uint8_t*) entry + util::deviceToHost32(entry->size));
             resourceValue = parseValue(name, config, value, entry->flags);
@@ -577,30 +352,6 @@
             return false;
         }
 
-        Source source = mSource;
-        if (sourceBlock) {
-            StringPiece path = util::getString8(mSourcePool,
-                                                util::deviceToHost32(sourceBlock->path.index));
-            if (!path.empty()) {
-                source.path = path.toString();
-            }
-            source.line = util::deviceToHost32(sourceBlock->line);
-
-            if (Style* style = valueCast<Style>(resourceValue.get())) {
-                // The parent's source is the same as the resource itself, set it here.
-                if (style->parent) {
-                    style->parent.value().setSource(source);
-                }
-            }
-        }
-
-        StringPiece16 comment = util::getString(mSourcePool,
-                                                util::deviceToHost32(sourceBlock->comment.index));
-        if (!comment.empty()) {
-            resourceValue->setComment(comment);
-        }
-
-        resourceValue->setSource(source);
         if (!mTable->addResourceAllowMangled(name, config, std::move(resourceValue),
                                              mContext->getDiagnostics())) {
             return false;
@@ -674,26 +425,15 @@
         const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ?
                 Reference::Type::kResource : Reference::Type::kAttribute;
 
-        if (data != 0) {
-            // This is a normal reference.
-            return util::make_unique<Reference>(data, type);
+        if (data == 0) {
+            // A reference of 0, must be the magic @null reference.
+            Res_value nullType = {};
+            nullType.dataType = Res_value::TYPE_REFERENCE;
+            return util::make_unique<BinaryPrimitive>(nullType);
         }
 
-        // This reference has an invalid ID. Check if it is an unresolved symbol.
-        if (Maybe<Reference> ref = getSymbol(&value->data)) {
-            ref.value().referenceType = type;
-            return util::make_unique<Reference>(std::move(ref.value()));
-        }
-
-        // This is not an unresolved symbol, so it must be the magic @null reference.
-        Res_value nullType = {};
-        nullType.dataType = Res_value::TYPE_REFERENCE;
-        return util::make_unique<BinaryPrimitive>(nullType);
-    }
-
-    if (value->dataType == ExtendedTypes::TYPE_RAW_STRING) {
-        return util::make_unique<RawString>(mTable->stringPool.makeRef(
-                util::getString(mValuePool, data), StringPool::Context{ 1, config }));
+        // This is a normal reference.
+        return util::make_unique<Reference>(data, type);
     }
 
     // Treat this as a raw binary primitive.
@@ -712,8 +452,6 @@
             return parseAttr(name, config, map);
         case ResourceType::kArray:
             return parseArray(name, config, map);
-        case ResourceType::kStyleable:
-            return parseStyleable(name, config, map);
         case ResourceType::kPlurals:
             return parsePlural(name, config, map);
         default:
@@ -727,51 +465,23 @@
                                                         const ConfigDescription& config,
                                                         const ResTable_map_entry* map) {
     std::unique_ptr<Style> style = util::make_unique<Style>();
-    if (util::deviceToHost32(map->parent.ident) == 0) {
-        // The parent is either not set or it is an unresolved symbol.
-        // Check to see if it is a symbol.
-        style->parent = getSymbol(&map->parent.ident);
-
-    } else {
-         // The parent is a regular reference to a resource.
+    if (util::deviceToHost32(map->parent.ident) != 0) {
+        // The parent is a regular reference to a resource.
         style->parent = Reference(util::deviceToHost32(map->parent.ident));
     }
 
     for (const ResTable_map& mapEntry : map) {
         if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
-            if (style->entries.empty()) {
-                mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                                  << "out-of-sequence meta data in style");
-                return {};
-            }
-            collectMetaData(mapEntry, &style->entries.back().key);
             continue;
         }
 
-        style->entries.emplace_back();
-        Style::Entry& styleEntry = style->entries.back();
-
-        if (util::deviceToHost32(mapEntry.name.ident) == 0) {
-            // The map entry's key (attribute) is not set. This must be
-            // a symbol reference, so resolve it.
-            Maybe<Reference> symbol = getSymbol(&mapEntry.name.ident);
-            if (!symbol) {
-                mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                                  << "unresolved style attribute");
-                return {};
-            }
-            styleEntry.key = std::move(symbol.value());
-
-        } else {
-            // The map entry's key (attribute) is a regular reference.
-            styleEntry.key.id = ResourceId(util::deviceToHost32(mapEntry.name.ident));
-        }
-
-        // Parse the attribute's value.
+        Style::Entry styleEntry;
+        styleEntry.key = Reference(util::deviceToHost32(mapEntry.name.ident));
         styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
         if (!styleEntry.value) {
             return {};
         }
+        style->entries.push_back(std::move(styleEntry));
     }
     return style;
 }
@@ -807,22 +517,7 @@
         if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
             Attribute::Symbol symbol;
             symbol.value = util::deviceToHost32(mapEntry.value.data);
-            if (util::deviceToHost32(mapEntry.name.ident) == 0) {
-                // The map entry's key (id) is not set. This must be
-                // a symbol reference, so resolve it.
-                Maybe<Reference> ref = getSymbol(&mapEntry.name.ident);
-                if (!ref) {
-                    mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                                      << "unresolved attribute symbol");
-                    return {};
-                }
-                symbol.symbol = std::move(ref.value());
-
-            } else {
-                // The map entry's key (id) is a regular reference.
-                symbol.symbol.id = ResourceId(util::deviceToHost32(mapEntry.name.ident));
-            }
-
+            symbol.symbol = Reference(util::deviceToHost32(mapEntry.name.ident));
             attr->symbols.push_back(std::move(symbol));
         }
     }
@@ -831,115 +526,26 @@
     return attr;
 }
 
-static bool isMetaDataEntry(const ResTable_map& mapEntry) {
-    switch (util::deviceToHost32(mapEntry.name.ident)) {
-    case ExtendedResTableMapTypes::ATTR_SOURCE_PATH:
-    case ExtendedResTableMapTypes::ATTR_SOURCE_LINE:
-    case ExtendedResTableMapTypes::ATTR_COMMENT:
-        return true;
-    }
-    return false;
-}
-
-bool BinaryResourceParser::collectMetaData(const ResTable_map& mapEntry, Value* value) {
-    switch (util::deviceToHost32(mapEntry.name.ident)) {
-    case ExtendedResTableMapTypes::ATTR_SOURCE_PATH:
-        value->setSource(Source(util::getString8(mSourcePool,
-                                                 util::deviceToHost32(mapEntry.value.data))));
-        return true;
-        break;
-
-    case ExtendedResTableMapTypes::ATTR_SOURCE_LINE:
-        value->setSource(value->getSource().withLine(util::deviceToHost32(mapEntry.value.data)));
-        return true;
-        break;
-
-    case ExtendedResTableMapTypes::ATTR_COMMENT:
-        value->setComment(util::getString(mSourcePool, util::deviceToHost32(mapEntry.value.data)));
-        return true;
-        break;
-    }
-    return false;
-}
-
 std::unique_ptr<Array> BinaryResourceParser::parseArray(const ResourceNameRef& name,
                                                         const ConfigDescription& config,
                                                         const ResTable_map_entry* map) {
     std::unique_ptr<Array> array = util::make_unique<Array>();
-    Source source;
     for (const ResTable_map& mapEntry : map) {
-        if (isMetaDataEntry(mapEntry)) {
-            if (array->items.empty()) {
-                mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                                  << "out-of-sequence meta data in array");
-                return {};
-            }
-            collectMetaData(mapEntry, array->items.back().get());
-            continue;
-        }
-
         array->items.push_back(parseValue(name, config, &mapEntry.value, 0));
     }
     return array;
 }
 
-std::unique_ptr<Styleable> BinaryResourceParser::parseStyleable(const ResourceNameRef& name,
-                                                                const ConfigDescription& config,
-                                                                const ResTable_map_entry* map) {
-    std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
-    for (const ResTable_map& mapEntry : map) {
-        if (isMetaDataEntry(mapEntry)) {
-            if (styleable->entries.empty()) {
-                mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                                  << "out-of-sequence meta data in styleable");
-                return {};
-            }
-            collectMetaData(mapEntry, &styleable->entries.back());
-            continue;
-        }
-
-        if (util::deviceToHost32(mapEntry.name.ident) == 0) {
-            // The map entry's key (attribute) is not set. This must be
-            // a symbol reference, so resolve it.
-            Maybe<Reference> ref = getSymbol(&mapEntry.name.ident);
-            if (!ref) {
-                mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                                  << "unresolved styleable symbol");
-                return {};
-            }
-            styleable->entries.emplace_back(std::move(ref.value()));
-
-        } else {
-            // The map entry's key (attribute) is a regular reference.
-            styleable->entries.emplace_back(util::deviceToHost32(mapEntry.name.ident));
-        }
-    }
-    return styleable;
-}
-
 std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef& name,
                                                           const ConfigDescription& config,
                                                           const ResTable_map_entry* map) {
     std::unique_ptr<Plural> plural = util::make_unique<Plural>();
-    Item* lastEntry = nullptr;
     for (const ResTable_map& mapEntry : map) {
-        if (isMetaDataEntry(mapEntry)) {
-            if (!lastEntry) {
-                mContext->getDiagnostics()->error(DiagMessage(mSource)
-                                                  << "out-of-sequence meta data in plural");
-                return {};
-            }
-            collectMetaData(mapEntry, lastEntry);
-            continue;
-        }
-
         std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
         if (!item) {
             return {};
         }
 
-        lastEntry = item.get();
-
         switch (util::deviceToHost32(mapEntry.name.ident)) {
             case ResTable_map::ATTR_ZERO:
                 plural->values[Plural::Zero] = std::move(item);
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.h b/tools/aapt2/unflatten/BinaryResourceParser.h
index 0745a59..12bc13d 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.h
+++ b/tools/aapt2/unflatten/BinaryResourceParser.h
@@ -55,14 +55,8 @@
     bool parse();
 
 private:
-    // Helper method to retrieve the symbol name for a given table offset specified
-    // as a pointer.
-    Maybe<Reference> getSymbol(const void* data);
-
     bool parseTable(const android::ResChunk_header* chunk);
-    bool parseSymbolTable(const android::ResChunk_header* chunk);
     bool parsePackage(const android::ResChunk_header* chunk);
-    bool parsePublic(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
     bool parseTypeSpec(const android::ResChunk_header* chunk);
     bool parseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
 
@@ -87,10 +81,6 @@
                                         const ConfigDescription& config,
                                         const android::ResTable_map_entry* map);
 
-    std::unique_ptr<Styleable> parseStyleable(const ResourceNameRef& name,
-                                              const ConfigDescription& config,
-                                              const android::ResTable_map_entry* map);
-
     /**
      * If the mapEntry is a special type that denotes meta data (source, comment), then it is
      * read and added to the Value.
@@ -106,23 +96,6 @@
     const void* mData;
     const size_t mDataLen;
 
-    // The array of symbol entries. Each element points to an offset
-    // in the table and an index into the symbol table string pool.
-    const SymbolTable_entry* mSymbolEntries = nullptr;
-
-    // Number of symbol entries.
-    size_t mSymbolEntryCount = 0;
-
-    // The symbol table string pool. Holds the names of symbols
-    // referenced in this table but not defined nor resolved to an
-    // ID.
-    android::ResStringPool mSymbolPool;
-
-    // The source string pool. Resource entries may have an extra
-    // field that points into this string pool, which denotes where
-    // the resource was parsed from originally.
-    android::ResStringPool mSourcePool;
-
     // The standard value string pool for resource values.
     android::ResStringPool mValuePool;
 
diff --git a/tools/aapt2/unflatten/FileExportHeaderReader.h b/tools/aapt2/unflatten/FileExportHeaderReader.h
deleted file mode 100644
index e552ea1..0000000
--- a/tools/aapt2/unflatten/FileExportHeaderReader.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2015 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_UNFLATTEN_FILEEXPORTHEADERREADER_H
-#define AAPT_UNFLATTEN_FILEEXPORTHEADERREADER_H
-
-#include "ResChunkPullParser.h"
-#include "Resource.h"
-#include "ResourceUtils.h"
-
-#include "flatten/ResourceTypeExtensions.h"
-#include "util/StringPiece.h"
-#include "util/Util.h"
-
-#include <androidfw/ResourceTypes.h>
-#include <sstream>
-#include <string>
-
-namespace aapt {
-
-static ssize_t parseFileExportHeaderImpl(const void* data, const size_t len,
-                                         const FileExport_header** outFileExport,
-                                         const ExportedSymbol** outExportedSymbolIndices,
-                                         android::ResStringPool* outStringPool,
-                                         std::string* outError) {
-    ResChunkPullParser parser(data, len);
-    if (!ResChunkPullParser::isGoodEvent(parser.next())) {
-        if (outError) *outError = parser.getLastError();
-        return -1;
-    }
-
-    if (util::deviceToHost16(parser.getChunk()->type) != RES_FILE_EXPORT_TYPE) {
-        if (outError) *outError = "no FileExport_header found";
-        return -1;
-    }
-
-    const FileExport_header* fileExport = convertTo<FileExport_header>(parser.getChunk());
-    if (!fileExport) {
-        if (outError) *outError = "corrupt FileExport_header";
-        return -1;
-    }
-
-    if (memcmp(fileExport->magic, "AAPT", sizeof(fileExport->magic)) != 0) {
-        if (outError) *outError = "invalid magic value";
-        return -1;
-    }
-
-    const size_t exportedSymbolCount = util::deviceToHost32(fileExport->exportedSymbolCount);
-
-    // Verify that we have enough space for all those symbols.
-    size_t dataLen = getChunkDataLen(&fileExport->header);
-    if (exportedSymbolCount > dataLen / sizeof(ExportedSymbol)) {
-        if (outError) *outError = "too many symbols";
-        return -1;
-    }
-
-    const size_t symbolIndicesSize = exportedSymbolCount * sizeof(ExportedSymbol);
-
-    const void* strPoolData = getChunkData(&fileExport->header) + symbolIndicesSize;
-    const size_t strPoolDataLen = dataLen - symbolIndicesSize;
-    if (outStringPool->setTo(strPoolData, strPoolDataLen, false) != android::NO_ERROR) {
-        if (outError) *outError = "corrupt string pool";
-        return -1;
-    }
-
-    *outFileExport = fileExport;
-    *outExportedSymbolIndices = (const ExportedSymbol*) getChunkData(
-            &fileExport->header);
-    return util::deviceToHost16(fileExport->header.headerSize) + symbolIndicesSize +
-            outStringPool->bytes();
-}
-
-static ssize_t getWrappedDataOffset(const void* data, size_t len, std::string* outError) {
-    const FileExport_header* header = nullptr;
-    const ExportedSymbol* entries = nullptr;
-    android::ResStringPool pool;
-    return parseFileExportHeaderImpl(data, len, &header, &entries, &pool, outError);
-}
-
-/**
- * Reads the FileExport_header and populates outRes with the values in that header.
- */
-static ssize_t unwrapFileExportHeader(const void* data, size_t len, ResourceFile* outRes,
-                                      std::string* outError) {
-
-    const FileExport_header* fileExport = nullptr;
-    const ExportedSymbol* entries = nullptr;
-    android::ResStringPool symbolPool;
-    const ssize_t offset = parseFileExportHeaderImpl(data, len, &fileExport, &entries, &symbolPool,
-                                                     outError);
-    if (offset < 0) {
-        return offset;
-    }
-
-    const size_t exportedSymbolCount = util::deviceToHost32(fileExport->exportedSymbolCount);
-    outRes->exportedSymbols.clear();
-    outRes->exportedSymbols.reserve(exportedSymbolCount);
-
-    for (size_t i = 0; i < exportedSymbolCount; i++) {
-        const StringPiece16 str = util::getString(symbolPool,
-                                                  util::deviceToHost32(entries[i].name.index));
-        StringPiece16 packageStr, typeStr, entryStr;
-        ResourceUtils::extractResourceName(str, &packageStr, &typeStr, &entryStr);
-        const ResourceType* resType = parseResourceType(typeStr);
-        if (!resType || entryStr.empty()) {
-            if (outError) {
-                std::stringstream errorStr;
-                errorStr << "invalid exported symbol at index="
-                         << util::deviceToHost32(entries[i].name.index)
-                         << " (" << str << ")";
-                *outError = errorStr.str();
-            }
-            return -1;
-        }
-
-        outRes->exportedSymbols.push_back(SourcedResourceName{
-                ResourceName{ packageStr.toString(), *resType, entryStr.toString() },
-                util::deviceToHost32(entries[i].line) });
-    }
-
-    const StringPiece16 str = util::getString(symbolPool,
-                                              util::deviceToHost32(fileExport->name.index));
-    StringPiece16 packageStr, typeStr, entryStr;
-    ResourceUtils::extractResourceName(str, &packageStr, &typeStr, &entryStr);
-    const ResourceType* resType = parseResourceType(typeStr);
-    if (!resType || entryStr.empty()) {
-        if (outError) {
-            std::stringstream errorStr;
-            errorStr << "invalid resource name at index="
-                     << util::deviceToHost32(fileExport->name.index)
-                     << " (" << str << ")";
-            *outError = errorStr.str();
-        }
-        return -1;
-    }
-
-    outRes->name = ResourceName{ packageStr.toString(), *resType, entryStr.toString() };
-    outRes->source.path = util::utf16ToUtf8(
-            util::getString(symbolPool, util::deviceToHost32(fileExport->source.index)));
-    outRes->config.copyFromDtoH(fileExport->config);
-    return offset;
-}
-
-} // namespace aapt
-
-#endif /* AAPT_UNFLATTEN_FILEEXPORTHEADERREADER_H */
diff --git a/tools/aapt2/unflatten/FileExportHeaderReader_test.cpp b/tools/aapt2/unflatten/FileExportHeaderReader_test.cpp
deleted file mode 100644
index a76c83b..0000000
--- a/tools/aapt2/unflatten/FileExportHeaderReader_test.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2015 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 "Resource.h"
-
-#include "flatten/FileExportWriter.h"
-#include "unflatten/FileExportHeaderReader.h"
-#include "util/BigBuffer.h"
-#include "util/Util.h"
-
-#include "test/Common.h"
-
-#include <gtest/gtest.h>
-
-namespace aapt {
-
-TEST(FileExportHeaderReaderTest, ReadHeaderWithNoSymbolExports) {
-    ResourceFile resFile = {
-            test::parseNameOrDie(u"@android:layout/main.xml"),
-            test::parseConfigOrDie("sw600dp-v4"),
-            Source{ "res/layout/main.xml" },
-    };
-
-    BigBuffer buffer(1024);
-    ChunkWriter writer = wrapBufferWithFileExportHeader(&buffer, &resFile);
-    *writer.getBuffer()->nextBlock<uint32_t>() = 42u;
-    writer.finish();
-
-    std::unique_ptr<uint8_t[]> data = util::copy(buffer);
-
-    ResourceFile actualResFile;
-
-    ssize_t offset = unwrapFileExportHeader(data.get(), buffer.size(), &actualResFile, nullptr);
-    ASSERT_GT(offset, 0);
-
-    EXPECT_EQ(offset, getWrappedDataOffset(data.get(), buffer.size(), nullptr));
-
-    EXPECT_EQ(actualResFile.config, test::parseConfigOrDie("sw600dp-v4"));
-    EXPECT_EQ(actualResFile.name, test::parseNameOrDie(u"@android:layout/main.xml"));
-    EXPECT_EQ(actualResFile.source.path, "res/layout/main.xml");
-
-    EXPECT_EQ(*(uint32_t*)(data.get() + offset), 42u);
-}
-
-} // namespace aapt