AAPT2: Variety of small fixes to get the build working

- Add option to rename package in AndroidManifest.xml
- Support default versionName and versionCode
- Accept True and False as valid booleans

Change-Id: I400e350b9dcd0fd1c197d1929144299c7823617d
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index 64353de..13f8b3b 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -30,6 +30,11 @@
 
 static const char* kWildcardName = "any";
 
+const ConfigDescription& ConfigDescription::defaultConfig() {
+    static ConfigDescription config = {};
+    return config;
+}
+
 static bool parseMcc(const char* name, ResTable_config* out) {
     if (strcmp(name, kWildcardName) == 0) {
         if (out) out->mcc = 0;
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index 4af089d..5749816 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -29,6 +29,11 @@
  * initialization and comparison methods.
  */
 struct ConfigDescription : public android::ResTable_config {
+    /**
+     * Returns an immutable default config.
+     */
+    static const ConfigDescription& defaultConfig();
+
     /*
      * Parse a string of the form 'fr-sw600dp-land' and fill in the
      * given ResTable_config with resulting configuration parameters.
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 5fce2c1..b4e75f9 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -210,4 +210,19 @@
     std::cout << "}" << std::endl;
 }
 
+void Debug::dumpHex(const void* data, size_t len) {
+    const uint8_t* d = (const uint8_t*) data;
+    for (size_t i = 0; i < len; i++) {
+        std::cerr << std::hex << std::setfill('0') << std::setw(2) << (uint32_t) d[i] << " ";
+        if (i % 8 == 7) {
+            std::cerr << "\n";
+        }
+    }
+
+    if (len - 1 % 8 != 7) {
+        std::cerr << std::endl;
+    }
+}
+
+
 } // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index 5b0d7d6..ba05be9 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -20,12 +20,16 @@
 #include "Resource.h"
 #include "ResourceTable.h"
 
+// Include for printf-like debugging.
+#include <iostream>
+
 namespace aapt {
 
 struct Debug {
     static void printTable(ResourceTable* table);
     static void printStyleGraph(ResourceTable* table,
                                 const ResourceName& targetStyle);
+    static void dumpHex(const void* data, size_t len);
 };
 
 } // namespace aapt
diff --git a/tools/aapt2/Flags.cpp b/tools/aapt2/Flags.cpp
index 9435396..666e8a8e 100644
--- a/tools/aapt2/Flags.cpp
+++ b/tools/aapt2/Flags.cpp
@@ -81,6 +81,8 @@
 }
 
 void Flags::usage(const StringPiece& command, std::ostream* out) {
+    constexpr size_t kWidth = 50;
+
     *out << command << " [options]";
     for (const Flag& flag : mFlags) {
         if (flag.required) {
@@ -100,11 +102,11 @@
         // the first line) followed by the description line. This will make sure that multiline
         // descriptions are still right justified and aligned.
         for (StringPiece line : util::tokenize<char>(flag.description, '\n')) {
-            *out << " " << std::setw(30) << std::left << argLine << line << "\n";
+            *out << " " << std::setw(kWidth) << std::left << argLine << line << "\n";
             argLine = " ";
         }
     }
-    *out << " " << std::setw(30) << std::left << "-h" << "Displays this help menu\n";
+    *out << " " << std::setw(kWidth) << std::left << "-h" << "Displays this help menu\n";
     out->flush();
 }
 
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 5e7d3ec..b37d366 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -70,6 +70,7 @@
  */
 struct ParsedResource {
     ResourceName name;
+    ConfigDescription config;
     Source source;
     ResourceId id;
     Maybe<SymbolState> symbolState;
@@ -108,8 +109,7 @@
 }
 
 // Recursively adds resources to the ResourceTable.
-static bool addResourcesToTable(ResourceTable* table, const ConfigDescription& config,
-                                IDiagnostics* diag, ParsedResource* res) {
+static bool addResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) {
     if (res->symbolState) {
         Symbol symbol;
         symbol.state = res->symbolState.value();
@@ -125,14 +125,14 @@
         res->value->setComment(std::move(res->comment));
         res->value->setSource(std::move(res->source));
 
-        if (!table->addResource(res->name, res->id, config, std::move(res->value), diag)) {
+        if (!table->addResource(res->name, res->id, res->config, std::move(res->value), diag)) {
             return false;
         }
     }
 
     bool error = false;
     for (ParsedResource& child : res->childResources) {
-        error |= !addResourcesToTable(table, config, diag, &child);
+        error |= !addResourcesToTable(table, diag, &child);
     }
     return !error;
 }
@@ -290,6 +290,7 @@
         }
 
         ParsedResource parsedResource;
+        parsedResource.config = mConfig;
         parsedResource.source = mSource.withLine(parser->getLineNumber());
         parsedResource.comment = std::move(comment);
 
@@ -310,7 +311,7 @@
             // Record that we stripped out this resource name.
             // We will check that at least one variant of this resource was included.
             strippedResources.insert(parsedResource.name);
-        } else if (!addResourcesToTable(mTable, mConfig, mDiag, &parsedResource)) {
+        } else if (!addResourcesToTable(mTable, mDiag, &parsedResource)) {
             error = true;
         }
     }
@@ -769,6 +770,13 @@
                                    bool weak) {
     outResource->name.type = ResourceType::kAttr;
 
+    // Attributes only end up in default configuration.
+    if (outResource->config != ConfigDescription::defaultConfig()) {
+        mDiag->warn(DiagMessage(outResource->source) << "ignoring configuration '"
+                    << outResource->config << "' for attribute " << outResource->name);
+        outResource->config = ConfigDescription::defaultConfig();
+    }
+
     uint32_t typeMask = 0;
 
     Maybe<StringPiece16> maybeFormat = xml::findAttribute(parser, u"format");
@@ -940,8 +948,7 @@
     }
 
     return Attribute::Symbol{
-            Reference(ResourceNameRef({}, ResourceType::kId, maybeName.value())),
-            val.data };
+            Reference(ResourceNameRef({}, ResourceType::kId, maybeName.value())), val.data };
 }
 
 static Maybe<Reference> parseXmlAttributeName(StringPiece16 str) {
@@ -1190,12 +1197,21 @@
     return true;
 }
 
-bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* outResource) {
+bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser,
+                                           ParsedResource* outResource) {
     outResource->name.type = ResourceType::kStyleable;
 
     // Declare-styleable is kPrivate by default, because it technically only exists in R.java.
     outResource->symbolState = SymbolState::kPublic;
 
+    // Declare-styleable only ends up in default config;
+    if (outResource->config != ConfigDescription::defaultConfig()) {
+        mDiag->warn(DiagMessage(outResource->source) << "ignoring configuration '"
+                            << outResource->config << "' for styleable "
+                            << outResource->name.entry);
+        outResource->config = ConfigDescription::defaultConfig();
+    }
+
     std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
 
     std::u16string comment;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 8d10ba1..cf0fcd1 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -47,13 +47,26 @@
         mContext = test::ContextBuilder().build();
     }
 
+    ::testing::AssertionResult testParse(const StringPiece& str) {
+        return testParse(str, ConfigDescription{}, {});
+    }
+
+    ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config) {
+        return testParse(str, config, {});
+    }
+
     ::testing::AssertionResult testParse(const StringPiece& str,
-                                         std::initializer_list<std::u16string> products = {}) {
+                                         std::initializer_list<std::u16string> products) {
+        return testParse(str, {}, std::move(products));
+    }
+
+    ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config,
+                                         std::initializer_list<std::u16string> products) {
         std::stringstream input(kXmlPreamble);
         input << "<resources>\n" << str << "\n</resources>" << std::endl;
         ResourceParserOptions parserOptions;
         parserOptions.products = products;
-        ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, {},
+        ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, config,
                               parserOptions);
         xml::XmlPullParser xmlParser(input);
         if (parser.parse(&xmlParser)) {
@@ -138,6 +151,26 @@
     EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
 }
 
