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
28using android::StringPiece;
29
30namespace aapt {
31
32// This is how PackageManager builds class names from AndroidManifest.xml entries.
33static 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
54static 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
61static 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
70static 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
79static 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
90static 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
120static 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.
150static 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).
163static 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
192bool 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
342class FullyQualifiedClassNameVisitor : public xml::Visitor {
343 public:
344  using xml::Visitor::Visit;
345
346  explicit FullyQualifiedClassNameVisitor(const StringPiece& package) : package_(package) {}
347
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
367static 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
382bool 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