blob: 77a949f1339d8828943ef3d09d3fb05121bcc84a [file] [log] [blame]
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17#include "ResourceUtils.h"
Adam Lesinski2ae4a872015-11-02 16:10:55 -080018#include "link/ManifestFixer.h"
19#include "util/Util.h"
Adam Lesinskicc5609d2016-04-05 12:41:07 -070020#include "xml/XmlActionExecutor.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080021#include "xml/XmlDom.h"
Adam Lesinski2ae4a872015-11-02 16:10:55 -080022
23namespace aapt {
24
Adam Lesinskicc5609d2016-04-05 12:41:07 -070025/**
26 * This is how PackageManager builds class names from AndroidManifest.xml entries.
27 */
28static 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.
33 className = u".";
34 className += attr->value;
Adam Lesinski52364f72016-01-11 13:10:24 -080035 }
Adam Lesinskicc5609d2016-04-05 12:41:07 -070036
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);
42
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");
49 return false;
50 }
51 return true;
52}
53
54static bool optionalNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
55 if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) {
56 return nameIsJavaClassName(el, attr, diag);
57 }
58 return true;
59}
60
61static bool requiredNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
62 if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) {
63 return nameIsJavaClassName(el, attr, diag);
64 }
65 diag->error(DiagMessage(el->lineNumber)
66 << "<" << el->name << "> is missing attribute 'android:name'");
Adam Lesinski52364f72016-01-11 13:10:24 -080067 return false;
68}
69
Adam Lesinskicc5609d2016-04-05 12:41:07 -070070static bool verifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
71 xml::Attribute* attr = el->findAttribute({}, u"package");
72 if (!attr) {
73 diag->error(DiagMessage(el->lineNumber) << "<manifest> tag is missing 'package' attribute");
74 return false;
75 } else if (ResourceUtils::isReference(attr->value)) {
76 diag->error(DiagMessage(el->lineNumber)
77 << "attribute 'package' in <manifest> tag must not be a reference");
78 return false;
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 << "'");
83 return false;
Adam Lesinski2ae4a872015-11-02 16:10:55 -080084 }
Adam Lesinski52364f72016-01-11 13:10:24 -080085 return true;
86}
87
Adam Lesinskicc5609d2016-04-05 12:41:07 -070088bool 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() << "'");
94 return false;
95 }
96 }
97
98 if (mOptions.renameInstrumentationTargetPackage) {
99 if (!util::isJavaPackageName(mOptions.renameInstrumentationTargetPackage.value())) {
100 diag->error(DiagMessage() << "invalid instrumentation target package override '"
101 << mOptions.renameInstrumentationTargetPackage.value() << "'");
102 return false;
103 }
104 }
105
106 // Common intent-filter actions.
107 xml::XmlNodeAction intentFilterAction;
108 intentFilterAction[u"action"];
109 intentFilterAction[u"category"];
110 intentFilterAction[u"data"];
111
112 // Common meta-data actions.
113 xml::XmlNodeAction metaDataAction;
114
115 // Manifest actions.
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{
122 xml::kSchemaAndroid,
123 u"versionName",
124 mOptions.versionNameDefault.value() });
125 }
126 }
127
128 if (mOptions.versionCodeDefault) {
129 if (el->findAttribute(xml::kSchemaAndroid, u"versionCode") == nullptr) {
130 el->attributes.push_back(xml::Attribute{
131 xml::kSchemaAndroid,
132 u"versionCode",
133 mOptions.versionCodeDefault.value() });
134 }
135 }
Adam Lesinski52364f72016-01-11 13:10:24 -0800136 return true;
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700137 });
Adam Lesinski52364f72016-01-11 13:10:24 -0800138
Adam Lesinski5ff3ad62016-04-13 20:30:45 -0700139 // Meta tags.
140 manifestAction[u"eat-comment"];
141
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700142 // Uses-sdk actions.
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() });
150 }
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800151
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700152 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() });
158 }
159 return true;
160 });
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800161
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700162 // Instrumentation actions.
163 manifestAction[u"instrumentation"].action([&](xml::Element* el) -> bool {
164 if (!mOptions.renameInstrumentationTargetPackage) {
165 return true;
166 }
167
168 if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"targetPackage")) {
169 attr->value = mOptions.renameInstrumentationTargetPackage.value();
170 }
171 return true;
172 });
173
Adam Lesinski5ff3ad62016-04-13 20:30:45 -0700174 manifestAction[u"original-package"];
Adam Lesinskic728c3d2016-04-06 17:40:25 -0700175 manifestAction[u"protected-broadcast"];
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700176 manifestAction[u"uses-permission"];
177 manifestAction[u"permission"];
178 manifestAction[u"permission-tree"];
179 manifestAction[u"permission-group"];
180
181 manifestAction[u"uses-configuration"];
182 manifestAction[u"uses-feature"];
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700183 manifestAction[u"supports-screens"];
184 manifestAction[u"compatible-screens"];
185 manifestAction[u"supports-gl-texture"];
186
187 // Application actions.
188 xml::XmlNodeAction& applicationAction = (*executor)[u"manifest"][u"application"];
189 applicationAction.action(optionalNameIsJavaClassName);
190
Adam Lesinskifee32d42016-05-31 15:07:20 -0700191 // Uses library actions.
192 applicationAction[u"uses-library"];
193
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700194 // Activity actions.
195 applicationAction[u"activity"].action(requiredNameIsJavaClassName);
196 applicationAction[u"activity"][u"intent-filter"] = intentFilterAction;
197 applicationAction[u"activity"][u"meta-data"] = metaDataAction;
198
199 // Activity alias actions.
200 applicationAction[u"activity-alias"][u"intent-filter"] = intentFilterAction;
201 applicationAction[u"activity-alias"][u"meta-data"] = metaDataAction;
202
203 // Service actions.
204 applicationAction[u"service"].action(requiredNameIsJavaClassName);
205 applicationAction[u"service"][u"intent-filter"] = intentFilterAction;
206 applicationAction[u"service"][u"meta-data"] = metaDataAction;
207
208 // Receiver actions.
209 applicationAction[u"receiver"].action(requiredNameIsJavaClassName);
210 applicationAction[u"receiver"][u"intent-filter"] = intentFilterAction;
211 applicationAction[u"receiver"][u"meta-data"] = metaDataAction;
212
213 // Provider actions.
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"];
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800218 return true;
219}
220
Adam Lesinski52364f72016-01-11 13:10:24 -0800221class FullyQualifiedClassNameVisitor : public xml::Visitor {
222public:
223 using xml::Visitor::visit;
224
225 FullyQualifiedClassNameVisitor(const StringPiece16& package) : mPackage(package) {
226 }
227
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());
233 }
234 }
235
236 // Super implementation to iterate over the children.
237 xml::Visitor::visit(el);
238 }
239
240private:
241 StringPiece16 mPackage;
242};
243
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700244static bool renameManifestPackage(const StringPiece16& packageOverride, xml::Element* manifestEl) {
Adam Lesinski52364f72016-01-11 13:10:24 -0800245 xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
246
247 // We've already verified that the manifest element is present, with a package name specified.
248 assert(attr);
249
250 std::u16string originalPackage = std::move(attr->value);
251 attr->value = packageOverride.toString();
252
253 FullyQualifiedClassNameVisitor visitor(originalPackage);
254 manifestEl->accept(&visitor);
255 return true;
256}
257
Adam Lesinski467f1712015-11-16 17:35:44 -0800258bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800259 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>");
263 return false;
264 }
265
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700266 if ((mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)
267 && root->findChild({}, u"uses-sdk") == nullptr) {
268 // Auto insert a <uses-sdk> element.
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800269 std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
270 usesSdk->name = u"uses-sdk";
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800271 root->addChild(std::move(usesSdk));
272 }
273
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700274 xml::XmlActionExecutor executor;
275 if (!buildRules(&executor, context->getDiagnostics())) {
276 return false;
277 }
278
279 if (!executor.execute(xml::XmlActionExecutorPolicy::Whitelist, context->getDiagnostics(),
280 doc)) {
281 return false;
282 }
283
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)) {
288 return false;
289 }
290 }
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800291 return true;
292}
293
294} // namespace aapt