diff options
-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 |