+// Old AAPT allowed attributes to be defined under different configurations, but ultimately
+// stored them with the default configuration. Check that we have the same behavior.
+TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
+    const ConfigDescription watchConfig = test::parseConfigOrDie("watch");
+    std::string input = R"EOF(
+        <attr name="foo" />
+        <declare-styleable name="bar">
+          <attr name="baz" />
+        </declare-styleable>)EOF";
+    ASSERT_TRUE(testParse(input, watchConfig));
+
+    EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, u"@attr/foo", watchConfig));
+    EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, u"@attr/baz", watchConfig));
+    EXPECT_EQ(nullptr, test::getValueForConfig<Styleable>(&mTable, u"@styleable/bar", watchConfig));
+
+    EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, u"@attr/foo"));
+    EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, u"@attr/baz"));
+    EXPECT_NE(nullptr, test::getValue<Styleable>(&mTable, u"@styleable/bar"));
+}
+
 TEST_F(ResourceParserTest, ParseAttrWithMinMax) {
     std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>";
     ASSERT_TRUE(testParse(input));
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 1dc123e..07f62af 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -176,7 +176,7 @@
 /*
  * Style parent's are a bit different. We accept the following formats:
  *
- * @[[*]package:]style/<entry>
+ * @[[*]package:][style/]<entry>
  * ?[[*]package:]style/<entry>
  * <[*]package>:[style/]<entry>
  * [[*]package:style/]<entry>
@@ -216,14 +216,6 @@
             *outError = err.str();
             return {};
         }
-    } else {
-        // No type was defined, this should not have a leading identifier.
-        if (hasLeadingIdentifiers) {
-            std::stringstream err;
-            err << "invalid parent reference '" << str << "'";
-            *outError = err.str();
-            return {};
-        }
     }
 
     if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
@@ -294,6 +286,12 @@
                                                     const StringPiece16& str) {
     android::Res_value flags = { };
     flags.dataType = android::Res_value::TYPE_INT_DEC;
+    flags.data = 0u;
+
+    if (util::trimWhitespace(str).empty()) {
+        // Empty string is a valid flag (0).
+        return util::make_unique<BinaryPrimitive>(flags);
+    }
 
     for (StringPiece16 part : util::tokenize(str, u'|')) {
         StringPiece16 trimmedPart = util::trimWhitespace(part);
@@ -386,12 +384,12 @@
 
 bool tryParseBool(const StringPiece16& str, bool* outValue) {
     StringPiece16 trimmedStr(util::trimWhitespace(str));
-    if (trimmedStr == u"true" || trimmedStr == u"TRUE") {
+    if (trimmedStr == u"true" || trimmedStr == u"TRUE" || trimmedStr == u"True") {
         if (outValue) {
             *outValue = true;
         }
         return true;
-    } else if (trimmedStr == u"false" || trimmedStr == u"FALSE") {
+    } else if (trimmedStr == u"false" || trimmedStr == u"FALSE" || trimmedStr == u"False") {
         if (outValue) {
             *outValue = false;
         }
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index 88efa67..c9f93e1 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -16,12 +16,34 @@
 
 #include "Resource.h"
 #include "ResourceUtils.h"
+#include "test/Builders.h"
 #include "test/Common.h"
 
 #include <gtest/gtest.h>
 
 namespace aapt {
 
+TEST(ResourceUtilsTest, ParseBool) {
+    bool val = false;
+    EXPECT_TRUE(ResourceUtils::tryParseBool(u"true", &val));
+    EXPECT_TRUE(val);
+
+    EXPECT_TRUE(ResourceUtils::tryParseBool(u"TRUE", &val));
+    EXPECT_TRUE(val);
+
+    EXPECT_TRUE(ResourceUtils::tryParseBool(u"True", &val));
+    EXPECT_TRUE(val);
+
+    EXPECT_TRUE(ResourceUtils::tryParseBool(u"false", &val));
+    EXPECT_FALSE(val);
+
+    EXPECT_TRUE(ResourceUtils::tryParseBool(u"FALSE", &val));
+    EXPECT_FALSE(val);
+
+    EXPECT_TRUE(ResourceUtils::tryParseBool(u"False", &val));
+    EXPECT_FALSE(val);
+}
+
 TEST(ResourceUtilsTest, ParseResourceName) {
     ResourceNameRef actual;
     bool actualPriv = false;
@@ -154,6 +176,10 @@
     AAPT_ASSERT_TRUE(ref);
     EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
 
+    ref = ResourceUtils::parseStyleParentReference(u"@android:foo", &errStr);
+    AAPT_ASSERT_TRUE(ref);
+    EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+
     ref = ResourceUtils::parseStyleParentReference(u"foo", &errStr);
     AAPT_ASSERT_TRUE(ref);
     EXPECT_EQ(ref.value().name.value(), kStyleFooName);
@@ -164,4 +190,16 @@
     EXPECT_TRUE(ref.value().privateReference);
 }
 
+TEST(ResourceUtilsTest, ParseEmptyFlag) {
+    std::unique_ptr<Attribute> attr = test::AttributeBuilder(false)
+            .setTypeMask(android::ResTable_map::TYPE_FLAGS)
+            .addItem(u"one", 0x01)
+            .addItem(u"two", 0x02)
+            .build();
+
+    std::unique_ptr<BinaryPrimitive> result = ResourceUtils::tryParseFlagSymbol(attr.get(), u"");
+    ASSERT_NE(nullptr, result);
+    EXPECT_EQ(0u, result->value.data);
+}
+
 } // namespace aapt
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index 8552f47..aadb00b 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
+#include "StringPool.h"
 #include "util/BigBuffer.h"
 #include "util/StringPiece.h"
-#include "StringPool.h"
 #include "util/Util.h"
 
 #include <algorithm>
@@ -342,7 +342,14 @@
 
             // Encode the actual UTF16 string length.
             data = encodeLength(data, entry->value.size());
-            strncpy16(data, entry->value.data(), entry->value.size());
+            const size_t byteLength = entry->value.size() * sizeof(char16_t);
+
+            // NOTE: For some reason, strncpy16(data, entry->value.data(), entry->value.size())
+            // truncates the string.
+            memcpy(data, entry->value.data(), byteLength);
+
+            // The null-terminating character is already here due to the block of data being set
+            // to 0s on allocation.
         }
     }
 
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index c722fbe..e93c2fba 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -180,6 +180,22 @@
     ASSERT_EQ(test.setTo(data.get(), buffer.size()), android::NO_ERROR);
 }
 
+TEST(StringPoolTest, FlattenOddCharactersUtf16) {
+    StringPool pool;
+    pool.makeRef(u"\u093f");
+    BigBuffer buffer(1024);
+    StringPool::flattenUtf16(&buffer, pool);
+
+    std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+    android::ResStringPool test;
+    ASSERT_EQ(test.setTo(data.get(), buffer.size()), android::NO_ERROR);
+    size_t len = 0;
+    const char16_t* str = test.stringAt(0, &len);
+    EXPECT_EQ(1u, len);
+    EXPECT_EQ(u'\u093f', *str);
+    EXPECT_EQ(0u, str[1]);
+}
+
 constexpr const char16_t* sLongString = u"バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限します。メール、SMSや、同期を使 用するその他のアプリは、起動しても更新されないことがあります。バッテリーセーバーは端末の充電中は自動的にOFFになります。";
 
 TEST(StringPoolTest, FlattenUtf8) {
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index c78670f..689ace6 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -117,7 +117,11 @@
     if (!data.configStr.empty()) {
         name << "-" << data.configStr;
     }
-    name << "_" << data.name << "." << data.extension << ".flat";
+    name << "_" << data.name;
+    if (!data.extension.empty()) {
+        name << "." << data.extension;
+    }
+    name << ".flat";
     return name.str();
 }
 
@@ -386,16 +390,26 @@
     fileExportWriter.getChunkHeader()->size =
             util::hostToDevice32(buffer.size() + f.value().getDataLength());
 
-    if (writer->writeEntry(buffer)) {
-        if (writer->writeEntry(f.value().getDataPtr(), f.value().getDataLength())) {
-            if (writer->finishEntry()) {
-                return true;
-            }
+    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;
         }
     }
 
-    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;
 }
 
 class CompileContext : public IAaptContext {
diff --git a/tools/aapt2/io/Data.h b/tools/aapt2/io/Data.h
index 9081c55..467e604 100644
--- a/tools/aapt2/io/Data.h
+++ b/tools/aapt2/io/Data.h
@@ -79,6 +79,21 @@
     size_t mSize;
 };
 
+/**
+ * When mmap fails because the file has length 0, we use the EmptyData to simulate data of length 0.
+ */
+class EmptyData : public IData {
+public:
+    const void* data() const override {
+        static const uint8_t d = 0;
+        return &d;
+    }
+
+    size_t size() const override {
+        return 0u;
+    }
+};
+
 } // namespace io
 } // namespace aapt
 
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index 76f87ae..e758d8a4 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -32,7 +32,10 @@
 std::unique_ptr<IData> RegularFile::openAsData() {
     android::FileMap map;
     if (Maybe<android::FileMap> map = file::mmapPath(mSource.path, nullptr)) {
-        return util::make_unique<MmappedData>(std::move(map.value()));
+        if (map.value().getDataPtr() && map.value().getDataLength() > 0) {
+            return util::make_unique<MmappedData>(std::move(map.value()));
+        }
+        return util::make_unique<EmptyData>();
     }
     return {};
 }
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 8a87d96..2a4c020 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -51,16 +51,19 @@
     std::vector<std::string> includePaths;
     std::vector<std::string> overlayFiles;
     Maybe<std::string> generateJavaClassPath;
