blob: db6e06dfa6c342cc2e4f10ed9f8018a0623cea1e [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
Adam Lesinski71965e82016-07-07 17:12:12 -070023#include <unordered_set>
24
Adam Lesinski2ae4a872015-11-02 16:10:55 -080025namespace aapt {
26
Adam Lesinskicc5609d2016-04-05 12:41:07 -070027/**
28 * This is how PackageManager builds class names from AndroidManifest.xml entries.
29 */
30static bool nameIsJavaClassName(xml::Element* el, xml::Attribute* attr,
31 SourcePathDiagnostics* diag) {
Adam Lesinskicc5609d2016-04-05 12:41:07 -070032 // We allow unqualified class names (ie: .HelloActivity)
33 // Since we don't know the package name, we can just make a fake one here and
34 // the test will be identical as long as the real package name is valid too.
35 Maybe<std::u16string> fullyQualifiedClassName =
Adam Lesinski71965e82016-07-07 17:12:12 -070036 util::getFullyQualifiedClassName(u"a", attr->value);
Adam Lesinskicc5609d2016-04-05 12:41:07 -070037
38 StringPiece16 qualifiedClassName = fullyQualifiedClassName
Adam Lesinski71965e82016-07-07 17:12:12 -070039 ? fullyQualifiedClassName.value() : attr->value;
Adam Lesinskicc5609d2016-04-05 12:41:07 -070040 if (!util::isJavaClassName(qualifiedClassName)) {
41 diag->error(DiagMessage(el->lineNumber)
42 << "attribute 'android:name' in <"
43 << el->name << "> tag must be a valid Java class name");
44 return false;
45 }
46 return true;
47}
48
49static bool optionalNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
50 if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) {
51 return nameIsJavaClassName(el, attr, diag);
52 }
53 return true;
54}
55
56static bool requiredNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
57 if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) {
58 return nameIsJavaClassName(el, attr, diag);
59 }
60 diag->error(DiagMessage(el->lineNumber)
61 << "<" << el->name << "> is missing attribute 'android:name'");
Adam Lesinski52364f72016-01-11 13:10:24 -080062 return false;
63}
64
Adam Lesinskicc5609d2016-04-05 12:41:07 -070065static bool verifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
66 xml::Attribute* attr = el->findAttribute({}, u"package");
67 if (!attr) {
68 diag->error(DiagMessage(el->lineNumber) << "<manifest> tag is missing 'package' attribute");
69 return false;
70 } else if (ResourceUtils::isReference(attr->value)) {
71 diag->error(DiagMessage(el->lineNumber)
72 << "attribute 'package' in <manifest> tag must not be a reference");
73 return false;
74 } else if (!util::isJavaPackageName(attr->value)) {
75 diag->error(DiagMessage(el->lineNumber)
76 << "attribute 'package' in <manifest> tag is not a valid Java package name: '"
77 << attr->value << "'");
78 return false;
Adam Lesinski2ae4a872015-11-02 16:10:55 -080079 }
Adam Lesinski52364f72016-01-11 13:10:24 -080080 return true;
81}
82
Adam Lesinskicc5609d2016-04-05 12:41:07 -070083bool ManifestFixer::buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag) {
84 // First verify some options.
85 if (mOptions.renameManifestPackage) {
86 if (!util::isJavaPackageName(mOptions.renameManifestPackage.value())) {
87 diag->error(DiagMessage() << "invalid manifest package override '"
88 << mOptions.renameManifestPackage.value() << "'");
89 return false;
90 }
91 }
92
93 if (mOptions.renameInstrumentationTargetPackage) {
94 if (!util::isJavaPackageName(mOptions.renameInstrumentationTargetPackage.value())) {
95 diag->error(DiagMessage() << "invalid instrumentation target package override '"
96 << mOptions.renameInstrumentationTargetPackage.value() << "'");
97 return false;
98 }
99 }
100
101 // Common intent-filter actions.
102 xml::XmlNodeAction intentFilterAction;
103 intentFilterAction[u"action"];
104 intentFilterAction[u"category"];
105 intentFilterAction[u"data"];
106
107 // Common meta-data actions.
108 xml::XmlNodeAction metaDataAction;
109
110 // Manifest actions.
111 xml::XmlNodeAction& manifestAction = (*executor)[u"manifest"];
112 manifestAction.action(verifyManifest);
113 manifestAction.action([&](xml::Element* el) -> bool {
114 if (mOptions.versionNameDefault) {
115 if (el->findAttribute(xml::kSchemaAndroid, u"versionName") == nullptr) {
116 el->attributes.push_back(xml::Attribute{
117 xml::kSchemaAndroid,
118 u"versionName",
119 mOptions.versionNameDefault.value() });
120 }
121 }
122
123 if (mOptions.versionCodeDefault) {
124 if (el->findAttribute(xml::kSchemaAndroid, u"versionCode") == nullptr) {
125 el->attributes.push_back(xml::Attribute{
126 xml::kSchemaAndroid,
127 u"versionCode",
128 mOptions.versionCodeDefault.value() });
129 }
130 }
Adam Lesinski52364f72016-01-11 13:10:24 -0800131 return true;
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700132 });
Adam Lesinski52364f72016-01-11 13:10:24 -0800133
Adam Lesinski5ff3ad62016-04-13 20:30:45 -0700134 // Meta tags.
135 manifestAction[u"eat-comment"];
136
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700137 // Uses-sdk actions.
138 manifestAction[u"uses-sdk"].action([&](xml::Element* el) -> bool {
139 if (mOptions.minSdkVersionDefault &&
140 el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion") == nullptr) {
141 // There was no minSdkVersion defined and we have a default to assign.
142 el->attributes.push_back(xml::Attribute{
143 xml::kSchemaAndroid, u"minSdkVersion",
144 mOptions.minSdkVersionDefault.value() });
145 }
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800146
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700147 if (mOptions.targetSdkVersionDefault &&
148 el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion") == nullptr) {
149 // There was no targetSdkVersion defined and we have a default to assign.
150 el->attributes.push_back(xml::Attribute{
151 xml::kSchemaAndroid, u"targetSdkVersion",
152 mOptions.targetSdkVersionDefault.value() });
153 }
154 return true;
155 });
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800156
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700157 // Instrumentation actions.
158 manifestAction[u"instrumentation"].action([&](xml::Element* el) -> bool {
159 if (!mOptions.renameInstrumentationTargetPackage) {
160 return true;
161 }
162
163 if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"targetPackage")) {
164 attr->value = mOptions.renameInstrumentationTargetPackage.value();
165 }
166 return true;
167 });
168
Adam Lesinski5ff3ad62016-04-13 20:30:45 -0700169 manifestAction[u"original-package"];
Adam Lesinskic728c3d2016-04-06 17:40:25 -0700170 manifestAction[u"protected-broadcast"];
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700171 manifestAction[u"uses-permission"];
172 manifestAction[u"permission"];
173 manifestAction[u"permission-tree"];
174 manifestAction[u"permission-group"];
175
176 manifestAction[u"uses-configuration"];
177 manifestAction[u"uses-feature"];
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700178 manifestAction[u"supports-screens"];
179 manifestAction[u"compatible-screens"];
180 manifestAction[u"supports-gl-texture"];
181
182 // Application actions.
183 xml::XmlNodeAction& applicationAction = (*executor)[u"manifest"][u"application"];
184 applicationAction.action(optionalNameIsJavaClassName);
185
Adam Lesinskifee32d42016-05-31 15:07:20 -0700186 // Uses library actions.
187 applicationAction[u"uses-library"];
188
Adam Lesinski5d84ad52016-06-23 13:18:16 -0700189 // Meta-data.
190 applicationAction[u"meta-data"] = metaDataAction;
191
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700192 // Activity actions.
193 applicationAction[u"activity"].action(requiredNameIsJavaClassName);
194 applicationAction[u"activity"][u"intent-filter"] = intentFilterAction;
195 applicationAction[u"activity"][u"meta-data"] = metaDataAction;
196
197 // Activity alias actions.
198 applicationAction[u"activity-alias"][u"intent-filter"] = intentFilterAction;
199 applicationAction[u"activity-alias"][u"meta-data"] = metaDataAction;
200
201 // Service actions.
202 applicationAction[u"service"].action(requiredNameIsJavaClassName);
203 applicationAction[u"service"][u"intent-filter"] = intentFilterAction;
204 applicationAction[u"service"][u"meta-data"] = metaDataAction;
205
206 // Receiver actions.
207 applicationAction[u"receiver"].action(requiredNameIsJavaClassName);
208 applicationAction[u"receiver"][u"intent-filter"] = intentFilterAction;
209 applicationAction[u"receiver"][u"meta-data"] = metaDataAction;
210
211 // Provider actions.
212 applicationAction[u"provider"].action(requiredNameIsJavaClassName);
213 applicationAction[u"provider"][u"grant-uri-permissions"];
214 applicationAction[u"provider"][u"meta-data"] = metaDataAction;
215 applicationAction[u"provider"][u"path-permissions"];
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800216 return true;
217}
218
Adam Lesinski52364f72016-01-11 13:10:24 -0800219class FullyQualifiedClassNameVisitor : public xml::Visitor {
220public:
221 using xml::Visitor::visit;
222
Chih-Hung Hsiehd53e3be2016-05-03 10:02:51 -0700223 explicit FullyQualifiedClassNameVisitor(const StringPiece16& package) : mPackage(package) {
Adam Lesinski52364f72016-01-11 13:10:24 -0800224 }
225
226 void visit(xml::Element* el) override {
227 for (xml::Attribute& attr : el->attributes) {
Adam Lesinski71965e82016-07-07 17:12:12 -0700228 if (attr.namespaceUri == xml::kSchemaAndroid
229 && mClassAttributes.find(attr.name) != mClassAttributes.end()) {
230 if (Maybe<std::u16string> newValue =
231 util::getFullyQualifiedClassName(mPackage, attr.value)) {
232 attr.value = std::move(newValue.value());
233 }
Adam Lesinski52364f72016-01-11 13:10:24 -0800234 }
235 }
236
237 // Super implementation to iterate over the children.
238 xml::Visitor::visit(el);
239 }
240
241private:
242 StringPiece16 mPackage;
Adam Lesinski71965e82016-07-07 17:12:12 -0700243 std::unordered_set<StringPiece16> mClassAttributes = { u"name" };
Adam Lesinski52364f72016-01-11 13:10:24 -0800244};
245
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700246static bool renameManifestPackage(const StringPiece16& packageOverride, xml::Element* manifestEl) {
Adam Lesinski52364f72016-01-11 13:10:24 -0800247 xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
248
249 // We've already verified that the manifest element is present, with a package name specified.
250 assert(attr);
251
252 std::u16string originalPackage = std::move(attr->value);
253 attr->value = packageOverride.toString();
254
255 FullyQualifiedClassNameVisitor visitor(originalPackage);
256 manifestEl->accept(&visitor);
257 return true;
258}
259
Adam Lesinski467f1712015-11-16 17:35:44 -0800260bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800261 xml::Element* root = xml::findRootElement(doc->root.get());
262 if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
263 context->getDiagnostics()->error(DiagMessage(doc->file.source)
264 << "root tag must be <manifest>");
265 return false;
266 }
267
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700268 if ((mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)
269 && root->findChild({}, u"uses-sdk") == nullptr) {
270 // Auto insert a <uses-sdk> element.
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800271 std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
272 usesSdk->name = u"uses-sdk";
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800273 root->addChild(std::move(usesSdk));
274 }
275
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700276 xml::XmlActionExecutor executor;
277 if (!buildRules(&executor, context->getDiagnostics())) {
278 return false;
279 }
280
281 if (!executor.execute(xml::XmlActionExecutorPolicy::Whitelist, context->getDiagnostics(),
282 doc)) {
283 return false;
284 }
285
286 if (mOptions.renameManifestPackage) {
287 // Rename manifest package outside of the XmlActionExecutor.
288 // We need to extract the old package name and FullyQualify all class names.
289 if (!renameManifestPackage(mOptions.renameManifestPackage.value(), root)) {
290 return false;
291 }
292 }
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800293 return true;
294}
295
296} // namespace aapt