Properly mangle file names

Change-Id: I49c0f82e8c06f056198eb64b8369d83403b74321
diff --git a/tools/aapt2/BinaryResourceParser.cpp b/tools/aapt2/BinaryResourceParser.cpp
index 71016c1..326a2ac 100644
--- a/tools/aapt2/BinaryResourceParser.cpp
+++ b/tools/aapt2/BinaryResourceParser.cpp
@@ -603,6 +603,13 @@
                     mTable->getValueStringPool().makeRef(
                             styleStr, StringPool::Context{1, config}));
         } else {
+            if (name.type != ResourceType::kString &&
+                    util::stringStartsWith<char16_t>(str, u"res/")) {
+                // This must be a FileReference.
+                return util::make_unique<FileReference>(mTable->getValueStringPool().makeRef(
+                            str, StringPool::Context{ 0, config }));
+            }
+
             // There are no styles associated with this string, so treat it as
             // a simple string.
             return util::make_unique<String>(
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 03b9ba4..be806c9 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -25,6 +25,7 @@
 #include "Linker.h"
 #include "ManifestParser.h"
 #include "ManifestValidator.h"
+#include "NameMangler.h"
 #include "Png.h"
 #include "ResourceParser.h"
 #include "ResourceTable.h"
@@ -266,22 +267,50 @@
 
 struct LinkItem {
     Source source;
-    std::string apkPath;
+    ResourceName name;
+    ConfigDescription config;
+    std::string originalPath;
+    ZipFile* apk;
 };
 
-std::string buildFileReference(const CompileItem& item) {
-    std::stringstream path;
-    path << "res/" << item.name.type;
-    if (item.config != ConfigDescription{}) {
-        path << "-" << item.config;
+template <typename TChar>
+static BasicStringPiece<TChar> getExtension(const BasicStringPiece<TChar>& str) {
+    auto iter = std::find(str.begin(), str.end(), static_cast<TChar>('.'));
+    if (iter == str.end()) {
+        return BasicStringPiece<TChar>();
     }
-    path << "/" << util::utf16ToUtf8(item.name.entry) + "." + item.extension;
+    size_t offset = (iter - str.begin()) + 1;
+    return str.substr(offset, str.size() - offset);
+}
+
+
+
+std::string buildFileReference(const ResourceNameRef& name, const ConfigDescription& config,
+                               const StringPiece& extension) {
+    std::stringstream path;
+    path << "res/" << name.type;
+    if (config != ConfigDescription{}) {
+        path << "-" << config;
+    }
+    path << "/" << util::utf16ToUtf8(name.entry);
+    if (!extension.empty()) {
+        path << "." << extension;
+    }
     return path.str();
 }
 
+std::string buildFileReference(const CompileItem& item) {
+    return buildFileReference(item.name, item.config, item.extension);
+}
+
+std::string buildFileReference(const LinkItem& item) {
+    return buildFileReference(item.name, item.config, getExtension<char>(item.originalPath));
+}
+
 bool addFileReference(const std::shared_ptr<ResourceTable>& table, const CompileItem& item) {
     StringPool& pool = table->getValueStringPool();
-    StringPool::Ref ref = pool.makeRef(util::utf8ToUtf16(buildFileReference(item)));
+    StringPool::Ref ref = pool.makeRef(util::utf8ToUtf16(buildFileReference(item)),
+                                       StringPool::Context{ 0, item.config });
     return table->addResource(item.name, item.config, item.source.line(0),
                               util::make_unique<FileReference>(ref));
 }
@@ -418,8 +447,8 @@
         return false;
     }
 
-    if (outApk->add(outBuffer, item.apkPath.data(), ZipEntry::kCompressDeflated, nullptr) !=
-            android::NO_ERROR) {
+    if (outApk->add(outBuffer, buildFileReference(item).data(), ZipEntry::kCompressDeflated,
+                nullptr) != android::NO_ERROR) {
         Logger::error(options.output) << "failed to write linked file '" << item.source
                                       << "' to apk." << std::endl;
         return false;
@@ -502,109 +531,6 @@
     return true;
 }
 
-bool loadAppInfo(const Source& source, AppInfo* outInfo) {
-    std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary);
-    if (!ifs) {
-        Logger::error(source) << strerror(errno) << std::endl;
-        return false;
-    }
-
-    ManifestParser parser;
-    std::shared_ptr<XmlPullParser> pullParser = std::make_shared<SourceXmlPullParser>(ifs);
-    return parser.parse(source, pullParser, outInfo);
-}
-
-static void printCommandsAndDie() {
-    std::cerr << "The following commands are supported:" << std::endl << std::endl;
-    std::cerr << "compile       compiles a subset of resources" << std::endl;
-    std::cerr << "link          links together compiled resources and libraries" << std::endl;
-    std::cerr << std::endl;
-    std::cerr << "run aapt2 with one of the commands and the -h flag for extra details."
-              << std::endl;
-    exit(1);
-}
-
-static AaptOptions prepareArgs(int argc, char** argv) {
-    if (argc < 2) {
-        std::cerr << "no command specified." << std::endl << std::endl;
-        printCommandsAndDie();
-    }
-
-    const StringPiece command(argv[1]);
-    argc -= 2;
-    argv += 2;
-
-    AaptOptions options;
-
-    if (command == "--version" || command == "version") {
-        std::cout << kAaptVersionStr << std::endl;
-        exit(0);
-    } else if (command == "link") {
-        options.phase = AaptOptions::Phase::Link;
-    } else if (command == "compile") {
-        options.phase = AaptOptions::Phase::Compile;
-    } else {
-        std::cerr << "invalid command '" << command << "'." << std::endl << std::endl;
-        printCommandsAndDie();
-    }
-
-    if (options.phase == AaptOptions::Phase::Compile) {
-        flag::requiredFlag("--package", "Android package name",
-                [&options](const StringPiece& arg) {
-                    options.appInfo.package = util::utf8ToUtf16(arg);
-                });
-        flag::optionalFlag("--binding", "Output directory for binding XML files",
-                [&options](const StringPiece& arg) {
-                    options.bindingOutput = Source{ arg.toString() };
-                });
-        flag::optionalSwitch("--no-version", "Disables automatic style and layout versioning",
-                             false, &options.versionStylesAndLayouts);
-
-    } else if (options.phase == AaptOptions::Phase::Link) {
-        flag::requiredFlag("--manifest", "AndroidManifest.xml of your app",
-                [&options](const StringPiece& arg) {
-                    options.manifest = Source{ arg.toString() };
-                });
-
-        flag::optionalFlag("-I", "add an Android APK to link against",
-                [&options](const StringPiece& arg) {
-                    options.libraries.push_back(Source{ arg.toString() });
-                });
-
-        flag::optionalFlag("--java", "directory in which to generate R.java",
-                [&options](const StringPiece& arg) {
-                    options.generateJavaClass = Source{ arg.toString() };
-                });
-    }
-
-    // Common flags for all steps.
-    flag::requiredFlag("-o", "Output path", [&options](const StringPiece& arg) {
-        options.output = Source{ arg.toString() };
-    });
-
-    bool help = false;
-    flag::optionalSwitch("-v", "enables verbose logging", true, &options.verbose);
-    flag::optionalSwitch("-h", "displays this help menu", true, &help);
-
-    // Build the command string for output (eg. "aapt2 compile").
-    std::string fullCommand = "aapt2";
-    fullCommand += " ";
-    fullCommand += command.toString();
-
-    // Actually read the command line flags.
-    flag::parse(argc, argv, fullCommand);
-
-    if (help) {
-        flag::usageAndDie(fullCommand);
-    }
-
-    // Copy all the remaining arguments.
-    for (const std::string& arg : flag::getArgs()) {
-        options.input.push_back(Source{ arg });
-    }
-    return options;
-}
-
 static bool compileValues(const std::shared_ptr<ResourceTable>& table, const Source& source,
                           const ConfigDescription& config) {
     std::ifstream in(source.path, std::ifstream::binary);
@@ -630,6 +556,7 @@
  * [--/res/]type[-config]/name
  */
 static Maybe<ResourcePathData> extractResourcePathData(const Source& source) {
+    // TODO(adamlesinski): Use Windows path separator on windows.
     std::vector<std::string> parts = util::splitAndLowercase(source.path, '/');
     if (parts.size() < 2) {
         Logger::error(source) << "bad resource path." << std::endl;
@@ -695,6 +622,38 @@
     return true;
 }
 
+/**
+ * For each FileReference in the table, adds a LinkItem to the link queue for processing.
+ */
+static void addApkFilesToLinkQueue(const std::u16string& package, const Source& source,
+                                   const std::shared_ptr<ResourceTable>& table,
+                                   const std::unique_ptr<ZipFile>& apk,
+                                   std::queue<LinkItem>* outLinkQueue) {
+    bool mangle = package != table->getPackage();
+    for (auto& type : *table) {
+        for (auto& entry : type->entries) {
+            ResourceName name = { package, type->type, entry->name };
+            if (mangle) {
+                NameMangler::mangle(table->getPackage(), &name.entry);
+            }
+
+            for (auto& value : entry->values) {
+                visitFunc<FileReference>(*value.value, [&](FileReference& ref) {
+                    std::string pathUtf8 = util::utf16ToUtf8(*ref.path);
+                    outLinkQueue->push(LinkItem{
+                            source, name, value.config, pathUtf8, apk.get() });
+                    // Now rewrite the file path.
+                    if (mangle) {
+                        ref.path = table->getValueStringPool().makeRef(util::utf8ToUtf16(
+                                    buildFileReference(name, value.config,
+                                                       getExtension<char>(pathUtf8))));
+                    }
+                });
+            }
+        }
+    }
+}
+
 static constexpr int kOpenFlags = ZipFile::kOpenCreate | ZipFile::kOpenTruncate |
         ZipFile::kOpenReadWrite;
 
@@ -740,9 +699,14 @@
         linkedPackages.insert(table->getPackage());
     }
 
+    std::queue<LinkItem> linkQueue;
     for (auto& p : apkFiles) {
         const std::shared_ptr<ResourceTable>& inTable = p.first;
 
+        // Collect all FileReferences and add them to the queue for processing.
+        addApkFilesToLinkQueue(options.appInfo.package, Source{}, inTable, p.second, &linkQueue);
+
+        // Merge the tables.
         if (!outTable->merge(std::move(*inTable))) {
             return false;
         }
@@ -779,39 +743,32 @@
         return false;
     }
 
-    for (auto& p : apkFiles) {
-        std::unique_ptr<ZipFile>& zipFile = p.second;
+    for (; !linkQueue.empty(); linkQueue.pop()) {
+        const LinkItem& item = linkQueue.front();
 
-        // TODO(adamlesinski): Get list of files to read when processing config filter.
+        ZipEntry* entry = item.apk->getEntryByName(item.originalPath.data());
+        if (!entry) {
+            Logger::error(item.source) << "failed to find '" << item.originalPath << "'."
+                                       << std::endl;
+            return false;
+        }
 
-        const int numEntries = zipFile->getNumEntries();
-        for (int i = 0; i < numEntries; i++) {
-            ZipEntry* entry = zipFile->getEntryByIndex(i);
-            assert(entry);
+        if (util::stringEndsWith<char>(item.originalPath, ".xml")) {
+            void* uncompressedData = item.apk->uncompress(entry);
+            assert(uncompressedData);
 
-            StringPiece filename = entry->getFileName();
-            if (!util::stringStartsWith<char>(filename, "res/")) {
-                continue;
+            if (!linkXml(options, resolver, item, uncompressedData, entry->getUncompressedLen(),
+                    &outApk)) {
+                Logger::error(options.output) << "failed to link '" << item.originalPath << "'."
+                                              << std::endl;
+                return false;
             }
-
-            if (util::stringEndsWith<char>(filename, ".xml")) {
-                void* uncompressedData = zipFile->uncompress(entry);
-                assert(uncompressedData);
-
-                LinkItem item = { Source{ filename.toString() }, filename.toString() };
-
-                if (!linkXml(options, resolver, item, uncompressedData,
-                            entry->getUncompressedLen(), &outApk)) {
-                    Logger::error(options.output) << "failed to link '" << filename << "'."
-                                                  << std::endl;
-                    return false;
-                }
-            } else {
-                if (outApk.add(zipFile.get(), entry, 0, nullptr) != android::NO_ERROR) {
-                    Logger::error(options.output) << "failed to copy '" << filename << "'."
-                                                  << std::endl;
-                    return false;
-                }
+        } else {
+            if (outApk.add(item.apk, entry, buildFileReference(item).data(), 0, nullptr) !=
+                    android::NO_ERROR) {
+                Logger::error(options.output) << "failed to copy '" << item.originalPath << "'."
+                                              << std::endl;
+                return false;
             }
         }
     }
@@ -957,6 +914,109 @@
     return true;
 }
 
+bool loadAppInfo(const Source& source, AppInfo* outInfo) {
+    std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary);
+    if (!ifs) {
+        Logger::error(source) << strerror(errno) << std::endl;
+        return false;
+    }
+
+    ManifestParser parser;
+    std::shared_ptr<XmlPullParser> pullParser = std::make_shared<SourceXmlPullParser>(ifs);
+    return parser.parse(source, pullParser, outInfo);
+}
+
+static void printCommandsAndDie() {
+    std::cerr << "The following commands are supported:" << std::endl << std::endl;
+    std::cerr << "compile       compiles a subset of resources" << std::endl;
+    std::cerr << "link          links together compiled resources and libraries" << std::endl;
+    std::cerr << std::endl;
+    std::cerr << "run aapt2 with one of the commands and the -h flag for extra details."
+              << std::endl;
+    exit(1);
+}
+
+static AaptOptions prepareArgs(int argc, char** argv) {
+    if (argc < 2) {
+        std::cerr << "no command specified." << std::endl << std::endl;
+        printCommandsAndDie();
+    }
+
+    const StringPiece command(argv[1]);
+    argc -= 2;
+    argv += 2;
+
+    AaptOptions options;
+
+    if (command == "--version" || command == "version") {
+        std::cout << kAaptVersionStr << std::endl;
+        exit(0);
+    } else if (command == "link") {
+        options.phase = AaptOptions::Phase::Link;
+    } else if (command == "compile") {
+        options.phase = AaptOptions::Phase::Compile;
+    } else {
+        std::cerr << "invalid command '" << command << "'." << std::endl << std::endl;
+        printCommandsAndDie();
+    }
+
+    if (options.phase == AaptOptions::Phase::Compile) {
+        flag::requiredFlag("--package", "Android package name",
+                [&options](const StringPiece& arg) {
+                    options.appInfo.package = util::utf8ToUtf16(arg);
+                });
+        flag::optionalFlag("--binding", "Output directory for binding XML files",
+                [&options](const StringPiece& arg) {
+                    options.bindingOutput = Source{ arg.toString() };
+                });
+        flag::optionalSwitch("--no-version", "Disables automatic style and layout versioning",
+                             false, &options.versionStylesAndLayouts);
+
+    } else if (options.phase == AaptOptions::Phase::Link) {
+        flag::requiredFlag("--manifest", "AndroidManifest.xml of your app",
+                [&options](const StringPiece& arg) {
+                    options.manifest = Source{ arg.toString() };
+                });
+
+        flag::optionalFlag("-I", "add an Android APK to link against",
+                [&options](const StringPiece& arg) {
+                    options.libraries.push_back(Source{ arg.toString() });
+                });
+
+        flag::optionalFlag("--java", "directory in which to generate R.java",
+                [&options](const StringPiece& arg) {
+                    options.generateJavaClass = Source{ arg.toString() };
+                });
+    }
+
+    // Common flags for all steps.
+    flag::requiredFlag("-o", "Output path", [&options](const StringPiece& arg) {
+        options.output = Source{ arg.toString() };
+    });
+
+    bool help = false;
+    flag::optionalSwitch("-v", "enables verbose logging", true, &options.verbose);
+    flag::optionalSwitch("-h", "displays this help menu", true, &help);
+
+    // Build the command string for output (eg. "aapt2 compile").
+    std::string fullCommand = "aapt2";
+    fullCommand += " ";
+    fullCommand += command.toString();
+
+    // Actually read the command line flags.
+    flag::parse(argc, argv, fullCommand);
+
+    if (help) {
+        flag::usageAndDie(fullCommand);
+    }
+
+    // Copy all the remaining arguments.
+    for (const std::string& arg : flag::getArgs()) {
+        options.input.push_back(Source{ arg });
+    }
+    return options;
+}
+
 int main(int argc, char** argv) {
     Logger::setLog(std::make_shared<Log>(std::cerr, std::cerr));
     AaptOptions options = prepareArgs(argc, argv);
diff --git a/tools/aapt2/TableFlattener.cpp b/tools/aapt2/TableFlattener.cpp
index 67c56e7..4aadadc 100644
--- a/tools/aapt2/TableFlattener.cpp
+++ b/tools/aapt2/TableFlattener.cpp
@@ -43,8 +43,7 @@
  */
 class MapFlattener : public ConstValueVisitor {
 public:
-    MapFlattener(BigBuffer* out, const FlatEntry& flatEntry,
-                 std::vector<std::pair<ResourceNameRef, uint32_t>>& symbols) :
+    MapFlattener(BigBuffer* out, const FlatEntry& flatEntry, SymbolEntryVector* symbols) :
             mOut(out), mSymbols(symbols) {
         mMap = mOut->nextBlock<android::ResTable_map_entry>();
         mMap->key.index = flatEntry.entryKey;
@@ -65,7 +64,7 @@
 
     void flattenParent(const Reference& ref) {
         if (!ref.id.isValid()) {
-            mSymbols.push_back({
+            mSymbols->push_back({
                     ResourceNameRef(ref.name),
                     (mOut->size() - mMap->size) + sizeof(*mMap) - sizeof(android::ResTable_entry)
             });
@@ -80,7 +79,7 @@
 
         // Write the key.
         if (!Res_INTERNALID(key.id.id) && !key.id.isValid()) {
-            mSymbols.push_back(std::make_pair(ResourceNameRef(key.name),
+            mSymbols->push_back(std::make_pair(ResourceNameRef(key.name),
                     mOut->size() - sizeof(*outMapEntry)));
         }
         outMapEntry->name.ident = key.id.id;
@@ -90,7 +89,7 @@
 
         if (outMapEntry->value.data == 0x0) {
             visitFunc<Reference>(value, [&](const Reference& reference) {
-                mSymbols.push_back(std::make_pair(ResourceNameRef(reference.name),
+                mSymbols->push_back(std::make_pair(ResourceNameRef(reference.name),
                         mOut->size() - sizeof(outMapEntry->value.data)));
             });
         }
@@ -188,16 +187,47 @@
 
 private:
     BigBuffer* mOut;
-    std::vector<std::pair<ResourceNameRef, uint32_t>>& mSymbols;
+    SymbolEntryVector* mSymbols;
     android::ResTable_map_entry* mMap;
 };
 
+/**
+ * Flattens a value, with special handling for References.
+ */
+struct ValueFlattener : ConstValueVisitor {
+    ValueFlattener(BigBuffer* out, SymbolEntryVector* symbols) :
+            result(false), mOut(out), mOutValue(nullptr), mSymbols(symbols) {
+        mOutValue = mOut->nextBlock<android::Res_value>();
+    }
+
+    virtual void visit(const Reference& ref, ValueVisitorArgs& a) override {
+        visitItem(ref, a);
+        if (mOutValue->data == 0x0) {
+            mSymbols->push_back({
+                    ResourceNameRef(ref.name),
+                    mOut->size() - sizeof(mOutValue->data)});
+        }
+    }
+
+    virtual void visitItem(const Item& item, ValueVisitorArgs&) override {
+        result = item.flatten(*mOutValue);
+        mOutValue->size = sizeof(*mOutValue);
+    }
+
+    bool result;
+
+private:
+    BigBuffer* mOut;
+    android::Res_value* mOutValue;
+    SymbolEntryVector* mSymbols;
+};
+
 TableFlattener::TableFlattener(Options options)
 : mOptions(options) {
 }
 
 bool TableFlattener::flattenValue(BigBuffer* out, const FlatEntry& flatEntry,
-        std::vector<std::pair<ResourceNameRef, uint32_t>>& symbolEntries) {
+                                  SymbolEntryVector* symbols) {
     if (flatEntry.value.isItem()) {
         android::ResTable_entry* entry = out->nextBlock<android::ResTable_entry>();
 
@@ -218,30 +248,16 @@
             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;
+        const Item* item = static_cast<const Item*>(&flatEntry.value);
+        ValueFlattener flattener(out, symbols);
+        item->accept(flattener, {});
+        return flattener.result;
     }
 
-    MapFlattener flattener(out, flatEntry, symbolEntries);
+    MapFlattener flattener(out, flatEntry, symbols);
     flatEntry.value.accept(flattener, {});
     return true;
 }
@@ -263,7 +279,7 @@
         return false;
     }
 
-    std::vector<std::pair<ResourceNameRef, uint32_t>> symbolEntries;
+    SymbolEntryVector symbolEntries;
 
     StringPool typePool;
     StringPool keyPool;
@@ -401,7 +417,7 @@
             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)) {
+                if (!flattenValue(&typeBlock, flatEntry, &symbolEntries)) {
                     Logger::error()
                             << "failed to flatten resource '"
                             << ResourceNameRef {
diff --git a/tools/aapt2/TableFlattener.h b/tools/aapt2/TableFlattener.h
index 0ae798c..ccbb737 100644
--- a/tools/aapt2/TableFlattener.h
+++ b/tools/aapt2/TableFlattener.h
@@ -22,6 +22,8 @@
 
 namespace aapt {
 
+using SymbolEntryVector = std::vector<std::pair<ResourceNameRef, uint32_t>>;
+
 struct FlatEntry;
 
 /**
@@ -49,8 +51,7 @@
     bool flatten(BigBuffer* out, const ResourceTable& table);
 
 private:
-    bool flattenValue(BigBuffer* out, const FlatEntry& flatEntry,
-                      std::vector<std::pair<ResourceNameRef, uint32_t>>& symbolEntries);
+    bool flattenValue(BigBuffer* out, const FlatEntry& flatEntry, SymbolEntryVector* symbols);
 
     Options mOptions;
 };
diff --git a/tools/aapt2/ZipEntry.cpp b/tools/aapt2/ZipEntry.cpp
index ad5d84a..891b4e1 100644
--- a/tools/aapt2/ZipEntry.cpp
+++ b/tools/aapt2/ZipEntry.cpp
@@ -144,9 +144,15 @@
  * Initializes the CDE and the LFH.
  */
 status_t ZipEntry::initFromExternal(const ZipFile* /* pZipFile */,
-    const ZipEntry* pEntry)
+    const ZipEntry* pEntry, const char* storageName)
 {
     mCDE = pEntry->mCDE;
+    if (storageName && *storageName != 0) {
+        mCDE.mFileNameLength = strlen(storageName);
+        mCDE.mFileName = new unsigned char[mCDE.mFileNameLength + 1];
+        strcpy((char*) mCDE.mFileName, storageName);
+    }
+
     // Check whether we got all the memory needed.
     if ((mCDE.mFileNameLength > 0 && mCDE.mFileName == NULL) ||
             (mCDE.mFileCommentLength > 0 && mCDE.mFileComment == NULL) ||
diff --git a/tools/aapt2/ZipEntry.h b/tools/aapt2/ZipEntry.h
index d048a3e..2745a43 100644
--- a/tools/aapt2/ZipEntry.h
+++ b/tools/aapt2/ZipEntry.h
@@ -171,9 +171,10 @@
 
     /*
      * Initialize the structure with the contents of a ZipEntry from
-     * another file.
+     * another file. If fileName is non-NULL, override the name with fileName.
      */
-    status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry);
+    status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry,
+                              const char* fileName);
 
     /*
      * Add some pad bytes to the LFH.  We do this by adding or resizing
diff --git a/tools/aapt2/ZipFile.cpp b/tools/aapt2/ZipFile.cpp
index 41e59cf..268c15e 100644
--- a/tools/aapt2/ZipFile.cpp
+++ b/tools/aapt2/ZipFile.cpp
@@ -546,7 +546,7 @@
  * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
  */
 status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
-    int padding, ZipEntry** ppEntry)
+                      const char* storageName, int padding, ZipEntry** ppEntry)
 {
     ZipEntry* pEntry = NULL;
     status_t result;
@@ -570,9 +570,10 @@
         goto bail;
     }
 
-    result = pEntry->initFromExternal(pSourceZip, pSourceEntry);
-    if (result != NO_ERROR)
+    result = pEntry->initFromExternal(pSourceZip, pSourceEntry, storageName);
+    if (result != NO_ERROR) {
         goto bail;
+    }
     if (padding != 0) {
         result = pEntry->addPadding(padding);
         if (result != NO_ERROR)
diff --git a/tools/aapt2/ZipFile.h b/tools/aapt2/ZipFile.h
index 9cbd1fa..9de92dd 100644
--- a/tools/aapt2/ZipFile.h
+++ b/tools/aapt2/ZipFile.h
@@ -123,14 +123,16 @@
         int compressionMethod, ZipEntry** ppEntry);
 
     /*
-     * Add an entry by copying it from another zip file.  If "padding" is
+     * Add an entry by copying it from another zip file.  If storageName is
+     * non-NULL, the entry will be inserted with the name storageName, otherwise
+     * it will have the same name as the source entry.  If "padding" is
      * nonzero, the specified number of bytes will be added to the "extra"
      * field in the header.
      *
      * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
      */
     status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
-        int padding, ZipEntry** ppEntry);
+                 const char* storageName, int padding, ZipEntry** ppEntry);
 
     /*
      * Mark an entry as having been removed.  It is not actually deleted
diff --git a/tools/aapt2/data/Makefile b/tools/aapt2/data/Makefile
index 5a2a1d1..6b5fafa 100644
--- a/tools/aapt2/data/Makefile
+++ b/tools/aapt2/data/Makefile
@@ -2,10 +2,8 @@
 # Environment dependent variables
 ##
 
-SHELL := /bin/bash
 AAPT := aapt2
-ZIP := zip -n .arsc:.png:AndroidManifest.xml
-ZIPALIGN := zipalign 4
+ZIPALIGN := zipalign -f 4
 FRAMEWORK := ../../../../../out/target/common/obj/APPS/framework-res_intermediates/package-export.apk
 
 ##
diff --git a/tools/aapt2/data/lib/Makefile b/tools/aapt2/data/lib/Makefile
index 2897ff1..8f56c54 100644
--- a/tools/aapt2/data/lib/Makefile
+++ b/tools/aapt2/data/lib/Makefile
@@ -3,7 +3,7 @@
 ##
 
 AAPT := aapt2
-ZIPALIGN := zipalign 4
+ZIPALIGN := zipalign -f 4
 FRAMEWORK := ../../../../../../out/target/common/obj/APPS/framework-res_intermediates/package-export.apk
 
 ##
diff --git a/tools/aapt2/data/lib/res/layout/main.xml b/tools/aapt2/data/lib/res/layout/main.xml
new file mode 100644
index 0000000..187ed2d
--- /dev/null
+++ b/tools/aapt2/data/lib/res/layout/main.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"/>
diff --git a/tools/aapt2/data/lib/res/raw/hello.txt b/tools/aapt2/data/lib/res/raw/hello.txt
new file mode 100644
index 0000000..44fc22b
--- /dev/null
+++ b/tools/aapt2/data/lib/res/raw/hello.txt
@@ -0,0 +1 @@
+Oh howdy there