-    std::set<std::string> extraJavaPackages;
+    Maybe<std::u16string> customJavaPackage;
+    std::set<std::u16string> extraJavaPackages;
     Maybe<std::string> generateProguardRulesPath;
     bool noAutoVersion = false;
     bool staticLib = false;
     bool verbose = false;
     bool outputToDirectory = false;
     bool autoAddOverlay = false;
+    bool doNotCompressAnything = false;
+    std::vector<std::string> extensionsToNotCompress;
     Maybe<std::u16string> privateSymbols;
-    Maybe<std::u16string> minSdkVersionDefault;
-    Maybe<std::u16string> targetSdkVersionDefault;
+    ManifestFixerOptions manifestFixerOptions;
+
 };
 
 struct LinkContext : public IAaptContext {
@@ -186,7 +189,20 @@
         return resFile;
     }
 
-    bool copyFileToArchive(io::IFile* file, const std::string& outPath, uint32_t flags,
+    uint32_t getCompressionFlags(const StringPiece& str) {
+        if (mOptions.doNotCompressAnything) {
+            return 0;
+        }
+
+        for (const std::string& extension : mOptions.extensionsToNotCompress) {
+            if (util::stringEndsWith<char>(str, extension)) {
+                return 0;
+            }
+        }
+        return ArchiveEntry::kCompress;
+    }
+
+    bool copyFileToArchive(io::IFile* file, const std::string& outPath,
                            IArchiveWriter* writer) {
         std::unique_ptr<io::IData> data = file->openAsData();
         if (!data) {
@@ -202,7 +218,7 @@
             return false;
         }
 
-        if (writer->startEntry(outPath, flags)) {
+        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->finishEntry()) {
@@ -319,7 +335,6 @@
             return false;
         }
 
-
         if (writer->startEntry(path, ArchiveEntry::kCompress)) {
             if (writer->writeEntry(buffer)) {
                 if (writer->finishEntry()) {
@@ -520,7 +535,7 @@
         const Source& src = file->getSource();
         if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
             return mergeResourceTable(file, override);
-        } else {
+        } else if (util::stringEndsWith<char>(src.path, ".flat")){
             // Try opening the file and looking for an Export header.
             std::unique_ptr<io::IData> data = file->openAsData();
             if (!data) {
@@ -533,7 +548,11 @@
             if (resourceFile) {
                 return mergeCompiledFile(file, std::move(resourceFile), override);
             }
+        } else {
+            // Ignore non .flat files. This could be classes.dex or something else that happens
+            // to be in an archive.
         }
+
         return false;
     }
 
@@ -646,10 +665,7 @@
 
         bool error = false;
         {
-            ManifestFixerOptions manifestFixerOptions;
-            manifestFixerOptions.minSdkVersionDefault = mOptions.minSdkVersionDefault;
-            manifestFixerOptions.targetSdkVersionDefault = mOptions.targetSdkVersionDefault;
-            ManifestFixer manifestFixer(manifestFixerOptions);
+            ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
             if (!manifestFixer.consume(&mContext, manifestXml.get())) {
                 error = true;
             }
@@ -786,7 +802,7 @@
                     mContext.getDiagnostics()->note(DiagMessage() << "copying " << path);
                 }
 
-                if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath, 0,
+                if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath,
                                        archiveWriter.get())) {
                     error = true;
                 }
@@ -813,12 +829,18 @@
 
         if (mOptions.generateJavaClassPath) {
             JavaClassGeneratorOptions options;
+            options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+
             if (mOptions.staticLib) {
                 options.useFinal = false;
             }
 
-            StringPiece16 actualPackage = mContext.getCompilationPackage();
+            const StringPiece16 actualPackage = mContext.getCompilationPackage();
             StringPiece16 outputPackage = mContext.getCompilationPackage();
+            if (mOptions.customJavaPackage) {
+                // Override the output java package to the custom one.
+                outputPackage = mOptions.customJavaPackage.value();
+            }
 
             if (mOptions.privateSymbols) {
                 // If we defined a private symbols package, we only emit Public symbols
@@ -826,7 +848,7 @@
 
                 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
                 if (!writeJavaFile(&mFinalTable, mContext.getCompilationPackage(),
-                                   mContext.getCompilationPackage(), options)) {
+                                   outputPackage, options)) {
                     return 1;
                 }
 
@@ -838,9 +860,8 @@
                 return 1;
             }
 
-            for (const std::string& extraPackage : mOptions.extraJavaPackages) {
-                if (!writeJavaFile(&mFinalTable, actualPackage, util::utf8ToUtf16(extraPackage),
-                                   options)) {
+            for (const std::u16string& extraPackage : mOptions.extraJavaPackages) {
+                if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
                     return 1;
                 }
             }
@@ -877,13 +898,16 @@
     LinkOptions options;
     Maybe<std::string> privateSymbolsPackage;
     Maybe<std::string> minSdkVersion, targetSdkVersion;
+    Maybe<std::string> renameManifestPackage, renameInstrumentationTargetPackage;
+    Maybe<std::string> versionCode, versionName;
+    Maybe<std::string> customJavaPackage;
     std::vector<std::string> extraJavaPackages;
     Flags flags = Flags()
             .requiredFlag("-o", "Output path", &options.outputPath)
             .requiredFlag("--manifest", "Path to the Android manifest to build",
                           &options.manifestPath)
             .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
-            .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics. "
+            .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
                               "The last conflicting resource given takes precedence.",
                               &options.overlayFiles)
             .optionalFlag("--java", "Directory in which to generate R.java",
@@ -900,15 +924,29 @@
                           "AndroidManifest.xml", &minSdkVersion)
             .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
                           "AndroidManifest.xml", &targetSdkVersion)
+            .optionalFlag("--version-code", "Version code (integer) to inject into the "
+                          "AndroidManifest.xml if none is present", &versionCode)
+            .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
+                          "if none is present", &versionName)
             .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
             .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
                           "private symbols.\n"
                           "If not specified, public and private symbols will use the application's "
                           "package name", &privateSymbolsPackage)
