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 "link/ManifestFixer.h"
18
19 #include <unordered_set>
20
21 #include "android-base/logging.h"
22
23 #include "ResourceUtils.h"
24 #include "util/Util.h"
25 #include "xml/XmlActionExecutor.h"
26 #include "xml/XmlDom.h"
27
28 using android::StringPiece;
29
30 namespace aapt {
31
RequiredNameIsNotEmpty(xml::Element * el,SourcePathDiagnostics * diag)32 static bool RequiredNameIsNotEmpty(xml::Element* el, SourcePathDiagnostics* diag) {
33 xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name");
34 if (attr == nullptr) {
35 diag->Error(DiagMessage(el->line_number)
36 << "<" << el->name << "> is missing attribute 'android:name'");
37 return false;
38 }
39
40 if (attr->value.empty()) {
41 diag->Error(DiagMessage(el->line_number)
42 << "attribute 'android:name' in <" << el->name << "> tag must not be empty");
43 return false;
44 }
45 return true;
46 }
47
48 // This is how PackageManager builds class names from AndroidManifest.xml entries.
NameIsJavaClassName(xml::Element * el,xml::Attribute * attr,SourcePathDiagnostics * diag)49 static bool NameIsJavaClassName(xml::Element* el, xml::Attribute* attr,
50 SourcePathDiagnostics* diag) {
51 // We allow unqualified class names (ie: .HelloActivity)
52 // Since we don't know the package name, we can just make a fake one here and
53 // the test will be identical as long as the real package name is valid too.
54 Maybe<std::string> fully_qualified_class_name =
55 util::GetFullyQualifiedClassName("a", attr->value);
56
57 StringPiece qualified_class_name = fully_qualified_class_name
58 ? fully_qualified_class_name.value()
59 : attr->value;
60
61 if (!util::IsJavaClassName(qualified_class_name)) {
62 diag->Error(DiagMessage(el->line_number)
63 << "attribute 'android:name' in <" << el->name
64 << "> tag must be a valid Java class name");
65 return false;
66 }
67 return true;
68 }
69
OptionalNameIsJavaClassName(xml::Element * el,SourcePathDiagnostics * diag)70 static bool OptionalNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
71 if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name")) {
72 return NameIsJavaClassName(el, attr, diag);
73 }
74 return true;
75 }
76
RequiredNameIsJavaClassName(xml::Element * el,SourcePathDiagnostics * diag)77 static bool RequiredNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
78 xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name");
79 if (attr == nullptr) {
80 diag->Error(DiagMessage(el->line_number)
81 << "<" << el->name << "> is missing attribute 'android:name'");
82 return false;
83 }
84 return NameIsJavaClassName(el, attr, diag);
85 }
86
RequiredNameIsJavaPackage(xml::Element * el,SourcePathDiagnostics * diag)87 static bool RequiredNameIsJavaPackage(xml::Element* el, SourcePathDiagnostics* diag) {
88 xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name");
89 if (attr == nullptr) {
90 diag->Error(DiagMessage(el->line_number)
91 << "<" << el->name << "> is missing attribute 'android:name'");
92 return false;
93 }
94
95 if (!util::IsJavaPackageName(attr->value)) {
96 diag->Error(DiagMessage(el->line_number) << "attribute 'android:name' in <" << el->name
97 << "> tag must be a valid Java package name");
98 return false;
99 }
100 return true;
101 }
102
RequiredAndroidAttribute(const std::string & attr)103 static xml::XmlNodeAction::ActionFuncWithDiag RequiredAndroidAttribute(const std::string& attr) {
104 return [=](xml::Element* el, SourcePathDiagnostics* diag) -> bool {
105 if (el->FindAttribute(xml::kSchemaAndroid, attr) == nullptr) {
106 diag->Error(DiagMessage(el->line_number)
107 << "<" << el->name << "> is missing required attribute 'android:" << attr << "'");
108 return false;
109 }
110 return true;
111 };
112 }
113
AutoGenerateIsFeatureSplit(xml::Element * el,SourcePathDiagnostics * diag)114 static bool AutoGenerateIsFeatureSplit(xml::Element* el, SourcePathDiagnostics* diag) {
115 constexpr const char* kFeatureSplit = "featureSplit";
116 constexpr const char* kIsFeatureSplit = "isFeatureSplit";
117
118 xml::Attribute* attr = el->FindAttribute({}, kFeatureSplit);
119 if (attr != nullptr) {
120 // Rewrite the featureSplit attribute to be "split". This is what the
121 // platform recognizes.
122 attr->name = "split";
123
124 // Now inject the android:isFeatureSplit="true" attribute.
125 xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, kIsFeatureSplit);
126 if (attr != nullptr) {
127 if (!ResourceUtils::ParseBool(attr->value).value_or_default(false)) {
128 // The isFeatureSplit attribute is false, which conflicts with the use
129 // of "featureSplit".
130 diag->Error(DiagMessage(el->line_number)
131 << "attribute 'featureSplit' used in <manifest> but 'android:isFeatureSplit' "
132 "is not 'true'");
133 return false;
134 }
135
136 // The attribute is already there and set to true, nothing to do.
137 } else {
138 el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, kIsFeatureSplit, "true"});
139 }
140 }
141 return true;
142 }
143
VerifyManifest(xml::Element * el,SourcePathDiagnostics * diag)144 static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
145 xml::Attribute* attr = el->FindAttribute({}, "package");
146 if (!attr) {
147 diag->Error(DiagMessage(el->line_number)
148 << "<manifest> tag is missing 'package' attribute");
149 return false;
150 } else if (ResourceUtils::IsReference(attr->value)) {
151 diag->Error(DiagMessage(el->line_number)
152 << "attribute 'package' in <manifest> tag must not be a reference");
153 return false;
154 } else if (!util::IsAndroidPackageName(attr->value)) {
155 diag->Error(DiagMessage(el->line_number)
156 << "attribute 'package' in <manifest> tag is not a valid Android package name: '"
157 << attr->value << "'");
158 return false;
159 }
160
161 attr = el->FindAttribute({}, "split");
162 if (attr) {
163 if (!util::IsJavaPackageName(attr->value)) {
164 diag->Error(DiagMessage(el->line_number) << "attribute 'split' in <manifest> tag is not a "
165 "valid split name");
166 return false;
167 }
168 }
169 return true;
170 }
171
172 // The coreApp attribute in <manifest> is not a regular AAPT attribute, so type
173 // checking on it is manual.
FixCoreAppAttribute(xml::Element * el,SourcePathDiagnostics * diag)174 static bool FixCoreAppAttribute(xml::Element* el, SourcePathDiagnostics* diag) {
175 if (xml::Attribute* attr = el->FindAttribute("", "coreApp")) {
176 std::unique_ptr<BinaryPrimitive> result = ResourceUtils::TryParseBool(attr->value);
177 if (!result) {
178 diag->Error(DiagMessage(el->line_number) << "attribute coreApp must be a boolean");
179 return false;
180 }
181 attr->compiled_value = std::move(result);
182 }
183 return true;
184 }
185
186 // Checks that <uses-feature> has android:glEsVersion or android:name, not both (or neither).
VerifyUsesFeature(xml::Element * el,SourcePathDiagnostics * diag)187 static bool VerifyUsesFeature(xml::Element* el, SourcePathDiagnostics* diag) {
188 bool has_name = false;
189 if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name")) {
190 if (attr->value.empty()) {
191 diag->Error(DiagMessage(el->line_number)
192 << "android:name in <uses-feature> must not be empty");
193 return false;
194 }
195 has_name = true;
196 }
197
198 bool has_gl_es_version = false;
199 if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "glEsVersion")) {
200 if (has_name) {
201 diag->Error(DiagMessage(el->line_number)
202 << "cannot define both android:name and android:glEsVersion in <uses-feature>");
203 return false;
204 }
205 has_gl_es_version = true;
206 }
207
208 if (!has_name && !has_gl_es_version) {
209 diag->Error(DiagMessage(el->line_number)
210 << "<uses-feature> must have either android:name or android:glEsVersion attribute");
211 return false;
212 }
213 return true;
214 }
215
BuildRules(xml::XmlActionExecutor * executor,IDiagnostics * diag)216 bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
217 IDiagnostics* diag) {
218 // First verify some options.
219 if (options_.rename_manifest_package) {
220 if (!util::IsJavaPackageName(options_.rename_manifest_package.value())) {
221 diag->Error(DiagMessage() << "invalid manifest package override '"
222 << options_.rename_manifest_package.value()
223 << "'");
224 return false;
225 }
226 }
227
228 if (options_.rename_instrumentation_target_package) {
229 if (!util::IsJavaPackageName(options_.rename_instrumentation_target_package.value())) {
230 diag->Error(DiagMessage()
231 << "invalid instrumentation target package override '"
232 << options_.rename_instrumentation_target_package.value()
233 << "'");
234 return false;
235 }
236 }
237
238 // Common <intent-filter> actions.
239 xml::XmlNodeAction intent_filter_action;
240 intent_filter_action["action"].Action(RequiredNameIsNotEmpty);
241 intent_filter_action["category"].Action(RequiredNameIsNotEmpty);
242 intent_filter_action["data"];
243
244 // Common <meta-data> actions.
245 xml::XmlNodeAction meta_data_action;
246
247 // Common <uses-feature> actions.
248 xml::XmlNodeAction uses_feature_action;
249 uses_feature_action.Action(VerifyUsesFeature);
250
251 // Common component actions.
252 xml::XmlNodeAction component_action;
253 component_action.Action(RequiredNameIsJavaClassName);
254 component_action["intent-filter"] = intent_filter_action;
255 component_action["meta-data"] = meta_data_action;
256
257 // Manifest actions.
258 xml::XmlNodeAction& manifest_action = (*executor)["manifest"];
259 manifest_action.Action(AutoGenerateIsFeatureSplit);
260 manifest_action.Action(VerifyManifest);
261 manifest_action.Action(FixCoreAppAttribute);
262 manifest_action.Action([&](xml::Element* el) -> bool {
263 if (options_.version_name_default) {
264 if (el->FindAttribute(xml::kSchemaAndroid, "versionName") == nullptr) {
265 el->attributes.push_back(
266 xml::Attribute{xml::kSchemaAndroid, "versionName",
267 options_.version_name_default.value()});
268 }
269 }
270
271 if (options_.version_code_default) {
272 if (el->FindAttribute(xml::kSchemaAndroid, "versionCode") == nullptr) {
273 el->attributes.push_back(
274 xml::Attribute{xml::kSchemaAndroid, "versionCode",
275 options_.version_code_default.value()});
276 }
277 }
278
279 if (el->FindAttribute("", "platformBuildVersionCode") == nullptr) {
280 auto versionCode = el->FindAttribute(xml::kSchemaAndroid, "versionCode");
281 if (versionCode != nullptr) {
282 el->attributes.push_back(xml::Attribute{"", "platformBuildVersionCode",
283 versionCode->value});
284 }
285 }
286
287 if (el->FindAttribute("", "platformBuildVersionName") == nullptr) {
288 auto versionName = el->FindAttribute(xml::kSchemaAndroid, "versionName");
289 if (versionName != nullptr) {
290 el->attributes.push_back(xml::Attribute{"", "platformBuildVersionName",
291 versionName->value});
292 }
293 }
294
295 return true;
296 });
297
298 // Meta tags.
299 manifest_action["eat-comment"];
300
301 // Uses-sdk actions.
302 manifest_action["uses-sdk"].Action([&](xml::Element* el) -> bool {
303 if (options_.min_sdk_version_default &&
304 el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion") == nullptr) {
305 // There was no minSdkVersion defined and we have a default to assign.
306 el->attributes.push_back(
307 xml::Attribute{xml::kSchemaAndroid, "minSdkVersion",
308 options_.min_sdk_version_default.value()});
309 }
310
311 if (options_.target_sdk_version_default &&
312 el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion") == nullptr) {
313 // There was no targetSdkVersion defined and we have a default to assign.
314 el->attributes.push_back(
315 xml::Attribute{xml::kSchemaAndroid, "targetSdkVersion",
316 options_.target_sdk_version_default.value()});
317 }
318 return true;
319 });
320
321 // Instrumentation actions.
322 manifest_action["instrumentation"].Action(RequiredNameIsJavaClassName);
323 manifest_action["instrumentation"].Action([&](xml::Element* el) -> bool {
324 if (!options_.rename_instrumentation_target_package) {
325 return true;
326 }
327
328 if (xml::Attribute* attr =
329 el->FindAttribute(xml::kSchemaAndroid, "targetPackage")) {
330 attr->value = options_.rename_instrumentation_target_package.value();
331 }
332 return true;
333 });
334 manifest_action["instrumentation"]["meta-data"] = meta_data_action;
335
336 manifest_action["original-package"];
337 manifest_action["overlay"];
338 manifest_action["protected-broadcast"];
339 manifest_action["adopt-permissions"];
340 manifest_action["uses-permission"];
341 manifest_action["uses-permission-sdk-23"];
342 manifest_action["permission"];
343 manifest_action["permission-tree"];
344 manifest_action["permission-group"];
345 manifest_action["uses-configuration"];
346 manifest_action["supports-screens"];
347 manifest_action["uses-feature"] = uses_feature_action;
348 manifest_action["feature-group"]["uses-feature"] = uses_feature_action;
349 manifest_action["compatible-screens"];
350 manifest_action["compatible-screens"]["screen"];
351 manifest_action["supports-gl-texture"];
352 manifest_action["meta-data"] = meta_data_action;
353 manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);
354
355 manifest_action["key-sets"]["key-set"]["public-key"];
356 manifest_action["key-sets"]["upgrade-key-set"];
357
358 // Application actions.
359 xml::XmlNodeAction& application_action = manifest_action["application"];
360 application_action.Action(OptionalNameIsJavaClassName);
361
362 application_action["uses-library"].Action(RequiredNameIsNotEmpty);
363 application_action["library"].Action(RequiredNameIsNotEmpty);
364
365 xml::XmlNodeAction& static_library_action = application_action["static-library"];
366 static_library_action.Action(RequiredNameIsJavaPackage);
367 static_library_action.Action(RequiredAndroidAttribute("version"));
368
369 xml::XmlNodeAction& uses_static_library_action = application_action["uses-static-library"];
370 uses_static_library_action.Action(RequiredNameIsJavaPackage);
371 uses_static_library_action.Action(RequiredAndroidAttribute("version"));
372 uses_static_library_action.Action(RequiredAndroidAttribute("certDigest"));
373
374 if (options_.debug_mode) {
375 application_action.Action([&](xml::Element* el) -> bool {
376 xml::Attribute *attr = el->FindOrCreateAttribute(xml::kSchemaAndroid, "debuggable");
377 attr->value = "true";
378 return true;
379 });
380 }
381
382 application_action["meta-data"] = meta_data_action;
383
384 application_action["activity"] = component_action;
385 application_action["activity"]["layout"];
386
387 application_action["activity-alias"] = component_action;
388 application_action["service"] = component_action;
389 application_action["receiver"] = component_action;
390
391 // Provider actions.
392 application_action["provider"] = component_action;
393 application_action["provider"]["grant-uri-permission"];
394 application_action["provider"]["path-permission"];
395
396 return true;
397 }
398
FullyQualifyClassName(const StringPiece & package,const StringPiece & attr_ns,const StringPiece & attr_name,xml::Element * el)399 static void FullyQualifyClassName(const StringPiece& package, const StringPiece& attr_ns,
400 const StringPiece& attr_name, xml::Element* el) {
401 xml::Attribute* attr = el->FindAttribute(attr_ns, attr_name);
402 if (attr != nullptr) {
403 if (Maybe<std::string> new_value = util::GetFullyQualifiedClassName(package, attr->value)) {
404 attr->value = std::move(new_value.value());
405 }
406 }
407 }
408
RenameManifestPackage(const StringPiece & package_override,xml::Element * manifest_el)409 static bool RenameManifestPackage(const StringPiece& package_override, xml::Element* manifest_el) {
410 xml::Attribute* attr = manifest_el->FindAttribute({}, "package");
411
412 // We've already verified that the manifest element is present, with a package
413 // name specified.
414 CHECK(attr != nullptr);
415
416 std::string original_package = std::move(attr->value);
417 attr->value = package_override.to_string();
418
419 xml::Element* application_el = manifest_el->FindChild({}, "application");
420 if (application_el != nullptr) {
421 FullyQualifyClassName(original_package, xml::kSchemaAndroid, "name", application_el);
422 FullyQualifyClassName(original_package, xml::kSchemaAndroid, "backupAgent", application_el);
423
424 for (xml::Element* child_el : application_el->GetChildElements()) {
425 if (child_el->namespace_uri.empty()) {
426 if (child_el->name == "activity" || child_el->name == "activity-alias" ||
427 child_el->name == "provider" || child_el->name == "receiver" ||
428 child_el->name == "service") {
429 FullyQualifyClassName(original_package, xml::kSchemaAndroid, "name", child_el);
430 }
431
432 if (child_el->name == "activity-alias") {
433 FullyQualifyClassName(original_package, xml::kSchemaAndroid, "targetActivity", child_el);
434 }
435 }
436 }
437 }
438 return true;
439 }
440
Consume(IAaptContext * context,xml::XmlResource * doc)441 bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) {
442 xml::Element* root = xml::FindRootElement(doc->root.get());
443 if (!root || !root->namespace_uri.empty() || root->name != "manifest") {
444 context->GetDiagnostics()->Error(DiagMessage(doc->file.source)
445 << "root tag must be <manifest>");
446 return false;
447 }
448
449 if ((options_.min_sdk_version_default || options_.target_sdk_version_default) &&
450 root->FindChild({}, "uses-sdk") == nullptr) {
451 // Auto insert a <uses-sdk> element. This must be inserted before the
452 // <application> tag. The device runtime PackageParser will make SDK version
453 // decisions while parsing <application>.
454 std::unique_ptr<xml::Element> uses_sdk = util::make_unique<xml::Element>();
455 uses_sdk->name = "uses-sdk";
456 root->InsertChild(0, std::move(uses_sdk));
457 }
458
459 if (options_.compile_sdk_version) {
460 xml::Attribute* attr = root->FindOrCreateAttribute(xml::kSchemaAndroid, "compileSdkVersion");
461
462 // Make sure we un-compile the value if it was set to something else.
463 attr->compiled_value = {};
464
465 attr->value = options_.compile_sdk_version.value();
466 }
467
468 if (options_.compile_sdk_version_codename) {
469 xml::Attribute* attr =
470 root->FindOrCreateAttribute(xml::kSchemaAndroid, "compileSdkVersionCodename");
471
472 // Make sure we un-compile the value if it was set to something else.
473 attr->compiled_value = {};
474
475 attr->value = options_.compile_sdk_version_codename.value();
476 }
477
478 xml::XmlActionExecutor executor;
479 if (!BuildRules(&executor, context->GetDiagnostics())) {
480 return false;
481 }
482
483 xml::XmlActionExecutorPolicy policy = options_.warn_validation
484 ? xml::XmlActionExecutorPolicy::kWhitelistWarning
485 : xml::XmlActionExecutorPolicy::kWhitelist;
486 if (!executor.Execute(policy, context->GetDiagnostics(), doc)) {
487 return false;
488 }
489
490 if (options_.rename_manifest_package) {
491 // Rename manifest package outside of the XmlActionExecutor.
492 // We need to extract the old package name and FullyQualify all class
493 // names.
494 if (!RenameManifestPackage(options_.rename_manifest_package.value(), root)) {
495 return false;
496 }
497 }
498 return true;
499 }
500
501 } // namespace aapt
502