AAPT2: Add Manifest fixing/validation

Change-Id: I7f6d8b74d1c590adc356b4da55cb6cb777cdf1da
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index a41d2d7..ceed21e 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -32,6 +32,7 @@
 	flatten/TableFlattener.cpp \
 	flatten/XmlFlattener.cpp \
 	link/AutoVersioner.cpp \
+	link/ManifestFixer.cpp \
 	link/PrivateAttributeMover.cpp \
 	link/ReferenceLinker.cpp \
 	link/TableMerger.cpp \
@@ -67,6 +68,7 @@
 	flatten/TableFlattener_test.cpp \
 	flatten/XmlFlattener_test.cpp \
 	link/AutoVersioner_test.cpp \
+	link/ManifestFixer_test.cpp \
 	link/PrivateAttributeMover_test.cpp \
 	link/ReferenceLinker_test.cpp \
 	link/TableMerger_test.cpp \
@@ -113,7 +115,7 @@
 endif
 
 cFlags := -Wall -Werror -Wno-unused-parameter -UNDEBUG
-cppFlags := -std=c++11 -Wno-missing-field-initializers -fno-exceptions
+cppFlags := -std=c++11 -Wno-missing-field-initializers -fno-exceptions -fno-rtti
 
 # ==========================================================
 # Build the host static library: libaapt2
diff --git a/tools/aapt2/Flags.cpp b/tools/aapt2/Flags.cpp
index 6ae5af7..9435396 100644
--- a/tools/aapt2/Flags.cpp
+++ b/tools/aapt2/Flags.cpp
@@ -16,6 +16,7 @@
 
 #include "Flags.h"
 #include "util/StringPiece.h"
+#include "util/Util.h"
 
 #include <iomanip>
 #include <iostream>
@@ -94,7 +95,14 @@
         if (flag.numArgs > 0) {
             argLine += " arg";
         }
-        *out << " " << std::setw(30) << std::left << argLine << flag.description << "\n";
+
+        // Split the description by newlines and write out the argument (which is empty after
+        // 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";
+            argLine = " ";
+        }
     }
     *out << " " << std::setw(30) << std::left << "-h" << "Displays this help menu\n";
     out->flush();
