• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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