/* * 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 "link/ManifestFixer.h" #include "util/Util.h" #include "xml/XmlActionExecutor.h" #include "xml/XmlDom.h" namespace aapt { /** * This is how PackageManager builds class names from AndroidManifest.xml entries. */ static bool nameIsJavaClassName(xml::Element* el, xml::Attribute* attr, SourcePathDiagnostics* diag) { std::u16string className = attr->value; if (className.find(u'.') == std::u16string::npos) { // There is no '.', so add one to the beginning. className = u"."; className += attr->value; } // We allow unqualified class names (ie: .HelloActivity) // Since we don't know the package name, we can just make a fake one here and // the test will be identical as long as the real package name is valid too. Maybe<std::u16string> fullyQualifiedClassName = util::getFullyQualifiedClassName(u"a", className); StringPiece16 qualifiedClassName = fullyQualifiedClassName ? fullyQualifiedClassName.value() : className; if (!util::isJavaClassName(qualifiedClassName)) { diag->error(DiagMessage(el->lineNumber) << "attribute 'android:name' in <" << el->name << "> tag must be a valid Java class name"); return false; } return true; } static bool optionalNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) { if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) { return nameIsJavaClassName(el, attr, diag); } return true; } static bool requiredNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) { if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) { return nameIsJavaClassName(el, attr, diag); } diag->error(DiagMessage(el->lineNumber) << "<" << el->name << "> is missing attribute 'android:name'"); return false; } static bool verifyManifest(xml::Element* el, SourcePathDiagnostics* diag) { xml::Attribute* attr = el->findAttribute({}, u"package"); if (!attr) { diag->error(DiagMessage(el->lineNumber) << "<manifest> tag is missing 'package' attribute"); return false; } else if (ResourceUtils::isReference(attr->value)) { diag->error(DiagMessage(el->lineNumber) << "attribute 'package' in <manifest> tag must not be a reference"); return false; } else if (!util::isJavaPackageName(attr->value)) { diag->error(DiagMessage(el->lineNumber) << "attribute 'package' in <manifest> tag is not a valid Java package name: '" << attr->value << "'"); return false; } return true; } bool ManifestFixer::buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag) { // First verify some options. if (mOptions.renameManifestPackage) { if (!util::isJavaPackageName(mOptions.renameManifestPackage.value())) { diag->error(DiagMessage() << "invalid manifest package override '" << mOptions.renameManifestPackage.value() << "'"); return false; } } if (mOptions.renameInstrumentationTargetPackage) { if (!util::isJavaPackageName(mOptions.renameInstrumentationTargetPackage.value())) { diag->error(DiagMessage() << "invalid instrumentation target package override '" << mOptions.renameInstrumentationTargetPackage.value() << "'"); return false; } } // Common intent-filter actions. xml::XmlNodeAction intentFilterAction; intentFilterAction[u"action"]; intentFilterAction[u"category"]; intentFilterAction[u"data"]; // Common meta-data actions. xml::XmlNodeAction metaDataAction; // Manifest actions. xml::XmlNodeAction& manifestAction = (*executor)[u"manifest"]; manifestAction.action(verifyManifest); manifestAction.action([&](xml::Element* el) -> bool { if (mOptions.versionNameDefault) { if (el->findAttribute(xml::kSchemaAndroid, u"versionName") == nullptr) { el->attributes.push_back(xml::Attribute{ xml::kSchemaAndroid, u"versionName", mOptions.versionNameDefault.value() }); } } if (mOptions.versionCodeDefault) { if (el->findAttribute(xml::kSchemaAndroid, u"versionCode") == nullptr) { el->attributes.push_back(xml::Attribute{ xml::kSchemaAndroid, u"versionCode", mOptions.versionCodeDefault.value() }); } } return true; }); // Meta tags. manifestAction[u"eat-comment"]; // Uses-sdk actions. manifestAction[u"uses-sdk"].action([&](xml::Element* el) -> bool { if (mOptions.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", mOptions.minSdkVersionDefault.value() }); } if (mOptions.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", mOptions.targetSdkVersionDefault.value() }); } return true; }); // Instrumentation actions. manifestAction[u"instrumentation"].action([&](xml::Element* el) -> bool { if (!mOptions.renameInstrumentationTargetPackage) { return true; } if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"targetPackage")) { attr->value = mOptions.renameInstrumentationTargetPackage.value(); } return true; }); manifestAction[u"original-package"]; manifestAction[u"protected-broadcast"]; manifestAction[u"uses-permission"]; manifestAction[u"permission"]; manifestAction[u"permission-tree"]; manifestAction[u"permission-group"]; manifestAction[u"uses-configuration"]; manifestAction[u"uses-feature"]; manifestAction[u"uses-library"]; manifestAction[u"supports-screens"]; manifestAction[u"compatible-screens"]; manifestAction[u"supports-gl-texture"]; // Application actions. xml::XmlNodeAction& applicationAction = (*executor)[u"manifest"][u"application"]; applicationAction.action(optionalNameIsJavaClassName); // Activity actions. applicationAction[u"activity"].action(requiredNameIsJavaClassName); applicationAction[u"activity"][u"intent-filter"] = intentFilterAction; applicationAction[u"activity"][u"meta-data"] = metaDataAction; // Activity alias actions. applicationAction[u"activity-alias"][u"intent-filter"] = intentFilterAction; applicationAction[u"activity-alias"][u"meta-data"] = metaDataAction; // Service actions. applicationAction[u"service"].action(requiredNameIsJavaClassName); applicationAction[u"service"][u"intent-filter"] = intentFilterAction; applicationAction[u"service"][u"meta-data"] = metaDataAction; // Receiver actions. applicationAction[u"receiver"].action(requiredNameIsJavaClassName); applicationAction[u"receiver"][u"intent-filter"] = intentFilterAction; applicationAction[u"receiver"][u"meta-data"] = metaDataAction; // Provider actions. applicationAction[u"provider"].action(requiredNameIsJavaClassName); applicationAction[u"provider"][u"grant-uri-permissions"]; applicationAction[u"provider"][u"meta-data"] = metaDataAction; applicationAction[u"provider"][u"path-permissions"]; return true; } class FullyQualifiedClassNameVisitor : public xml::Visitor { public: using xml::Visitor::visit; FullyQualifiedClassNameVisitor(const StringPiece16& package) : mPackage(package) { } void visit(xml::Element* el) override { for (xml::Attribute& attr : el->attributes) { if (Maybe<std::u16string> newValue = util::getFullyQualifiedClassName(mPackage, attr.value)) { attr.value = std::move(newValue.value()); } } // Super implementation to iterate over the children. xml::Visitor::visit(el); } private: StringPiece16 mPackage; }; static bool renameManifestPackage(const StringPiece16& packageOverride, xml::Element* manifestEl) { xml::Attribute* attr = manifestEl->findAttribute({}, u"package"); // We've already verified that the manifest element is present, with a package name specified. assert(attr); std::u16string originalPackage = std::move(attr->value); attr->value = packageOverride.toString(); FullyQualifiedClassNameVisitor visitor(originalPackage); manifestEl->accept(&visitor); return true; } bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) { xml::Element* root = xml::findRootElement(doc->root.get()); if (!root || !root->namespaceUri.empty() || root->name != u"manifest") { context->getDiagnostics()->error(DiagMessage(doc->file.source) << "root tag must be <manifest>"); return false; } if ((mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault) && root->findChild({}, u"uses-sdk") == nullptr) { // Auto insert a <uses-sdk> element. std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>(); usesSdk->name = u"uses-sdk"; root->addChild(std::move(usesSdk)); } xml::XmlActionExecutor executor; if (!buildRules(&executor, context->getDiagnostics())) { return false; } if (!executor.execute(xml::XmlActionExecutorPolicy::Whitelist, context->getDiagnostics(), doc)) { return false; } if (mOptions.renameManifestPackage) { // Rename manifest package outside of the XmlActionExecutor. // We need to extract the old package name and FullyQualify all class names. if (!renameManifestPackage(mOptions.renameManifestPackage.value(), root)) { return false; } } return true; } } // namespace aapt