diff --git a/tools/aapt2/ManifestValidator.cpp b/tools/aapt2/ManifestValidator.cpp
deleted file mode 100644
index 9f971fb..0000000
--- a/tools/aapt2/ManifestValidator.cpp
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Logger.h"
-#include "ManifestValidator.h"
-#include "util/Maybe.h"
-#include "Source.h"
-#include "util/Util.h"
-
-#include <androidfw/ResourceTypes.h>
-
-namespace aapt {
-
-ManifestValidator::ManifestValidator(const android::ResTable& table)
-: mTable(table) {
-}
-
-bool ManifestValidator::validate(const Source& source, android::ResXMLParser* parser) {
-    SourceLogger logger(source);
-
-    android::ResXMLParser::event_code_t code;
-    while ((code = parser->next()) != android::ResXMLParser::END_DOCUMENT &&
-            code != android::ResXMLParser::BAD_DOCUMENT) {
-        if (code != android::ResXMLParser::START_TAG) {
-            continue;
-        }
-
-        size_t len = 0;
-        const StringPiece16 namespaceUri(parser->getElementNamespace(&len), len);
-        if (!namespaceUri.empty()) {
-            continue;
-        }
-
-        const StringPiece16 name(parser->getElementName(&len), len);
-        if (name.empty()) {
-            logger.error(parser->getLineNumber())
-                    << "failed to get the element name."
-                    << std::endl;
-            return false;
-        }
-
-        if (name == u"manifest") {
-            if (!validateManifest(source, parser)) {
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
-Maybe<StringPiece16> ManifestValidator::getAttributeValue(android::ResXMLParser* parser,
-                                                          size_t idx) {
-    android::Res_value value;
-    if (parser->getAttributeValue(idx, &value) < 0) {
-        return StringPiece16();
-    }
-
-    const android::ResStringPool* pool = &parser->getStrings();
-    if (value.dataType == android::Res_value::TYPE_REFERENCE) {
-        ssize_t strIdx = mTable.resolveReference(&value, 0x10000000u);
-        if (strIdx < 0) {
-            return {};
-        }
-        pool = mTable.getTableStringBlock(strIdx);
-    }
-
-    if (value.dataType != android::Res_value::TYPE_STRING || !pool) {
-        return {};
-    }
-    return util::getString(*pool, value.data);
-}
-
-Maybe<StringPiece16> ManifestValidator::getAttributeInlineValue(android::ResXMLParser* parser,
-                                                                size_t idx) {
-    android::Res_value value;
-    if (parser->getAttributeValue(idx, &value) < 0) {
-        return StringPiece16();
-    }
-
-    if (value.dataType != android::Res_value::TYPE_STRING) {
-        return {};
-    }
-    return util::getString(parser->getStrings(), value.data);
-}
-
-bool ManifestValidator::validateInlineAttribute(android::ResXMLParser* parser, size_t idx,
-                                                SourceLogger& logger,
-                                                const StringPiece16& charSet) {
-    size_t len = 0;
-    StringPiece16 element(parser->getElementName(&len), len);
-    StringPiece16 attributeName(parser->getAttributeName(idx, &len), len);
-    Maybe<StringPiece16> result = getAttributeInlineValue(parser, idx);
-    if (!result) {
-        logger.error(parser->getLineNumber())
-                << "<"
-                << element
-                << "> must have a '"
-                << attributeName
-                << "' attribute with a string literal value."
-                << std::endl;
-        return false;
-    }
-    return validateAttributeImpl(element, attributeName, result.value(), charSet,
-                                 parser->getLineNumber(), logger);
-}
-
-bool ManifestValidator::validateAttribute(android::ResXMLParser* parser, size_t idx,
-                                          SourceLogger& logger, const StringPiece16& charSet) {
-    size_t len = 0;
-    StringPiece16 element(parser->getElementName(&len), len);
-    StringPiece16 attributeName(parser->getAttributeName(idx, &len), len);
-    Maybe<StringPiece16> result = getAttributeValue(parser, idx);
-    if (!result) {
-        logger.error(parser->getLineNumber())
-                << "<"
-                << element
-                << "> must have a '"
-                << attributeName
-                << "' attribute that points to a string."
-                << std::endl;
-        return false;
-    }
-    return validateAttributeImpl(element, attributeName, result.value(), charSet,
-                                 parser->getLineNumber(), logger);
-}
-
-bool ManifestValidator::validateAttributeImpl(const StringPiece16& element,
-                                              const StringPiece16& attributeName,
-                                              const StringPiece16& attributeValue,
-                                              const StringPiece16& charSet, size_t lineNumber,
-                                              SourceLogger& logger) {
-    StringPiece16::const_iterator badIter =
-            util::findNonAlphaNumericAndNotInSet(attributeValue, charSet);
-    if (badIter != attributeValue.end()) {
-        logger.error(lineNumber)
-                << "tag <"
-                << element
-                << "> attribute '"
-                << attributeName
-                << "' has invalid character '"
-                << StringPiece16(badIter, 1)
-                << "'."
-                << std::endl;
-        return false;
-    }
-
-    if (!attributeValue.empty()) {
-        StringPiece16 trimmed = util::trimWhitespace(attributeValue);
-        if (attributeValue.begin() != trimmed.begin()) {
-            logger.error(lineNumber)
-                    << "tag <"
-                    << element
-                    << "> attribute '"
-                    << attributeName
-                    << "' can not start with whitespace."
-                    << std::endl;
-            return false;
-        }
-
-        if (attributeValue.end() != trimmed.end()) {
-            logger.error(lineNumber)
-                    << "tag <"
-                    << element
-                    << "> attribute '"
-                    << attributeName
-                    << "' can not end with whitespace."
-                    << std::endl;
-            return false;
-        }
-    }
-    return true;
-}
-
-constexpr const char16_t* kPackageIdentSet = u"._";
-
-bool ManifestValidator::validateManifest(const Source& source, android::ResXMLParser* parser) {
-    bool error = false;
-    SourceLogger logger(source);
-
-    const StringPiece16 kAndroid = u"android";
-    const StringPiece16 kPackage = u"package";
-    const StringPiece16 kSharedUserId = u"sharedUserId";
-
-    ssize_t idx;
-
-    idx = parser->indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size());
-    if (idx < 0) {
-        logger.error(parser->getLineNumber())
-                << "missing package attribute."
-                << std::endl;
-        error = true;
-    } else {
-        error |= !validateInlineAttribute(parser, idx, logger, kPackageIdentSet);
-    }
-
-    idx = parser->indexOfAttribute(kAndroid.data(), kAndroid.size(),
-                                   kSharedUserId.data(), kSharedUserId.size());
-    if (idx >= 0) {
-        error |= !validateInlineAttribute(parser, idx, logger, kPackageIdentSet);
-    }
-    return !error;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/ManifestValidator.h b/tools/aapt2/ManifestValidator.h
deleted file mode 100644
index 1a7f48e..0000000
--- a/tools/aapt2/ManifestValidator.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef AAPT_MANIFEST_VALIDATOR_H
-#define AAPT_MANIFEST_VALIDATOR_H
-
-#include "Logger.h"
-#include "util/Maybe.h"
-#include "Source.h"
-#include "util/StringPiece.h"
-
-#include <androidfw/ResourceTypes.h>
-
-namespace aapt {
-
-class ManifestValidator {
-public:
-    ManifestValidator(const android::ResTable& table);
-    ManifestValidator(const ManifestValidator&) = delete;
-
-    bool validate(const Source& source, android::ResXMLParser* parser);
-
-private:
-    bool validateManifest(const Source& source, android::ResXMLParser* parser);
-
-    Maybe<StringPiece16> getAttributeInlineValue(android::ResXMLParser* parser, size_t idx);
-    Maybe<StringPiece16> getAttributeValue(android::ResXMLParser* parser, size_t idx);
-
-    bool validateInlineAttribute(android::ResXMLParser* parser, size_t idx,
-                                 SourceLogger& logger, const StringPiece16& charSet);
-    bool validateAttribute(android::ResXMLParser* parser, size_t idx, SourceLogger& logger,
-                           const StringPiece16& charSet);
-    bool validateAttributeImpl(const StringPiece16& element, const StringPiece16& attributeName,
-                               const StringPiece16& attributeValue, const StringPiece16& charSet,
-                               size_t lineNumber, SourceLogger& logger);
-
-    const android::ResTable& mTable;
-};
-
-} // namespace aapt
-
-#endif // AAPT_MANIFEST_VALIDATOR_H
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 0db1c37..ae3b4ff 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -74,9 +74,11 @@
             return false;
         }
 
-        outRef->package = package;
-        outRef->type = *parsedType;
-        outRef->entry = entry;
+        if (outRef != nullptr) {
+            outRef->package = package;
+            outRef->type = *parsedType;
+            outRef->entry = entry;
+        }
         if (outCreate) {
             *outCreate = create;
         }
@@ -88,6 +90,10 @@
     return false;
 }
 
+bool isReference(const StringPiece16& str) {
+    return tryParseReference(str, nullptr, nullptr, nullptr);
+}
+
 bool tryParseAttributeReference(const StringPiece16& str, ResourceNameRef* outRef) {
     StringPiece16 trimmedStr(util::trimWhitespace(str));
     if (trimmedStr.empty()) {
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 118a2ee..3c532de 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -49,6 +49,11 @@
                        bool* outCreate = nullptr, bool* outPrivate = nullptr);
 
 /*
+ * Returns true if the string is in the form of a resource reference (@[+][package:]type/name).
+ */
+bool isReference(const StringPiece16& str);
+
+/*
  * Returns true if the string was parsed as an attribute reference (?[package:]type/name),
  * with `outReference` set to the parsed reference.
  */
diff --git a/tools/aapt2/XmlDom.h b/tools/aapt2/XmlDom.h
index b1987f1..9a46bcb 100644
--- a/tools/aapt2/XmlDom.h
+++ b/tools/aapt2/XmlDom.h
@@ -34,6 +34,8 @@
 namespace aapt {
 namespace xml {
 
+constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
+
 struct RawVisitor;
 
 /**
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index c12da64..901a344 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -25,8 +25,6 @@
 
 namespace aapt {
 
-constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
-
 static Maybe<StringPiece16> extractJavaIdentifier(IDiagnostics* diag, const Source& source,
                                                   const StringPiece16& value) {
     const StringPiece16 sep = u".";
@@ -62,7 +60,7 @@
 
 static bool writeSymbol(IDiagnostics* diag, const Source& source, xml::Element* el,
                         std::ostream* out) {
-    xml::Attribute* attr = el->findAttribute(kSchemaAndroid, u"name");
+    xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name");
     if (!attr) {
         diag->error(DiagMessage(source) << "<" << el->name << "> must define 'android:name'");
         return false;
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index 7683f27..4431477 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -25,8 +25,6 @@
 namespace aapt {
 namespace proguard {
 
-constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
-
 class BaseVisitor : public xml::Visitor {
 public:
     BaseVisitor(const Source& source, KeepSet* keepSet) : mSource(source), mKeepSet(keepSet) {
@@ -83,7 +81,7 @@
         bool checkName = false;
         if (node->namespaceUri.empty()) {
             checkClass = node->name == u"view" || node->name == u"fragment";
-        } else if (node->namespaceUri == kSchemaAndroid) {
+        } else if (node->namespaceUri == xml::kSchemaAndroid) {
             checkName = node->name == u"fragment";
         }
 
@@ -91,10 +89,10 @@
             if (checkClass && attr.namespaceUri.empty() && attr.name == u"class" &&
                     util::isJavaClassName(attr.value)) {
                 addClass(node->lineNumber, attr.value);
-            } else if (checkName && attr.namespaceUri == kSchemaAndroid && attr.name == u"name" &&
-                    util::isJavaClassName(attr.value)) {
+            } else if (checkName && attr.namespaceUri == xml::kSchemaAndroid &&
+                    attr.name == u"name" && util::isJavaClassName(attr.value)) {
                 addClass(node->lineNumber, attr.value);
-            } else if (attr.namespaceUri == kSchemaAndroid && attr.name == u"onClick") {
+            } else if (attr.namespaceUri == xml::kSchemaAndroid && attr.name == u"onClick") {
                 addMethod(node->lineNumber, attr.value);
             }
         }
@@ -114,7 +112,7 @@
         }
 
         if (checkFragment) {
-            xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"fragment");
+            xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"fragment");
             if (attr && util::isJavaClassName(attr->value)) {
                 addClass(node->lineNumber, attr->value);
             }
@@ -156,7 +154,7 @@
                 }
             } else if (node->name == u"application") {
                 getName = true;
-                xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"backupAgent");
+                xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"backupAgent");
                 if (attr) {
                     Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
                                                                                     attr->value);
@@ -171,7 +169,7 @@
             }
 
             if (getName) {
-                xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"name");
+                xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"name");
                 if (attr) {
                     Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
                                                                                     attr->value);
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 77918ac..0236e98 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -28,6 +28,7 @@
 #include "java/ManifestClassGenerator.h"
 #include "java/ProguardRules.h"
 #include "link/Linkers.h"
+#include "link/ManifestFixer.h"
 #include "link/TableMerger.h"
 #include "process/IResourceTableConsumer.h"
 #include "process/SymbolTable.h"
@@ -54,6 +55,8 @@
     bool verbose = false;
     bool outputToDirectory = false;
     Maybe<std::u16string> privateSymbols;
+    Maybe<std::u16string> minSdkVersionDefault;
+    Maybe<std::u16string> targetSdkVersionDefault;
 };
 
 struct LinkContext : public IAaptContext {
@@ -240,15 +243,8 @@
     }
 
     Maybe<AppInfo> extractAppInfoFromManifest(XmlResource* xmlRes) {
-        xml::Node* node = xmlRes->root.get();
-
-        // Find the first xml::Element.
-        while (node && !xml::nodeCast<xml::Element>(node)) {
-            node = !node->children.empty() ? node->children.front().get() : nullptr;
-        }
-
         // Make sure the first element is <manifest> with package attribute.
-        if (xml::Element* manifestEl = xml::nodeCast<xml::Element>(node)) {
+        if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
             if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
                 if (xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package")) {
                     return AppInfo{ packageAttr->value };
@@ -570,9 +566,16 @@
         }
 
         {
+            ManifestFixerOptions manifestFixerOptions;
+            manifestFixerOptions.minSdkVersionDefault = mOptions.minSdkVersionDefault;
+            manifestFixerOptions.targetSdkVersionDefault = mOptions.targetSdkVersionDefault;
+            ManifestFixer manifestFixer(manifestFixerOptions);
+            if (!manifestFixer.consume(&mContext, manifestXml.get())) {
+                error = true;
+            }
+
             XmlReferenceLinker manifestLinker;
             if (manifestLinker.consume(&mContext, manifestXml.get())) {
-
                 if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
                                                                manifestXml.get(),
                                                                &proguardKeepSet)) {
@@ -742,6 +745,7 @@
 int link(const std::vector<StringPiece>& args) {
     LinkOptions options;
     Maybe<std::string> privateSymbolsPackage;
+    Maybe<std::string> minSdkVersion, targetSdkVersion;
     Flags flags = Flags()
             .requiredFlag("-o", "Output path", &options.outputPath)
             .requiredFlag("--manifest", "Path to the Android manifest to build",
@@ -757,10 +761,15 @@
             .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
                             "by -o",
                             &options.outputToDirectory)
+            .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
+                          "AndroidManifest.xml", &minSdkVersion)
+            .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
+                          "AndroidManifest.xml", &targetSdkVersion)
             .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
             .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
-                          "private symbols. If not specified, public and private symbols will "
-                          "use the application's package name", &privateSymbolsPackage)
+                          "private symbols.\n"
+                          "If not specified, public and private symbols will use the application's "
+                          "package name", &privateSymbolsPackage)
             .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
 
     if (!flags.parse("aapt2 link", args, &std::cerr)) {
@@ -771,6 +780,14 @@
         options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
     }
 
+    if (minSdkVersion) {
+        options.minSdkVersionDefault = util::utf8ToUtf16(minSdkVersion.value());
+    }
+
+    if (targetSdkVersion) {
+        options.targetSdkVersionDefault = util::utf8ToUtf16(targetSdkVersion.value());
+    }
+
     LinkCommand cmd = { options };
     return cmd.run(flags.getArgs());
 }
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
new file mode 100644
index 0000000..52d9426
--- /dev/null
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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 "ResourceUtils.h"
+#include "XmlDom.h"
+
+#include "link/ManifestFixer.h"
+#include "util/Util.h"
+
+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;
+    }
+
+    return !error;
+}
+
+static bool fixUsesSdk(IAaptContext* context, const Source& source, xml::Element* el,
+                       const ManifestFixerOptions& options) {
+    if (options.minSdkVersionDefault &&
+            el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion") == nullptr) {
+        // There was no minSdkVersion defined and we have a default to assign.
+        el->attributes.push_back(xml::Attribute{
+                xml::kSchemaAndroid, u"minSdkVersion", options.minSdkVersionDefault.value() });
+    }
+
+    if (options.targetSdkVersionDefault &&
+            el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion") == nullptr) {
+        // There was no targetSdkVersion defined and we have a default to assign.
+        el->attributes.push_back(xml::Attribute{
+                xml::kSchemaAndroid, u"targetSdkVersion",
+                options.targetSdkVersionDefault.value() });
+    }
+    return true;
+}
+
+bool ManifestFixer::consume(IAaptContext* context, XmlResource* doc) {
+    xml::Element* root = xml::findRootElement(doc->root.get());
+    if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
+        context->getDiagnostics()->error(DiagMessage(doc->file.source)
+                                         << "root tag must be <manifest>");
+        return false;
+    }
+
+    if (!verifyManifest(context, doc->file.source, root)) {
+        return false;
+    }
+
+    bool foundUsesSdk = false;
+    for (xml::Element* el : root->getChildElements()) {
+        if (!el->namespaceUri.empty()) {
+            continue;
+        }
+
+        if (el->name == u"uses-sdk") {
+            foundUsesSdk = true;
+            fixUsesSdk(context, doc->file.source, el, mOptions);
+        }
+    }
+
+    if (!foundUsesSdk && (mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)) {
+        std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
+        usesSdk->name = u"uses-sdk";
+        fixUsesSdk(context, doc->file.source, usesSdk.get(), mOptions);
+        root->addChild(std::move(usesSdk));
+    }
+
+    return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
new file mode 100644
index 0000000..16e161d
--- /dev/null
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_LINK_MANIFESTFIXER_H
+#define AAPT_LINK_MANIFESTFIXER_H
+
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+struct ManifestFixerOptions {
+    Maybe<std::u16string> minSdkVersionDefault;
+    Maybe<std::u16string> targetSdkVersionDefault;
+};
+
+/**
+ * Verifies that the manifest is correctly formed and inserts defaults
+ * where specified with ManifestFixerOptions.
+ */
+struct ManifestFixer : public IXmlResourceConsumer {
+    ManifestFixerOptions mOptions;
+
+    ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) {
+    }
+
+    bool consume(IAaptContext* context, XmlResource* doc) override;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_LINK_MANIFESTFIXER_H */
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
new file mode 100644
index 0000000..5c5d8af
--- /dev/null
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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 "link/ManifestFixer.h"
+
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+struct ManifestFixerTest : public ::testing::Test {
+    std::unique_ptr<IAaptContext> mContext;
+
+    void SetUp() override {
+        mContext = test::ContextBuilder()
+                .setCompilationPackage(u"android")
+                .setPackageId(0x01)
+                .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+                .setSymbolTable(test::StaticSymbolTableBuilder()
+                        .addSymbol(u"@android:attr/package", ResourceId(0x01010000),
+                                   test::AttributeBuilder()
+                                        .setTypeMask(android::ResTable_map::TYPE_STRING)
+                                        .build())
+                        .addSymbol(u"@android:attr/minSdkVersion", ResourceId(0x01010001),
+                                   test::AttributeBuilder()
+                                        .setTypeMask(android::ResTable_map::TYPE_STRING |
+                                                     android::ResTable_map::TYPE_INTEGER)
+                                        .build())
+                        .addSymbol(u"@android:attr/targetSdkVersion", ResourceId(0x01010002),
+                                   test::AttributeBuilder()
+                                        .setTypeMask(android::ResTable_map::TYPE_STRING |
+                                                     android::ResTable_map::TYPE_INTEGER)
+                                        .build())
+                        .addSymbol(u"@android:string/str", ResourceId(0x01060000))
+                        .build())
+                .build();
+    }
+
+    std::unique_ptr<XmlResource> verify(const StringPiece& str) {
+        return verifyWithOptions(str, {});
+    }
+
+    std::unique_ptr<XmlResource> verifyWithOptions(const StringPiece& str,
+                                                   const ManifestFixerOptions& options) {
+        std::unique_ptr<XmlResource> doc = test::buildXmlDom(str);
+        ManifestFixer fixer(options);
+        if (fixer.consume(mContext.get(), doc.get())) {
+            return doc;
+        }
+        return {};
+    }
+};
+
+TEST_F(ManifestFixerTest, EnsureManifestIsRootTag) {
+    EXPECT_EQ(nullptr, verify("<other-tag />"));
+    EXPECT_EQ(nullptr, verify("<ns:manifest xmlns:ns=\"com\" />"));
+    EXPECT_NE(nullptr, verify("<manifest package=\"android\"></manifest>"));
+}
+
+TEST_F(ManifestFixerTest, EnsureManifestHasPackage) {
+    EXPECT_NE(nullptr, verify("<manifest package=\"android\" />"));
+    EXPECT_NE(nullptr, verify("<manifest package=\"com.android\" />"));
+    EXPECT_NE(nullptr, verify("<manifest package=\"com.android.google\" />"));
+    EXPECT_EQ(nullptr, verify("<manifest package=\"com.android.google.Class$1\" />"));
+    EXPECT_EQ(nullptr,
+              verify("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" "
+                     "android:package=\"com.android\" />"));
+    EXPECT_EQ(nullptr, verify("<manifest package=\"@string/str\" />"));
+}
+
+
+
+TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
+    ManifestFixerOptions options = { std::u16string(u"8"), std::u16string(u"22") };
+
+    std::unique_ptr<XmlResource> doc = verifyWithOptions(R"EOF(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                package="android">
+        <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" />
+      </manifest>)EOF", options);
+    ASSERT_NE(nullptr, doc);
+
+    xml::Element* el;
+    xml::Attribute* attr;
+
+    el = xml::findRootElement(doc->root.get());
+    ASSERT_NE(nullptr, el);
+    el = el->findChild({}, u"uses-sdk");
+    ASSERT_NE(nullptr, el);
+    attr = el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(u"7", attr->value);
+    attr = el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(u"21", attr->value);
+
+    doc = verifyWithOptions(R"EOF(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                package="android">
+        <uses-sdk android:targetSdkVersion="21" />
+      </manifest>)EOF", options);
+    ASSERT_NE(nullptr, doc);
+
+    el = xml::findRootElement(doc->root.get());
+    ASSERT_NE(nullptr, el);
+    el = el->findChild({}, u"uses-sdk");
+    ASSERT_NE(nullptr, el);
+    attr = el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(u"8", attr->value);
+    attr = el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(u"21", attr->value);
+
+    doc = verifyWithOptions(R"EOF(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                package="android">
+        <uses-sdk />
+      </manifest>)EOF", options);
+    ASSERT_NE(nullptr, doc);
+
+    el = xml::findRootElement(doc->root.get());
+    ASSERT_NE(nullptr, el);
+    el = el->findChild({}, u"uses-sdk");
+    ASSERT_NE(nullptr, el);
+    attr = el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(u"8", attr->value);
+    attr = el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(u"22", attr->value);
+
+    doc = verifyWithOptions(R"EOF(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                package="android" />)EOF", options);
+    ASSERT_NE(nullptr, doc);
+
+    el = xml::findRootElement(doc->root.get());
+    ASSERT_NE(nullptr, el);
+    el = el->findChild({}, u"uses-sdk");
+    ASSERT_NE(nullptr, el);
+    attr = el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(u"8", attr->value);
+    attr = el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(u"22", attr->value);
+}
+
+} // namespace aapt