+            .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
+                          &customJavaPackage)
             .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
                               "package names", &extraJavaPackages)
             .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
                             "overlays without <add-resource> tags", &options.autoAddOverlay)
+            .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
+                          &renameManifestPackage)
+            .optionalFlag("--rename-instrumentation-target-package",
+                          "Changes the name of the target package for instrumentation. Most useful "
+                          "when used\nin conjunction with --rename-manifest-package",
+                          &renameInstrumentationTargetPackage)
+            .optionalFlagList("-0", "File extensions not to compress",
+                              &options.extensionsToNotCompress)
             .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
 
     if (!flags.parse("aapt2 link", args, &std::cerr)) {
@@ -920,18 +958,42 @@
     }
 
     if (minSdkVersion) {
-        options.minSdkVersionDefault = util::utf8ToUtf16(minSdkVersion.value());
+        options.manifestFixerOptions.minSdkVersionDefault =
+                util::utf8ToUtf16(minSdkVersion.value());
     }
 
     if (targetSdkVersion) {
-        options.targetSdkVersionDefault = util::utf8ToUtf16(targetSdkVersion.value());
+        options.manifestFixerOptions.targetSdkVersionDefault =
+                util::utf8ToUtf16(targetSdkVersion.value());
+    }
+
+    if (renameManifestPackage) {
+        options.manifestFixerOptions.renameManifestPackage =
+                util::utf8ToUtf16(renameManifestPackage.value());
+    }
+
+    if (renameInstrumentationTargetPackage) {
+        options.manifestFixerOptions.renameInstrumentationTargetPackage =
+                util::utf8ToUtf16(renameInstrumentationTargetPackage.value());
+    }
+
+    if (versionCode) {
+        options.manifestFixerOptions.versionCodeDefault = util::utf8ToUtf16(versionCode.value());
+    }
+
+    if (versionName) {
+        options.manifestFixerOptions.versionNameDefault = util::utf8ToUtf16(versionName.value());
+    }
+
+    if (customJavaPackage) {
+        options.customJavaPackage = util::utf8ToUtf16(customJavaPackage.value());
     }
 
     // Populate the set of extra packages for which to generate R.java.
     for (std::string& extraPackage : extraJavaPackages) {
         // A given package can actually be a colon separated list of packages.
         for (StringPiece package : util::split(extraPackage, ':')) {
-            options.extraJavaPackages.insert(package.toString());
+            options.extraJavaPackages.insert(util::utf8ToUtf16(package));
         }
     }
 
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 2034c57..9baf1d8 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -22,25 +22,43 @@
 namespace aapt {
 
 static bool verifyManifest(IAaptContext* context, const Source& source, xml::Element* manifestEl) {
-    bool error = false;
-
     xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
     if (!attr) {
         context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
                                          << "missing 'package' attribute");
-        error = true;
     } else if (ResourceUtils::isReference(attr->value)) {
         context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
                                          << "value for attribute 'package' must not be a "
                                             "reference");
-        error = true;
     } else if (!util::isJavaPackageName(attr->value)) {
         context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
                                          << "invalid package name '" << attr->value << "'");
