diff options
| author | 2015-11-02 16:10:55 -0800 | |
|---|---|---|
| committer | 2015-11-02 16:56:34 -0800 | |
| commit | 2ae4a877d1623f851040ce69239552c873f1abf0 (patch) | |
| tree | b13fd89771d05c8f5471218695c9eff5ab287b23 | |
| parent | 3b4cd94034ff3e5567a2ba6da35d640ff61db4b9 (diff) | |
AAPT2: Add Manifest fixing/validation
Change-Id: I7f6d8b74d1c590adc356b4da55cb6cb777cdf1da
| -rw-r--r-- | tools/aapt2/Android.mk | 4 | ||||
| -rw-r--r-- | tools/aapt2/Flags.cpp | 10 | ||||
| -rw-r--r-- | tools/aapt2/ManifestValidator.cpp | 217 | ||||
| -rw-r--r-- | tools/aapt2/ManifestValidator.h | 55 | ||||
| -rw-r--r-- | tools/aapt2/ResourceUtils.cpp | 12 | ||||
| -rw-r--r-- | tools/aapt2/ResourceUtils.h | 5 | ||||
| -rw-r--r-- | tools/aapt2/XmlDom.h | 2 | ||||
| -rw-r--r-- | tools/aapt2/java/ManifestClassGenerator.cpp | 4 | ||||
| -rw-r--r-- | tools/aapt2/java/ProguardRules.cpp | 16 | ||||
| -rw-r--r-- | tools/aapt2/link/Link.cpp | 39 | ||||
| -rw-r--r-- | tools/aapt2/link/ManifestFixer.cpp | 100 | ||||
| -rw-r--r-- | tools/aapt2/link/ManifestFixer.h | 44 | ||||
| -rw-r--r-- | tools/aapt2/link/ManifestFixer_test.cpp | 165 | 
13 files changed, 373 insertions, 300 deletions
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk index a41d2d72b754..ceed21edeb1b 100644 --- a/tools/aapt2/Android.mk +++ b/tools/aapt2/Android.mk @@ -32,6 +32,7 @@ sources := \  	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 @@ testSources := \  	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 @@ else  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 6ae5af7bb92a..9435396991df 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 @@ void Flags::usage(const StringPiece& command, std::ostream* out) {          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 9f971fbcf728..000000000000 --- 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 1a7f48e9b5ac..000000000000 --- 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 0db1c372c901..ae3b4ff1e363 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -74,9 +74,11 @@ bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool*              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 @@ bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool*      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 118a2ee9d769..3c532de9060a 100644 --- a/tools/aapt2/ResourceUtils.h +++ b/tools/aapt2/ResourceUtils.h @@ -49,6 +49,11 @@ bool tryParseReference(const StringPiece16& str, ResourceNameRef* outReference,                         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 b1987f1fd245..9a46bcb9fc5c 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 c12da6465a2a..901a344881fc 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 Maybe<StringPiece16> extractJavaIdentifier(IDiagnostics* diag, const Sour  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 7683f27f40b3..44314772fbd4 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 @@ struct LayoutVisitor : public BaseVisitor {          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 @@ struct LayoutVisitor : public BaseVisitor {              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 @@ struct XmlResourceVisitor : public BaseVisitor {          }          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 @@ struct ManifestVisitor : public BaseVisitor {                  }              } 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 @@ struct ManifestVisitor : public BaseVisitor {              }              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 77918acb2996..0236e988162a 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 @@ struct LinkOptions {      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 @@ struct LinkCommand {      }      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 @@ struct LinkCommand {          }          { +            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 @@ struct LinkCommand {  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 @@ int link(const std::vector<StringPiece>& args) {              .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 @@ int link(const std::vector<StringPiece>& args) {          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 000000000000..52d942670243 --- /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 000000000000..16e161dc40d8 --- /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 000000000000..5c5d8afa610d --- /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  |