AAPT2

First checking of AAPT2. The individual phases of AAPT2 work, but there
are some missing pieces.

For early testing we are missing:
- Need to properly mark file references and include them in package
- Need to package into zip

Final AAPT for apps we are missing:
- Need to crush PNGs
- Need to parse 9-patches
- Need to validate all of AndroidManifest.xml
- Need to write align method to align resource tables for splits.

Final AAPT for apps + system we are missing:
- Need to handle overlays
- Need to store comments for R file
- Need to handle --shared-lib (dynamic references too).

New AAPT features coming:
- Need to import compiled libraries
    - Name mangling
    - R file generation for library code

Change-Id: I95f8a63581b81a1f424ae6fb2c373c883b72c18d
diff --git a/tools/aapt2/TableFlattener.cpp b/tools/aapt2/TableFlattener.cpp
new file mode 100644
index 0000000..c306185
--- /dev/null
+++ b/tools/aapt2/TableFlattener.cpp
@@ -0,0 +1,511 @@
+/*
+ * 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 "BigBuffer.h"
+#include "ConfigDescription.h"
+#include "Logger.h"
+#include "ResourceTable.h"
+#include "ResourceTypeExtensions.h"
+#include "ResourceValues.h"
+#include "StringPool.h"
+#include "TableFlattener.h"
+#include "Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <sstream>
+
+namespace aapt {
+
+struct FlatEntry {
+    const ResourceEntry& entry;
+    const Value& value;
+    uint32_t entryKey;
+    uint32_t sourcePathKey;
+    uint32_t sourceLine;
+};
+
+/**
+ * Visitor that knows how to encode Map values.
+ */
+class MapFlattener : public ConstValueVisitor {
+public:
+    MapFlattener(BigBuffer* out, const FlatEntry& flatEntry,
+                 std::vector<std::pair<ResourceNameRef, uint32_t>>& symbols) :
+            mOut(out), mSymbols(symbols) {
+        mMap = mOut->nextBlock<android::ResTable_map_entry>();
+        mMap->key.index = flatEntry.entryKey;
+        mMap->flags = android::ResTable_entry::FLAG_COMPLEX;
+        if (flatEntry.entry.publicStatus.isPublic) {
+            mMap->flags |= android::ResTable_entry::FLAG_PUBLIC;
+        }
+        if (flatEntry.value.isWeak()) {
+            mMap->flags |= android::ResTable_entry::FLAG_WEAK;
+        }
+
+        ResTable_entry_source* sourceBlock = mOut->nextBlock<ResTable_entry_source>();
+        sourceBlock->pathIndex = flatEntry.sourcePathKey;
+        sourceBlock->line = flatEntry.sourceLine;
+
+        mMap->size = sizeof(*mMap) + sizeof(*sourceBlock);
+    }
+
+    void flattenParent(const Reference& ref) {
+        if (!ref.id.isValid()) {
+            mSymbols.push_back({
+                    ResourceNameRef(ref.name),
+                    (mOut->size() - mMap->size) + sizeof(*mMap) - sizeof(android::ResTable_entry)
+            });
+        }
+        mMap->parent.ident = ref.id.id;
+    }
+
+    void flattenEntry(const Reference& key, const Item& value) {
+        mMap->count++;
+
+        android::ResTable_map* outMapEntry = mOut->nextBlock<android::ResTable_map>();
+
+        // Write the key.
+        if (!Res_INTERNALID(key.id.id) && !key.id.isValid()) {
+            mSymbols.push_back(std::make_pair(ResourceNameRef(key.name),
+                    mOut->size() - sizeof(*outMapEntry)));
+        }
+        outMapEntry->name.ident = key.id.id;
+
+        // Write the value.
+        value.flatten(outMapEntry->value);
+
+        if (outMapEntry->value.data == 0x0) {
+            visitFunc<Reference>(value, [&](const Reference& reference) {
+                mSymbols.push_back(std::make_pair(ResourceNameRef(reference.name),
+                        mOut->size() - sizeof(outMapEntry->value.data)));
+            });
+        }
+        outMapEntry->value.size = sizeof(outMapEntry->value);
+    }
+
+    static bool compareStyleEntries(const Style::Entry* lhs, const Style::Entry* rhs) {
+        return lhs->key.id < rhs->key.id;
+    }
+
+    void visit(const Style& style, ValueVisitorArgs&) override {
+        if (style.parent.name.isValid()) {
+            flattenParent(style.parent);
+        }
+
+        // First sort the entries by ID.
+        std::vector<const Style::Entry*> sortedEntries;
+        for (const auto& styleEntry : style.entries) {
+            auto iter = std::lower_bound(sortedEntries.begin(), sortedEntries.end(),
+                    &styleEntry, compareStyleEntries);
+            sortedEntries.insert(iter, &styleEntry);
+        }
+
+        for (const Style::Entry* styleEntry : sortedEntries) {
+            flattenEntry(styleEntry->key, *styleEntry->value);
+        }
+    }
+
+    void visit(const Attribute& attr, ValueVisitorArgs&) override {
+        android::Res_value tempVal;
+        tempVal.dataType = android::Res_value::TYPE_INT_DEC;
+        tempVal.data = attr.typeMask;
+        flattenEntry(Reference(ResourceId{android::ResTable_map::ATTR_TYPE}),
+                BinaryPrimitive(tempVal));
+
+        for (const auto& symbol : attr.symbols) {
+            tempVal.data = symbol.value;
+            flattenEntry(symbol.symbol, BinaryPrimitive(tempVal));
+        }
+    }
+
+    void visit(const Styleable& styleable, ValueVisitorArgs&) override {
+        for (const auto& attr : styleable.entries) {
+            flattenEntry(attr, BinaryPrimitive(android::Res_value{}));
+        }
+    }
+
+    void visit(const Array& array, ValueVisitorArgs&) override {
+        for (const auto& item : array.items) {
+            flattenEntry({}, *item);
+        }
+    }
+
+    void visit(const Plural& plural, ValueVisitorArgs&) override {
+        const size_t count = plural.values.size();
+        for (size_t i = 0; i < count; i++) {
+            if (!plural.values[i]) {
+                continue;
+            }
+
+            ResourceId q;
+            switch (i) {
+                case Plural::Zero:
+                    q.id = android::ResTable_map::ATTR_ZERO;
+                    break;
+
+                case Plural::One:
+                    q.id = android::ResTable_map::ATTR_ONE;
+                    break;
+
+                case Plural::Two:
+                    q.id = android::ResTable_map::ATTR_TWO;
+                    break;
+
+                case Plural::Few:
+                    q.id = android::ResTable_map::ATTR_FEW;
+                    break;
+
+                case Plural::Many:
+                    q.id = android::ResTable_map::ATTR_MANY;
+                    break;
+
+                case Plural::Other:
+                    q.id = android::ResTable_map::ATTR_OTHER;
+                    break;
+
+                default:
+                    assert(false);
+                    break;
+            }
+
+            flattenEntry(Reference(q), *plural.values[i]);
+        }
+    }
+
+private:
+    BigBuffer* mOut;
+    std::vector<std::pair<ResourceNameRef, uint32_t>>& mSymbols;
+    android::ResTable_map_entry* mMap;
+};
+
+TableFlattener::TableFlattener(Options options)
+: mOptions(options) {
+}
+
+bool TableFlattener::flattenValue(BigBuffer* out, const FlatEntry& flatEntry,
+        std::vector<std::pair<ResourceNameRef, uint32_t>>& symbolEntries) {
+    if (flatEntry.value.isItem()) {
+        android::ResTable_entry* entry = out->nextBlock<android::ResTable_entry>();
+
+        if (flatEntry.entry.publicStatus.isPublic) {
+            entry->flags |= android::ResTable_entry::FLAG_PUBLIC;
+        }
+
+        if (flatEntry.value.isWeak()) {
+            entry->flags |= android::ResTable_entry::FLAG_WEAK;
+        }
+
+        entry->key.index = flatEntry.entryKey;
+        entry->size = sizeof(*entry);
+
+        if (mOptions.useExtendedChunks) {
+            // Write the extra source block. This will be ignored by
+            // the Android runtime.
+            ResTable_entry_source* sourceBlock = out->nextBlock<ResTable_entry_source>();
+            sourceBlock->pathIndex = flatEntry.sourcePathKey;
+            sourceBlock->line = flatEntry.sourceLine;
+
+            entry->size += sizeof(*sourceBlock);
+        }
+
+        android::Res_value* outValue = out->nextBlock<android::Res_value>();
+
+        const Item& item = static_cast<const Item&>(flatEntry.value);
+        if (!item.flatten(*outValue)) {
+            return false;
+        }
+
+        if (outValue->data == 0x0) {
+            visitFunc<Reference>(item, [&](const Reference& reference) {
+                symbolEntries.push_back({
+                        ResourceNameRef(reference.name),
+                        out->size() - sizeof(outValue->data)
+                });
+            });
+        }
+        outValue->size = sizeof(*outValue);
+        return true;
+    }
+
+    MapFlattener flattener(out, flatEntry, symbolEntries);
+    flatEntry.value.accept(flattener, {});
+    return true;
+}
+
+bool TableFlattener::flatten(BigBuffer* out, const ResourceTable& table) {
+    const size_t beginning = out->size();
+
+    if (table.getPackage().size() == 0) {
+        Logger::error()
+                << "ResourceTable has no package name."
+                << std::endl;
+        return false;
+    }
+
+    if (table.getPackageId() == ResourceTable::kUnsetPackageId) {
+        Logger::error()
+                << "ResourceTable has no package ID set."
+                << std::endl;
+        return false;
+    }
+
+    std::vector<std::pair<ResourceNameRef, uint32_t>> symbolEntries;
+
+    StringPool typePool;
+    StringPool keyPool;
+    StringPool sourcePool;
+
+    // Sort the types by their IDs. They will be inserted into the StringPool
+    // in this order.
+    std::vector<ResourceTableType*> sortedTypes;
+    for (const auto& type : table) {
+        if (type->type == ResourceType::kStyleable && !mOptions.useExtendedChunks) {
+            continue;
+        }
+
+        auto iter = std::lower_bound(std::begin(sortedTypes), std::end(sortedTypes), type.get(),
+                [](const ResourceTableType* lhs, const ResourceTableType* rhs) -> bool {
+                    return lhs->typeId < rhs->typeId;
+                });
+        sortedTypes.insert(iter, type.get());
+    }
+
+    BigBuffer typeBlock(1024);
+    size_t expectedTypeId = 1;
+    for (const ResourceTableType* type : sortedTypes) {
+        if (type->typeId == ResourceTableType::kUnsetTypeId
+                || type->typeId == 0) {
+            Logger::error()
+                    << "resource type '"
+                    << type->type
+                    << "' from package '"
+                    << table.getPackage()
+                    << "' has no ID."
+                    << std::endl;
+            return false;
+        }
+
+        // If there is a gap in the type IDs, fill in the StringPool
+        // with empty values until we reach the ID we expect.
+        while (type->typeId > expectedTypeId) {
+            std::u16string typeName(u"?");
+            typeName += expectedTypeId;
+            typePool.makeRef(typeName);
+            expectedTypeId++;
+        }
+        expectedTypeId++;
+        typePool.makeRef(toString(type->type));
+
+        android::ResTable_typeSpec* spec = typeBlock.nextBlock<android::ResTable_typeSpec>();
+        spec->header.type = android::RES_TABLE_TYPE_SPEC_TYPE;
+        spec->header.headerSize = sizeof(*spec);
+        spec->header.size = spec->header.headerSize + (type->entries.size() * sizeof(uint32_t));
+        spec->id = type->typeId;
+        spec->entryCount = type->entries.size();
+
+        // Reserve space for the masks of each resource in this type. These
+        // show for which configuration axis the resource changes.
+        uint32_t* configMasks = typeBlock.nextBlock<uint32_t>(type->entries.size());
+
+        // Sort the entries by entry ID and write their configuration masks.
+        std::vector<ResourceEntry*> entries;
+        const size_t entryCount = type->entries.size();
+        for (size_t entryIndex = 0; entryIndex < entryCount; entryIndex++) {
+            const auto& entry = type->entries[entryIndex];
+
+            if (entry->entryId == ResourceEntry::kUnsetEntryId) {
+                Logger::error()
+                        << "resource '"
+                        << ResourceName{ table.getPackage(), type->type, entry->name }
+                        << "' has no ID."
+                        << std::endl;
+                return false;
+            }
+
+            auto iter = std::lower_bound(std::begin(entries), std::end(entries), entry.get(),
+                    [](const ResourceEntry* lhs, const ResourceEntry* rhs) -> bool {
+                        return lhs->entryId < rhs->entryId;
+                    });
+            entries.insert(iter, entry.get());
+
+            // Populate the config masks for this entry.
+            if (entry->publicStatus.isPublic) {
+                configMasks[entry->entryId] |= android::ResTable_typeSpec::SPEC_PUBLIC;
+            }
+
+            const size_t configCount = entry->values.size();
+            for (size_t i = 0; i < configCount; i++) {
+                const ConfigDescription& config = entry->values[i].config;
+                for (size_t j = i + 1; j < configCount; j++) {
+                    configMasks[entry->entryId] |= config.diff(entry->values[j].config);
+                }
+            }
+        }
+
+        // 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.
+        std::map<ConfigDescription, std::vector<FlatEntry>> data;
+        for (const ResourceEntry* entry : entries) {
+            size_t keyIndex = keyPool.makeRef(entry->name).getIndex();
+
+            if (keyIndex > std::numeric_limits<uint32_t>::max()) {
+                Logger::error()
+                        << "resource key string pool exceeded max size."
+                        << std::endl;
+                return false;
+            }
+
+            for (const auto& configValue : entry->values) {
+                data[configValue.config].push_back(FlatEntry{
+                        *entry,
+                        *configValue.value,
+                        static_cast<uint32_t>(keyIndex),
+                        static_cast<uint32_t>(sourcePool.makeRef(util::utf8ToUtf16(
+                                        configValue.source.path)).getIndex()),
+                        static_cast<uint32_t>(configValue.source.line)
+                });
+            }
+        }
+
+        // Begin flattening a configuration for the current type.
+        for (const auto& entry : data) {
+            const size_t typeHeaderStart = typeBlock.size();
+            android::ResTable_type* typeHeader = typeBlock.nextBlock<android::ResTable_type>();
+            typeHeader->header.type = android::RES_TABLE_TYPE_TYPE;
+            typeHeader->header.headerSize = sizeof(*typeHeader);
+            typeHeader->id = type->typeId;
+            typeHeader->entryCount = type->entries.size();
+            typeHeader->entriesStart = typeHeader->header.headerSize
+                    + (sizeof(uint32_t) * type->entries.size());
+            typeHeader->config = entry.first;
+
+            uint32_t* indices = typeBlock.nextBlock<uint32_t>(type->entries.size());
+            memset(indices, 0xff, type->entries.size() * sizeof(uint32_t));
+
+            const size_t entryStart = typeBlock.size();
+            for (const FlatEntry& flatEntry : entry.second) {
+                assert(flatEntry.entry.entryId < type->entries.size());
+                indices[flatEntry.entry.entryId] = typeBlock.size() - entryStart;
+                if (!flattenValue(&typeBlock, flatEntry, symbolEntries)) {
+                    Logger::error()
+                            << "failed to flatten resource '"
+                            << ResourceNameRef {
+                                    table.getPackage(), type->type, flatEntry.entry.name }
+                            << "' for configuration '"
+                            << entry.first
+                            << "'."
+                            << std::endl;
+                    return false;
+                }
+            }
+
+            typeBlock.align4();
+            typeHeader->header.size = typeBlock.size() - typeHeaderStart;
+        }
+    }
+
+    const size_t beforeTable = out->size();
+    android::ResTable_header* header = out->nextBlock<android::ResTable_header>();
+    header->header.type = android::RES_TABLE_TYPE;
+    header->header.headerSize = sizeof(*header);
+    header->packageCount = 1;
+
+    SymbolTable_entry* symbolEntryData = nullptr;
+    if (!symbolEntries.empty() && mOptions.useExtendedChunks) {
+        const size_t beforeSymbolTable = out->size();
+        StringPool symbolPool;
+        SymbolTable_header* symbolHeader = out->nextBlock<SymbolTable_header>();
+        symbolHeader->header.type = RES_TABLE_SYMBOL_TABLE_TYPE;
+        symbolHeader->header.headerSize = sizeof(*symbolHeader);
+        symbolHeader->count = symbolEntries.size();
+
+        symbolEntryData = out->nextBlock<SymbolTable_entry>(symbolHeader->count);
+
+        size_t i = 0;
+        for (const auto& entry : symbolEntries) {
+            symbolEntryData[i].offset = entry.second;
+            StringPool::Ref ref = symbolPool.makeRef(
+                    entry.first.package.toString() + u":" +
+                    toString(entry.first.type).toString() + u"/" +
+                    entry.first.entry.toString());
+            symbolEntryData[i].stringIndex = ref.getIndex();
+            i++;
+        }
+
+        StringPool::flattenUtf8(out, symbolPool);
+        out->align4();
+        symbolHeader->header.size = out->size() - beforeSymbolTable;
+    }
+
+    if (sourcePool.size() > 0 && mOptions.useExtendedChunks) {
+        const size_t beforeSourcePool = out->size();
+        android::ResChunk_header* sourceHeader = out->nextBlock<android::ResChunk_header>();
+        sourceHeader->type = RES_TABLE_SOURCE_POOL_TYPE;
+        sourceHeader->headerSize = sizeof(*sourceHeader);
+        StringPool::flattenUtf8(out, sourcePool);
+        out->align4();
+        sourceHeader->size = out->size() - beforeSourcePool;
+    }
+
+    StringPool::flattenUtf8(out, table.getValueStringPool());
+
+    const size_t beforePackageIndex = out->size();
+    android::ResTable_package* package = out->nextBlock<android::ResTable_package>();
+    package->header.type = android::RES_TABLE_PACKAGE_TYPE;
+    package->header.headerSize = sizeof(*package);
+
+    if (table.getPackageId() > std::numeric_limits<uint8_t>::max()) {
+        Logger::error()
+                << "package ID 0x'"
+                << std::hex << table.getPackageId() << std::dec
+                << "' is invalid."
+                << std::endl;
+        return false;
+    }
+    package->id = table.getPackageId();
+
+    if (table.getPackage().size() >= sizeof(package->name) / sizeof(package->name[0])) {
+        Logger::error()
+                << "package name '"
+                << table.getPackage()
+                << "' is too long."
+                << std::endl;
+        return false;
+    }
+    memcpy(package->name, reinterpret_cast<const uint16_t*>(table.getPackage().data()),
+            table.getPackage().length() * sizeof(char16_t));
+    package->name[table.getPackage().length()] = 0;
+
+    package->typeStrings = package->header.headerSize;
+    StringPool::flattenUtf8(out, typePool);
+    package->keyStrings = out->size() - beforePackageIndex;
+    StringPool::flattenUtf8(out, keyPool);
+
+    if (symbolEntryData != nullptr) {
+        for (size_t i = 0; i < symbolEntries.size(); i++) {
+            symbolEntryData[i].offset += out->size() - beginning;
+        }
+    }
+
+    out->appendBuffer(std::move(typeBlock));
+
+    package->header.size = out->size() - beforePackageIndex;
+    header->header.size = out->size() - beforeTable;
+    return true;
+}
+
+} // namespace aapt