-        error = true;
+    } else {
+        return true;
+    }
+    return false;
+}
+
+static bool includeVersionName(IAaptContext* context, const Source& source,
+                               const StringPiece16& versionName, xml::Element* manifestEl) {
+    if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionName")) {
+        return true;
     }
 
-    return !error;
+    manifestEl->attributes.push_back(xml::Attribute{
+            xml::kSchemaAndroid, u"versionName", versionName.toString() });
+    return true;
+}
+
+static bool includeVersionCode(IAaptContext* context, const Source& source,
+                               const StringPiece16& versionCode, xml::Element* manifestEl) {
+    if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionCode")) {
+        return true;
+    }
+
+    manifestEl->attributes.push_back(xml::Attribute{
+            xml::kSchemaAndroid, u"versionCode", versionCode.toString() });
+    return true;
 }
 
 static bool fixUsesSdk(IAaptContext* context, const Source& source, xml::Element* el,
@@ -62,6 +80,76 @@
     return true;
 }
 
+class FullyQualifiedClassNameVisitor : public xml::Visitor {
+public:
+    using xml::Visitor::visit;
+
+    FullyQualifiedClassNameVisitor(const StringPiece16& package) : mPackage(package) {
+    }
+
+    void visit(xml::Element* el) override {
+        for (xml::Attribute& attr : el->attributes) {
+            if (Maybe<std::u16string> newValue =
+                    util::getFullyQualifiedClassName(mPackage, attr.value)) {
+                attr.value = std::move(newValue.value());
+            }
+        }
+
+        // Super implementation to iterate over the children.
+        xml::Visitor::visit(el);
+    }
+
+private:
+    StringPiece16 mPackage;
+};
+
+static bool renameManifestPackage(IAaptContext* context, const Source& source,
+                                  const StringPiece16& packageOverride, xml::Element* manifestEl) {
+    if (!util::isJavaPackageName(packageOverride)) {
+        context->getDiagnostics()->error(DiagMessage() << "invalid manifest package override '"
+                                         << packageOverride << "'");
+        return false;
+    }
+
+    xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
+
+    // We've already verified that the manifest element is present, with a package name specified.
+    assert(attr);
+
+    std::u16string originalPackage = std::move(attr->value);
+    attr->value = packageOverride.toString();
+
+    FullyQualifiedClassNameVisitor visitor(originalPackage);
+    manifestEl->accept(&visitor);
+    return true;
+}
+
+static bool renameInstrumentationTargetPackage(IAaptContext* context, const Source& source,
+                                               const StringPiece16& packageOverride,
+                                               xml::Element* manifestEl) {
+    if (!util::isJavaPackageName(packageOverride)) {
+        context->getDiagnostics()->error(DiagMessage()
+                                         << "invalid instrumentation target package override '"
+                                         << packageOverride << "'");
+        return false;
+    }
+
+    xml::Element* instrumentationEl = manifestEl->findChild({}, u"instrumentation");
+    if (!instrumentationEl) {
+        // No error if there is no work to be done.
+        return true;
+    }
+
+    xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, u"targetPackage");
+    if (!attr) {
+        // No error if there is no work to be done.
+        return true;
+    }
+
+    attr->value = packageOverride.toString();
+    return true;
+}
+
 bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
     xml::Element* root = xml::findRootElement(doc->root.get());
     if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
