2 * Copyright (C) 2015 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "ResourceUtils.h"
18 #include "link/ManifestFixer.h"
19 #include "util/Util.h"
20 #include "xml/XmlActionExecutor.h"
21 #include "xml/XmlDom.h"
26 * This is how PackageManager builds class names from AndroidManifest.xml entries.
28 static bool nameIsJavaClassName(xml::Element* el, xml::Attribute* attr,
29 SourcePathDiagnostics* diag) {
30 std::u16string className = attr->value;
31 if (className.find(u'.') == std::u16string::npos) {
32 // There is no '.', so add one to the beginning.
34 className += attr->value;
37 // We allow unqualified class names (ie: .HelloActivity)
38 // Since we don't know the package name, we can just make a fake one here and
39 // the test will be identical as long as the real package name is valid too.
40 Maybe<std::u16string> fullyQualifiedClassName =
41 util::getFullyQualifiedClassName(u"a", className);
43 StringPiece16 qualifiedClassName = fullyQualifiedClassName
44 ? fullyQualifiedClassName.value() : className;
45 if (!util::isJavaClassName(qualifiedClassName)) {
46 diag->error(DiagMessage(el->lineNumber)
47 << "attribute 'android:name' in <"
48 << el->name << "> tag must be a valid Java class name");
54 static bool optionalNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
55 if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) {
56 return nameIsJavaClassName(el, attr, diag);
61 static bool requiredNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
62 if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) {
63 return nameIsJavaClassName(el, attr, diag);
65 diag->error(DiagMessage(el->lineNumber)
66 << "<" << el->name << "> is missing attribute 'android:name'");
70 static bool verifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
71 xml::Attribute* attr = el->findAttribute({}, u"package");
73 diag->error(DiagMessage(el->lineNumber) << "<manifest> tag is missing 'package' attribute");
75 } else if (ResourceUtils::isReference(attr->value)) {
76 diag->error(DiagMessage(el->lineNumber)
77 << "attribute 'package' in <manifest> tag must not be a reference");
79 } else if (!util::isJavaPackageName(attr->value)) {
80 diag->error(DiagMessage(el->lineNumber)
81 << "attribute 'package' in <manifest> tag is not a valid Java package name: '"
82 << attr->value << "'");
88 bool ManifestFixer::buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag) {
89 // First verify some options.
90 if (mOptions.renameManifestPackage) {
91 if (!util::isJavaPackageName(mOptions.renameManifestPackage.value())) {
92 diag->error(DiagMessage() << "invalid manifest package override '"
93 << mOptions.renameManifestPackage.value() << "'");
98 if (mOptions.renameInstrumentationTargetPackage) {
99 if (!util::isJavaPackageName(mOptions.renameInstrumentationTargetPackage.value())) {
100 diag->error(DiagMessage() << "invalid instrumentation target package override '"
101 << mOptions.renameInstrumentationTargetPackage.value() << "'");
106 // Common intent-filter actions.
107 xml::XmlNodeAction intentFilterAction;
108 intentFilterAction[u"action"];
109 intentFilterAction[u"category"];
110 intentFilterAction[u"data"];
112 // Common meta-data actions.
113 xml::XmlNodeAction metaDataAction;
116 xml::XmlNodeAction& manifestAction = (*executor)[u"manifest"];
117 manifestAction.action(verifyManifest);
118 manifestAction.action([&](xml::Element* el) -> bool {
119 if (mOptions.versionNameDefault) {
120 if (el->findAttribute(xml::kSchemaAndroid, u"versionName") == nullptr) {
121 el->attributes.push_back(xml::Attribute{
124 mOptions.versionNameDefault.value() });
128 if (mOptions.versionCodeDefault) {
129 if (el->findAttribute(xml::kSchemaAndroid, u"versionCode") == nullptr) {
130 el->attributes.push_back(xml::Attribute{
133 mOptions.versionCodeDefault.value() });
140 manifestAction[u"eat-comment"];
143 manifestAction[u"uses-sdk"].action([&](xml::Element* el) -> bool {
144 if (mOptions.minSdkVersionDefault &&
145 el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion") == nullptr) {
146 // There was no minSdkVersion defined and we have a default to assign.
147 el->attributes.push_back(xml::Attribute{
148 xml::kSchemaAndroid, u"minSdkVersion",
149 mOptions.minSdkVersionDefault.value() });
152 if (mOptions.targetSdkVersionDefault &&
153 el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion") == nullptr) {
154 // There was no targetSdkVersion defined and we have a default to assign.
155 el->attributes.push_back(xml::Attribute{
156 xml::kSchemaAndroid, u"targetSdkVersion",
157 mOptions.targetSdkVersionDefault.value() });
162 // Instrumentation actions.
163 manifestAction[u"instrumentation"].action([&](xml::Element* el) -> bool {
164 if (!mOptions.renameInstrumentationTargetPackage) {
168 if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"targetPackage")) {
169 attr->value = mOptions.renameInstrumentationTargetPackage.value();
174 manifestAction[u"original-package"];
175 manifestAction[u"protected-broadcast"];
176 manifestAction[u"uses-permission"];
177 manifestAction[u"permission"];
178 manifestAction[u"permission-tree"];
179 manifestAction[u"permission-group"];
181 manifestAction[u"uses-configuration"];
182 manifestAction[u"uses-feature"];
183 manifestAction[u"supports-screens"];
184 manifestAction[u"compatible-screens"];
185 manifestAction[u"supports-gl-texture"];
187 // Application actions.
188 xml::XmlNodeAction& applicationAction = (*executor)[u"manifest"][u"application"];
189 applicationAction.action(optionalNameIsJavaClassName);
191 // Uses library actions.
192 applicationAction[u"uses-library"];
195 applicationAction[u"activity"].action(requiredNameIsJavaClassName);
196 applicationAction[u"activity"][u"intent-filter"] = intentFilterAction;
197 applicationAction[u"activity"][u"meta-data"] = metaDataAction;
199 // Activity alias actions.
200 applicationAction[u"activity-alias"][u"intent-filter"] = intentFilterAction;
201 applicationAction[u"activity-alias"][u"meta-data"] = metaDataAction;
204 applicationAction[u"service"].action(requiredNameIsJavaClassName);
205 applicationAction[u"service"][u"intent-filter"] = intentFilterAction;
206 applicationAction[u"service"][u"meta-data"] = metaDataAction;
209 applicationAction[u"receiver"].action(requiredNameIsJavaClassName);
210 applicationAction[u"receiver"][u"intent-filter"] = intentFilterAction;
211 applicationAction[u"receiver"][u"meta-data"] = metaDataAction;
214 applicationAction[u"provider"].action(requiredNameIsJavaClassName);
215 applicationAction[u"provider"][u"grant-uri-permissions"];
216 applicationAction[u"provider"][u"meta-data"] = metaDataAction;
217 applicationAction[u"provider"][u"path-permissions"];
221 class FullyQualifiedClassNameVisitor : public xml::Visitor {
223 using xml::Visitor::visit;
225 FullyQualifiedClassNameVisitor(const StringPiece16& package) : mPackage(package) {
228 void visit(xml::Element* el) override {
229 for (xml::Attribute& attr : el->attributes) {
230 if (Maybe<std::u16string> newValue =
231 util::getFullyQualifiedClassName(mPackage, attr.value)) {
232 attr.value = std::move(newValue.value());
236 // Super implementation to iterate over the children.
237 xml::Visitor::visit(el);
241 StringPiece16 mPackage;
244 static bool renameManifestPackage(const StringPiece16& packageOverride, xml::Element* manifestEl) {
245 xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
247 // We've already verified that the manifest element is present, with a package name specified.
250 std::u16string originalPackage = std::move(attr->value);
251 attr->value = packageOverride.toString();
253 FullyQualifiedClassNameVisitor visitor(originalPackage);
254 manifestEl->accept(&visitor);
258 bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
259 xml::Element* root = xml::findRootElement(doc->root.get());
260 if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
261 context->getDiagnostics()->error(DiagMessage(doc->file.source)
262 << "root tag must be <manifest>");
266 if ((mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)
267 && root->findChild({}, u"uses-sdk") == nullptr) {
268 // Auto insert a <uses-sdk> element.
269 std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
270 usesSdk->name = u"uses-sdk";
271 root->addChild(std::move(usesSdk));
274 xml::XmlActionExecutor executor;
275 if (!buildRules(&executor, context->getDiagnostics())) {
279 if (!executor.execute(xml::XmlActionExecutorPolicy::Whitelist, context->getDiagnostics(),
284 if (mOptions.renameManifestPackage) {
285 // Rename manifest package outside of the XmlActionExecutor.
286 // We need to extract the old package name and FullyQualify all class names.
287 if (!renameManifestPackage(mOptions.renameManifestPackage.value(), root)) {