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