@@ -74,6 +162,36 @@
         return false;
     }
 
+    if (mOptions.versionCodeDefault) {
+        if (!includeVersionCode(context, doc->file.source, mOptions.versionCodeDefault.value(),
+                                root)) {
+            return false;
+        }
+    }
+
+    if (mOptions.versionNameDefault) {
+        if (!includeVersionName(context, doc->file.source, mOptions.versionNameDefault.value(),
+                                root)) {
+            return false;
+        }
+    }
+
+    if (mOptions.renameManifestPackage) {
+        // Rename manifest package.
+        if (!renameManifestPackage(context, doc->file.source,
+                                   mOptions.renameManifestPackage.value(), root)) {
+            return false;
+        }
+    }
+
+    if (mOptions.renameInstrumentationTargetPackage) {
+        if (!renameInstrumentationTargetPackage(context, doc->file.source,
+                                                mOptions.renameInstrumentationTargetPackage.value(),
+                                                root)) {
+            return false;
+        }
+    }
+
     bool foundUsesSdk = false;
     for (xml::Element* el : root->getChildElements()) {
         if (!el->namespaceUri.empty()) {
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index a77e6d5..b8d9c83 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -28,6 +28,10 @@
 struct ManifestFixerOptions {
     Maybe<std::u16string> minSdkVersionDefault;
     Maybe<std::u16string> targetSdkVersionDefault;
+    Maybe<std::u16string> renameManifestPackage;
+    Maybe<std::u16string> renameInstrumentationTargetPackage;
+    Maybe<std::u16string> versionNameDefault;
+    Maybe<std::u16string> versionCodeDefault;
 };
 
 /**
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index f6bf895..f40fbfb 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -82,8 +82,6 @@
     EXPECT_EQ(nullptr, verify("<manifest package=\"@string/str\" />"));
 }
 
-
-
 TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
     ManifestFixerOptions options = { std::u16string(u"8"), std::u16string(u"22") };
 
@@ -97,7 +95,7 @@
     xml::Element* el;
     xml::Attribute* attr;
 
-    el = xml::findRootElement(doc->root.get());
+    el = xml::findRootElement(doc.get());
     ASSERT_NE(nullptr, el);
     el = el->findChild({}, u"uses-sdk");
     ASSERT_NE(nullptr, el);
@@ -115,7 +113,7 @@
       </manifest>)EOF", options);
     ASSERT_NE(nullptr, doc);
 
-    el = xml::findRootElement(doc->root.get());
+    el = xml::findRootElement(doc.get());
     ASSERT_NE(nullptr, el);
     el = el->findChild({}, u"uses-sdk");
     ASSERT_NE(nullptr, el);
@@ -133,7 +131,7 @@
       </manifest>)EOF", options);
     ASSERT_NE(nullptr, doc);
 
-    el = xml::findRootElement(doc->root.get());
+    el = xml::findRootElement(doc.get());
     ASSERT_NE(nullptr, el);
     el = el->findChild({}, u"uses-sdk");
     ASSERT_NE(nullptr, el);
@@ -149,7 +147,7 @@
                 package="android" />)EOF", options);
     ASSERT_NE(nullptr, doc);
 
-    el = xml::findRootElement(doc->root.get());
+    el = xml::findRootElement(doc.get());
     ASSERT_NE(nullptr, el);
     el = el->findChild({}, u"uses-sdk");
     ASSERT_NE(nullptr, el);
@@ -161,4 +159,98 @@
     EXPECT_EQ(u"22", attr->value);
 }
 
+TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) {
+    ManifestFixerOptions options;
+    options.renameManifestPackage = std::u16string(u"com.android");
+
+    std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                package="android">
+        <application name=".MainApplication" text="hello">
+          <activity name=".activity.Start" />
+          <receiver name="com.google.android.Receiver" />
+        </application>
+      </manifest>)EOF", options);
+    ASSERT_NE(nullptr, doc);
+
+    xml::Element* manifestEl = xml::findRootElement(doc.get());
+    ASSERT_NE(nullptr, manifestEl);
+
+    xml::Attribute* attr = nullptr;
+
+    attr = manifestEl->findAttribute({}, u"package");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(std::u16string(u"com.android"), attr->value);
+
+    xml::Element* applicationEl = manifestEl->findChild({}, u"application");
+    ASSERT_NE(nullptr, applicationEl);
+
+    attr = applicationEl->findAttribute({}, u"name");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(std::u16string(u"android.MainApplication"), attr->value);
+
+    attr = applicationEl->findAttribute({}, u"text");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(std::u16string(u"hello"), attr->value);
+
+    xml::Element* el;
+    el = applicationEl->findChild({}, u"activity");
+    ASSERT_NE(nullptr, el);
+
+    attr = el->findAttribute({}, u"name");
+    ASSERT_NE(nullptr, el);
+    EXPECT_EQ(std::u16string(u"android.activity.Start"), attr->value);
+
+    el = applicationEl->findChild({}, u"receiver");
+    ASSERT_NE(nullptr, el);
+
+    attr = el->findAttribute({}, u"name");
+    ASSERT_NE(nullptr, el);
+    EXPECT_EQ(std::u16string(u"com.google.android.Receiver"), attr->value);
+}
+
+TEST_F(ManifestFixerTest, RenameManifestInstrumentationPackageAndFullyQualifyTarget) {
+    ManifestFixerOptions options;
+    options.renameInstrumentationTargetPackage = std::u16string(u"com.android");
+
+    std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                package="android">
+        <instrumentation android:targetPackage="android" />
+      </manifest>)EOF", options);
+    ASSERT_NE(nullptr, doc);
+
+    xml::Element* manifestEl = xml::findRootElement(doc.get());
+    ASSERT_NE(nullptr, manifestEl);
+
+    xml::Element* instrumentationEl = manifestEl->findChild({}, u"instrumentation");
+    ASSERT_NE(nullptr, instrumentationEl);
+
+    xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, u"targetPackage");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(std::u16string(u"com.android"), attr->value);
+}
+
+TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) {
+    ManifestFixerOptions options;
+    options.versionNameDefault = std::u16string(u"Beta");
+    options.versionCodeDefault = std::u16string(u"0x10000000");
+
+    std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                package="android" />)EOF", options);
+    ASSERT_NE(nullptr, doc);
+
+    xml::Element* manifestEl = xml::findRootElement(doc.get());
+    ASSERT_NE(nullptr, manifestEl);
+
+    xml::Attribute* attr = manifestEl->findAttribute(xml::kSchemaAndroid, u"versionName");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(std::u16string(u"Beta"), attr->value);
+
+    attr = manifestEl->findAttribute(xml::kSchemaAndroid, u"versionCode");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(std::u16string(u"0x10000000"), attr->value);
+}
+
 } // namespace aapt
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index a81dc7b..04e8199 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -163,6 +163,11 @@
     }
 
     android::FileMap fileMap;
+    if (fileStats.st_size == 0) {
+        // mmap doesn't like a length of 0. Instead we return an empty FileMap.
+        return std::move(fileMap);
+    }
+
     if (!fileMap.create(path.data(), fd, 0, fileStats.st_size, true)) {
         if (outError) *outError = strerror(errno);
         return {};
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index 9ecc974..7b0c71d 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -175,10 +175,11 @@
         return {};
     }
 
-    std::u16string result(package.data(), package.size());
     if (className.data()[0] != u'.') {
-        result += u'.';
+        return {};
     }
+
+    std::u16string result(package.data(), package.size());
     result.append(className.data(), className.size());
     if (!isJavaClassName(result)) {
         return {};
diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
index 9208e07..1e0c7fa 100644
--- a/tools/aapt2/util/Util_test.cpp
+++ b/tools/aapt2/util/Util_test.cpp
@@ -144,8 +144,7 @@
 
 TEST(UtilTest, FullyQualifiedClassName) {
     Maybe<std::u16string> res = util::getFullyQualifiedClassName(u"android", u"asdf");
-    AAPT_ASSERT_TRUE(res);
-    EXPECT_EQ(res.value(), u"android.asdf");
+    AAPT_ASSERT_FALSE(res);
 
     res = util::getFullyQualifiedClassName(u"android", u".asdf");
     AAPT_ASSERT_TRUE(res);