AAPT2: Add Manifest fixing/validation
Change-Id: I7f6d8b74d1c590adc356b4da55cb6cb777cdf1da
This commit is contained in:
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
@ -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
|
@ -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()) {
|
||||
|
@ -48,6 +48,11 @@ void extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
|
||||
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.
|
||||
|
@ -34,6 +34,8 @@
|
||||
namespace aapt {
|
||||
namespace xml {
|
||||
|
||||
constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
|
||||
|
||||
struct RawVisitor;
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
100
tools/aapt2/link/ManifestFixer.cpp
Normal file
100
tools/aapt2/link/ManifestFixer.cpp
Normal file
@ -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
|
44
tools/aapt2/link/ManifestFixer.h
Normal file
44
tools/aapt2/link/ManifestFixer.h
Normal file
@ -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 */
|
165
tools/aapt2/link/ManifestFixer_test.cpp
Normal file
165
tools/aapt2/link/ManifestFixer_test.cpp
Normal file
@ -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
|
Reference in New Issue
Block a user