blob: 3779638590be0312dea859652b56d607ecf05d34 [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 Lesinskicc5609d2016-04-05 12:41:07 -0700139 // Uses-sdk actions.
140 manifestAction[u"uses-sdk"].action([&](xml::Element* el) -> bool {
141 if (mOptions.minSdkVersionDefault &&
142 el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion") == nullptr) {
143 // There was no minSdkVersion defined and we have a default to assign.
144 el->attributes.push_back(xml::Attribute{
145 xml::kSchemaAndroid, u"minSdkVersion",
146 mOptions.minSdkVersionDefault.value() });
147 }
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800148
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700149 if (mOptions.targetSdkVersionDefault &&
150 el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion") == nullptr) {
151 // There was no targetSdkVersion defined and we have a default to assign.
152 el->attributes.push_back(xml::Attribute{
153 xml::kSchemaAndroid, u"targetSdkVersion",
154 mOptions.targetSdkVersionDefault.value() });
155 }
156 return true;
157 });
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800158
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700159 // Instrumentation actions.
160 manifestAction[u"instrumentation"].action([&](xml::Element* el) -> bool {
161 if (!mOptions.renameInstrumentationTargetPackage) {
162 return true;
163 }
164
165 if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"targetPackage")) {
166 attr->value = mOptions.renameInstrumentationTargetPackage.value();
167 }
168 return true;
169 });
170
Adam Lesinskic728c3d2016-04-06 17:40:25 -0700171 manifestAction[u"eat-comment"];
172 manifestAction[u"protected-broadcast"];
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700173 manifestAction[u"uses-permission"];
174 manifestAction[u"permission"];
175 manifestAction[u"permission-tree"];
176 manifestAction[u"permission-group"];
177
178 manifestAction[u"uses-configuration"];
179 manifestAction[u"uses-feature"];
180 manifestAction[u"uses-library"];
181 manifestAction[u"supports-screens"];
182 manifestAction[u"compatible-screens"];
183 manifestAction[u"supports-gl-texture"];
184
185 // Application actions.
186 xml::XmlNodeAction& applicationAction = (*executor)[u"manifest"][u"application"];
187 applicationAction.action(optionalNameIsJavaClassName);
188
189 // Activity actions.
190 applicationAction[u"activity"].action(requiredNameIsJavaClassName);
191 applicationAction[u"activity"][u"intent-filter"] = intentFilterAction;
192 applicationAction[u"activity"][u"meta-data"] = metaDataAction;
193
194 // Activity alias actions.
195 applicationAction[u"activity-alias"][u"intent-filter"] = intentFilterAction;
196 applicationAction[u"activity-alias"][u"meta-data"] = metaDataAction;
197
198 // Service actions.
199 applicationAction[u"service"].action(requiredNameIsJavaClassName);
200 applicationAction[u"service"][u"intent-filter"] = intentFilterAction;
201 applicationAction[u"service"][u"meta-data"] = metaDataAction;
202
203 // Receiver actions.
204 applicationAction[u"receiver"].action(requiredNameIsJavaClassName);
205 applicationAction[u"receiver"][u"intent-filter"] = intentFilterAction;
206 applicationAction[u"receiver"][u"meta-data"] = metaDataAction;
207
208 // Provider actions.
209 applicationAction[u"provider"].action(requiredNameIsJavaClassName);
210 applicationAction[u"provider"][u"grant-uri-permissions"];
211 applicationAction[u"provider"][u"meta-data"] = metaDataAction;
212 applicationAction[u"provider"][u"path-permissions"];
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800213 return true;
214}
215
Adam Lesinski52364f72016-01-11 13:10:24 -0800216class FullyQualifiedClassNameVisitor : public xml::Visitor {
217public:
218 using xml::Visitor::visit;
219
220 FullyQualifiedClassNameVisitor(const StringPiece16& package) : mPackage(package) {
221 }
222
223 void visit(xml::Element* el) override {
224 for (xml::Attribute& attr : el->attributes) {
225 if (Maybe<std::u16string> newValue =
226 util::getFullyQualifiedClassName(mPackage, attr.value)) {
227 attr.value = std::move(newValue.value());
228 }
229 }
230
231 // Super implementation to iterate over the children.
232 xml::Visitor::visit(el);
233 }
234
235private:
236 StringPiece16 mPackage;
237};
238
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700239static bool renameManifestPackage(const StringPiece16& packageOverride, xml::Element* manifestEl) {
Adam Lesinski52364f72016-01-11 13:10:24 -0800240 xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
241
242 // We've already verified that the manifest element is present, with a package name specified.
243 assert(attr);
244
245 std::u16string originalPackage = std::move(attr->value);
246 attr->value = packageOverride.toString();
247
248 FullyQualifiedClassNameVisitor visitor(originalPackage);
249 manifestEl->accept(&visitor);
250 return true;
251}
252
Adam Lesinski467f1712015-11-16 17:35:44 -0800253bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800254 xml::Element* root = xml::findRootElement(doc->root.get());
255 if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
256 context->getDiagnostics()->error(DiagMessage(doc->file.source)
257 << "root tag must be <manifest>");
258 return false;
259 }
260
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700261 if ((mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)
262 && root->findChild({}, u"uses-sdk") == nullptr) {
263 // Auto insert a <uses-sdk> element.
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800264 std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
265 usesSdk->name = u"uses-sdk";
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800266 root->addChild(std::move(usesSdk));
267 }
268
Adam Lesinskicc5609d2016-04-05 12:41:07 -0700269 xml::XmlActionExecutor executor;
270 if (!buildRules(&executor, context->getDiagnostics())) {
271 return false;
272 }
273
274 if (!executor.execute(xml::XmlActionExecutorPolicy::Whitelist, context->getDiagnostics(),
275 doc)) {
276 return false;
277 }
278
279 if (mOptions.renameManifestPackage) {
280 // Rename manifest package outside of the XmlActionExecutor.
281 // We need to extract the old package name and FullyQualify all class names.
282 if (!renameManifestPackage(mOptions.renameManifestPackage.value(), root)) {
283 return false;
284 }
285 }
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800286 return true;
287}
288
289} // namespace aapt