1 /*
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"
18 #include "link/ManifestFixer.h"
19 #include "util/Util.h"
20 #include "xml/XmlActionExecutor.h"
21 #include "xml/XmlDom.h"
22
23 namespace aapt {
24
25 /**
26 * This is how PackageManager builds class names from AndroidManifest.xml entries.
27 */
nameIsJavaClassName(xml::Element * el,xml::Attribute * attr,SourcePathDiagnostics * diag)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.
33 className = u".";
34 className += attr->value;
35 }
36
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
optionalNameIsJavaClassName(xml::Element * el,SourcePathDiagnostics * diag)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);
57 }
58 return true;
59 }
60
requiredNameIsJavaClassName(xml::Element * el,SourcePathDiagnostics * 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);
64 }
65 diag->error(DiagMessage(el->lineNumber)
66 << "<" << el->name << "> is missing attribute 'android:name'");
67 return false;
68 }
69
verifyManifest(xml::Element * el,SourcePathDiagnostics * diag)70 static 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;
84 }
85 return true;
86 }
87
buildRules(xml::XmlActionExecutor * executor,IDiagnostics * diag)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() << "'");
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 }
136 return true;
137 });
138
139 // Meta tags.
140 manifestAction[u"eat-comment"];
141
142 // 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 }
151
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() });
158 }
159 return true;
160 });
161
162 // 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
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"];
180
181 manifestAction[u"uses-configuration"];
182 manifestAction[u"uses-feature"];
183 manifestAction[u"uses-library"];
184 manifestAction[u"supports-screens"];
185 manifestAction[u"compatible-screens"];
186 manifestAction[u"supports-gl-texture"];
187
188 // Application actions.
189 xml::XmlNodeAction& applicationAction = (*executor)[u"manifest"][u"application"];
190 applicationAction.action(optionalNameIsJavaClassName);
191
192 // 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"];
216 return true;
217 }
218
219 class FullyQualifiedClassNameVisitor : public xml::Visitor {
220 public:
221 using xml::Visitor::visit;
222
FullyQualifiedClassNameVisitor(const StringPiece16 & package)223 FullyQualifiedClassNameVisitor(const StringPiece16& package) : mPackage(package) {
224 }
225
visit(xml::Element * el)226 void visit(xml::Element* el) override {
227 for (xml::Attribute& attr : el->attributes) {
228 if (Maybe<std::u16string> newValue =
229 util::getFullyQualifiedClassName(mPackage, attr.value)) {
230 attr.value = std::move(newValue.value());
231 }
232 }
233
234 // Super implementation to iterate over the children.
235 xml::Visitor::visit(el);
236 }
237
238 private:
239 StringPiece16 mPackage;
240 };
241
renameManifestPackage(const StringPiece16 & packageOverride,xml::Element * manifestEl)242 static bool renameManifestPackage(const StringPiece16& packageOverride, xml::Element* manifestEl) {
243 xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
244
245 // We've already verified that the manifest element is present, with a package name specified.
246 assert(attr);
247
248 std::u16string originalPackage = std::move(attr->value);
249 attr->value = packageOverride.toString();
250
251 FullyQualifiedClassNameVisitor visitor(originalPackage);
252 manifestEl->accept(&visitor);
253 return true;
254 }
255
consume(IAaptContext * context,xml::XmlResource * doc)256 bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
257 xml::Element* root = xml::findRootElement(doc->root.get());
258 if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
259 context->getDiagnostics()->error(DiagMessage(doc->file.source)
260 << "root tag must be <manifest>");
261 return false;
262 }
263
264 if ((mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)
265 && root->findChild({}, u"uses-sdk") == nullptr) {
266 // Auto insert a <uses-sdk> element.
267 std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
268 usesSdk->name = u"uses-sdk";
269 root->addChild(std::move(usesSdk));
270 }
271
272 xml::XmlActionExecutor executor;
273 if (!buildRules(&executor, context->getDiagnostics())) {
274 return false;
275 }
276
277 if (!executor.execute(xml::XmlActionExecutorPolicy::Whitelist, context->getDiagnostics(),
278 doc)) {
279 return false;
280 }
281
282 if (mOptions.renameManifestPackage) {
283 // Rename manifest package outside of the XmlActionExecutor.
284 // We need to extract the old package name and FullyQualify all class names.
285 if (!renameManifestPackage(mOptions.renameManifestPackage.value(), root)) {
286 return false;
287 }
288 }
289 return true;
290 }
291
292 } // namespace aapt
293