/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "link/ManifestFixer.h" #include #include "android-base/logging.h" #include "ResourceUtils.h" #include "trace/TraceBuffer.h" #include "util/Util.h" #include "xml/XmlActionExecutor.h" #include "xml/XmlDom.h" using android::StringPiece; namespace aapt { static bool RequiredNameIsNotEmpty(xml::Element* el, SourcePathDiagnostics* diag) { xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name"); if (attr == nullptr) { diag->Error(DiagMessage(el->line_number) << "<" << el->name << "> is missing attribute 'android:name'"); return false; } if (attr->value.empty()) { diag->Error(DiagMessage(el->line_number) << "attribute 'android:name' in <" << el->name << "> tag must not be empty"); return false; } return true; } // This is how PackageManager builds class names from AndroidManifest.xml entries. static bool NameIsJavaClassName(xml::Element* el, xml::Attribute* attr, SourcePathDiagnostics* diag) { // We allow unqualified class names (ie: .HelloActivity) // Since we don't know the package name, we can just make a fake one here and // the test will be identical as long as the real package name is valid too. Maybe fully_qualified_class_name = util::GetFullyQualifiedClassName("a", attr->value); StringPiece qualified_class_name = fully_qualified_class_name ? fully_qualified_class_name.value() : attr->value; if (!util::IsJavaClassName(qualified_class_name)) { diag->Error(DiagMessage(el->line_number) << "attribute 'android:name' in <" << el->name << "> tag must be a valid Java class name"); return false; } return true; } static bool OptionalNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) { if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name")) { return NameIsJavaClassName(el, attr, diag); } return true; } static bool RequiredNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) { xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name"); if (attr == nullptr) { diag->Error(DiagMessage(el->line_number) << "<" << el->name << "> is missing attribute 'android:name'"); return false; } return NameIsJavaClassName(el, attr, diag); } static bool RequiredNameIsJavaPackage(xml::Element* el, SourcePathDiagnostics* diag) { xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name"); if (attr == nullptr) { diag->Error(DiagMessage(el->line_number) << "<" << el->name << "> is missing attribute 'android:name'"); return false; } if (!util::IsJavaPackageName(attr->value)) { diag->Error(DiagMessage(el->line_number) << "attribute 'android:name' in <" << el->name << "> tag must be a valid Java package name"); return false; } return true; } static xml::XmlNodeAction::ActionFuncWithDiag RequiredAndroidAttribute(const std::string& attr) { return [=](xml::Element* el, SourcePathDiagnostics* diag) -> bool { if (el->FindAttribute(xml::kSchemaAndroid, attr) == nullptr) { diag->Error(DiagMessage(el->line_number) << "<" << el->name << "> is missing required attribute 'android:" << attr << "'"); return false; } return true; }; } static xml::XmlNodeAction::ActionFuncWithDiag RequiredOneAndroidAttribute( const std::string& attrName1, const std::string& attrName2) { return [=](xml::Element* el, SourcePathDiagnostics* diag) -> bool { xml::Attribute* attr1 = el->FindAttribute(xml::kSchemaAndroid, attrName1); xml::Attribute* attr2 = el->FindAttribute(xml::kSchemaAndroid, attrName2); if (attr1 == nullptr && attr2 == nullptr) { diag->Error(DiagMessage(el->line_number) << "<" << el->name << "> is missing required attribute 'android:" << attrName1 << "' or 'android:" << attrName2 << "'"); return false; } if (attr1 != nullptr && attr2 != nullptr) { diag->Error(DiagMessage(el->line_number) << "<" << el->name << "> can only specify one of attribute 'android:" << attrName1 << "' or 'android:" << attrName2 << "'"); return false; } return true; }; } static bool AutoGenerateIsFeatureSplit(xml::Element* el, SourcePathDiagnostics* diag) { constexpr const char* kFeatureSplit = "featureSplit"; constexpr const char* kIsFeatureSplit = "isFeatureSplit"; xml::Attribute* attr = el->FindAttribute({}, kFeatureSplit); if (attr != nullptr) { // Rewrite the featureSplit attribute to be "split". This is what the // platform recognizes. attr->name = "split"; // Now inject the android:isFeatureSplit="true" attribute. xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, kIsFeatureSplit); if (attr != nullptr) { if (!ResourceUtils::ParseBool(attr->value).value_or_default(false)) { // The isFeatureSplit attribute is false, which conflicts with the use // of "featureSplit". diag->Error(DiagMessage(el->line_number) << "attribute 'featureSplit' used in but 'android:isFeatureSplit' " "is not 'true'"); return false; } // The attribute is already there and set to true, nothing to do. } else { el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, kIsFeatureSplit, "true"}); } } return true; } static bool VerifyManifest(xml::Element* el, xml::XmlActionExecutorPolicy policy, SourcePathDiagnostics* diag) { xml::Attribute* attr = el->FindAttribute({}, "package"); if (!attr) { diag->Error(DiagMessage(el->line_number) << " tag is missing 'package' attribute"); return false; } else if (ResourceUtils::IsReference(attr->value)) { diag->Error(DiagMessage(el->line_number) << "attribute 'package' in tag must not be a reference"); return false; } else if (!util::IsAndroidPackageName(attr->value)) { DiagMessage error_msg(el->line_number); error_msg << "attribute 'package' in tag is not a valid Android package name: '" << attr->value << "'"; if (policy == xml::XmlActionExecutorPolicy::kAllowListWarning) { // Treat the error only as a warning. diag->Warn(error_msg); } else { diag->Error(error_msg); return false; } } attr = el->FindAttribute({}, "split"); if (attr) { if (!util::IsJavaPackageName(attr->value)) { diag->Error(DiagMessage(el->line_number) << "attribute 'split' in tag is not a " "valid split name"); return false; } } return true; } // The coreApp attribute in is not a regular AAPT attribute, so type // checking on it is manual. static bool FixCoreAppAttribute(xml::Element* el, SourcePathDiagnostics* diag) { if (xml::Attribute* attr = el->FindAttribute("", "coreApp")) { std::unique_ptr result = ResourceUtils::TryParseBool(attr->value); if (!result) { diag->Error(DiagMessage(el->line_number) << "attribute coreApp must be a boolean"); return false; } attr->compiled_value = std::move(result); } return true; } // Checks that has android:glEsVersion or android:name, not both (or neither). static bool VerifyUsesFeature(xml::Element* el, SourcePathDiagnostics* diag) { bool has_name = false; if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name")) { if (attr->value.empty()) { diag->Error(DiagMessage(el->line_number) << "android:name in must not be empty"); return false; } has_name = true; } bool has_gl_es_version = false; if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "glEsVersion")) { if (has_name) { diag->Error(DiagMessage(el->line_number) << "cannot define both android:name and android:glEsVersion in "); return false; } has_gl_es_version = true; } if (!has_name && !has_gl_es_version) { diag->Error(DiagMessage(el->line_number) << " must have either android:name or android:glEsVersion attribute"); return false; } return true; } // Ensure that 'ns_decls' contains a declaration for 'uri', using 'prefix' as // the xmlns prefix if possible. static void EnsureNamespaceIsDeclared(const std::string& prefix, const std::string& uri, std::vector* ns_decls) { if (std::find_if(ns_decls->begin(), ns_decls->end(), [&](const xml::NamespaceDecl& ns_decl) { return ns_decl.uri == uri; }) != ns_decls->end()) { return; } std::set used_prefixes; for (const auto& ns_decl : *ns_decls) { used_prefixes.insert(ns_decl.prefix); } // Make multiple attempts in the unlikely event that 'prefix' is already taken. std::string disambiguator; for (int i = 0; i < used_prefixes.size() + 1; i++) { std::string attempted_prefix = prefix + disambiguator; if (used_prefixes.find(attempted_prefix) == used_prefixes.end()) { ns_decls->push_back(xml::NamespaceDecl{attempted_prefix, uri}); return; } disambiguator = std::to_string(i); } } bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag) { // First verify some options. if (options_.rename_manifest_package) { if (!util::IsJavaPackageName(options_.rename_manifest_package.value())) { diag->Error(DiagMessage() << "invalid manifest package override '" << options_.rename_manifest_package.value() << "'"); return false; } } if (options_.rename_instrumentation_target_package) { if (!util::IsJavaPackageName(options_.rename_instrumentation_target_package.value())) { diag->Error(DiagMessage() << "invalid instrumentation target package override '" << options_.rename_instrumentation_target_package.value() << "'"); return false; } } if (options_.rename_overlay_target_package) { if (!util::IsJavaPackageName(options_.rename_overlay_target_package.value())) { diag->Error(DiagMessage() << "invalid overlay target package override '" << options_.rename_overlay_target_package.value() << "'"); return false; } } // Common actions. xml::XmlNodeAction intent_filter_action; intent_filter_action["action"].Action(RequiredNameIsNotEmpty); intent_filter_action["category"].Action(RequiredNameIsNotEmpty); intent_filter_action["data"]; // Common actions. xml::XmlNodeAction meta_data_action; // Common actions. xml::XmlNodeAction property_action; property_action.Action(RequiredOneAndroidAttribute("resource", "value")); // Common actions. xml::XmlNodeAction uses_feature_action; uses_feature_action.Action(VerifyUsesFeature); // Common component actions. xml::XmlNodeAction component_action; component_action.Action(RequiredNameIsJavaClassName); component_action["intent-filter"] = intent_filter_action; component_action["preferred"] = intent_filter_action; component_action["meta-data"] = meta_data_action; component_action["property"] = property_action; // Manifest actions. xml::XmlNodeAction& manifest_action = (*executor)["manifest"]; manifest_action.Action(AutoGenerateIsFeatureSplit); manifest_action.Action(VerifyManifest); manifest_action.Action(FixCoreAppAttribute); manifest_action.Action([&](xml::Element* el) -> bool { EnsureNamespaceIsDeclared("android", xml::kSchemaAndroid, &el->namespace_decls); if (options_.version_name_default) { if (options_.replace_version) { el->RemoveAttribute(xml::kSchemaAndroid, "versionName"); } if (el->FindAttribute(xml::kSchemaAndroid, "versionName") == nullptr) { el->attributes.push_back( xml::Attribute{xml::kSchemaAndroid, "versionName", options_.version_name_default.value()}); } } if (options_.version_code_default) { if (options_.replace_version) { el->RemoveAttribute(xml::kSchemaAndroid, "versionCode"); } if (el->FindAttribute(xml::kSchemaAndroid, "versionCode") == nullptr) { el->attributes.push_back( xml::Attribute{xml::kSchemaAndroid, "versionCode", options_.version_code_default.value()}); } } if (options_.version_code_major_default) { if (options_.replace_version) { el->RemoveAttribute(xml::kSchemaAndroid, "versionCodeMajor"); } if (el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor") == nullptr) { el->attributes.push_back( xml::Attribute{xml::kSchemaAndroid, "versionCodeMajor", options_.version_code_major_default.value()}); } } if (options_.revision_code_default) { if (options_.replace_version) { el->RemoveAttribute(xml::kSchemaAndroid, "revisionCode"); } if (el->FindAttribute(xml::kSchemaAndroid, "revisionCode") == nullptr) { el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, "revisionCode", options_.revision_code_default.value()}); } } return true; }); // Meta tags. manifest_action["eat-comment"]; // Uses-sdk actions. manifest_action["uses-sdk"].Action([&](xml::Element* el) -> bool { if (options_.min_sdk_version_default && el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion") == nullptr) { // There was no minSdkVersion defined and we have a default to assign. el->attributes.push_back( xml::Attribute{xml::kSchemaAndroid, "minSdkVersion", options_.min_sdk_version_default.value()}); } if (options_.target_sdk_version_default && el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion") == nullptr) { // There was no targetSdkVersion defined and we have a default to assign. el->attributes.push_back( xml::Attribute{xml::kSchemaAndroid, "targetSdkVersion", options_.target_sdk_version_default.value()}); } return true; }); manifest_action["uses-sdk"]["extension-sdk"]; // Instrumentation actions. manifest_action["instrumentation"].Action(RequiredNameIsJavaClassName); manifest_action["instrumentation"].Action([&](xml::Element* el) -> bool { if (!options_.rename_instrumentation_target_package) { return true; } if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "targetPackage")) { attr->value = options_.rename_instrumentation_target_package.value(); } return true; }); manifest_action["instrumentation"]["meta-data"] = meta_data_action; manifest_action["attribution"]; manifest_action["attribution"]["inherit-from"]; manifest_action["original-package"]; manifest_action["overlay"].Action([&](xml::Element* el) -> bool { if (!options_.rename_overlay_target_package) { return true; } if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "targetPackage")) { attr->value = options_.rename_overlay_target_package.value(); } return true; }); manifest_action["protected-broadcast"]; manifest_action["adopt-permissions"]; manifest_action["uses-permission"]; manifest_action["uses-permission"]["required-feature"].Action(RequiredNameIsNotEmpty); manifest_action["uses-permission"]["required-not-feature"].Action(RequiredNameIsNotEmpty); manifest_action["uses-permission-sdk-23"]; manifest_action["permission"]; manifest_action["permission"]["meta-data"] = meta_data_action; manifest_action["permission-tree"]; manifest_action["permission-group"]; manifest_action["uses-configuration"]; manifest_action["supports-screens"]; manifest_action["uses-feature"] = uses_feature_action; manifest_action["feature-group"]["uses-feature"] = uses_feature_action; manifest_action["compatible-screens"]; manifest_action["compatible-screens"]["screen"]; manifest_action["supports-gl-texture"]; manifest_action["restrict-update"]; manifest_action["package-verifier"]; manifest_action["meta-data"] = meta_data_action; manifest_action["uses-split"].Action(RequiredNameIsJavaPackage); manifest_action["queries"]["package"].Action(RequiredNameIsJavaPackage); manifest_action["queries"]["intent"] = intent_filter_action; manifest_action["queries"]["provider"].Action(RequiredAndroidAttribute("authorities")); // TODO: more complicated component name tag manifest_action["key-sets"]["key-set"]["public-key"]; manifest_action["key-sets"]["upgrade-key-set"]; // Application actions. xml::XmlNodeAction& application_action = manifest_action["application"]; application_action.Action(OptionalNameIsJavaClassName); application_action["uses-library"].Action(RequiredNameIsNotEmpty); application_action["uses-native-library"].Action(RequiredNameIsNotEmpty); application_action["library"].Action(RequiredNameIsNotEmpty); application_action["profileable"]; application_action["property"] = property_action; xml::XmlNodeAction& static_library_action = application_action["static-library"]; static_library_action.Action(RequiredNameIsJavaPackage); static_library_action.Action(RequiredAndroidAttribute("version")); xml::XmlNodeAction& uses_static_library_action = application_action["uses-static-library"]; uses_static_library_action.Action(RequiredNameIsJavaPackage); uses_static_library_action.Action(RequiredAndroidAttribute("version")); uses_static_library_action.Action(RequiredAndroidAttribute("certDigest")); uses_static_library_action["additional-certificate"]; xml::XmlNodeAction& uses_package_action = application_action["uses-package"]; uses_package_action.Action(RequiredNameIsJavaPackage); uses_package_action["additional-certificate"]; if (options_.debug_mode) { application_action.Action([&](xml::Element* el) -> bool { xml::Attribute *attr = el->FindOrCreateAttribute(xml::kSchemaAndroid, "debuggable"); attr->value = "true"; return true; }); } application_action["meta-data"] = meta_data_action; application_action["processes"]; application_action["processes"]["deny-permission"]; application_action["processes"]["allow-permission"]; application_action["processes"]["process"]["deny-permission"]; application_action["processes"]["process"]["allow-permission"]; application_action["activity"] = component_action; application_action["activity"]["layout"]; application_action["activity-alias"] = component_action; application_action["service"] = component_action; application_action["receiver"] = component_action; // Provider actions. application_action["provider"] = component_action; application_action["provider"]["grant-uri-permission"]; application_action["provider"]["path-permission"]; manifest_action["package"] = manifest_action; return true; } static void FullyQualifyClassName(const StringPiece& package, const StringPiece& attr_ns, const StringPiece& attr_name, xml::Element* el) { xml::Attribute* attr = el->FindAttribute(attr_ns, attr_name); if (attr != nullptr) { if (Maybe new_value = util::GetFullyQualifiedClassName(package, attr->value)) { attr->value = std::move(new_value.value()); } } } static bool RenameManifestPackage(const StringPiece& package_override, xml::Element* manifest_el) { xml::Attribute* attr = manifest_el->FindAttribute({}, "package"); // We've already verified that the manifest element is present, with a package // name specified. CHECK(attr != nullptr); std::string original_package = std::move(attr->value); attr->value = package_override.to_string(); xml::Element* application_el = manifest_el->FindChild({}, "application"); if (application_el != nullptr) { FullyQualifyClassName(original_package, xml::kSchemaAndroid, "name", application_el); FullyQualifyClassName(original_package, xml::kSchemaAndroid, "backupAgent", application_el); for (xml::Element* child_el : application_el->GetChildElements()) { if (child_el->namespace_uri.empty()) { if (child_el->name == "activity" || child_el->name == "activity-alias" || child_el->name == "provider" || child_el->name == "receiver" || child_el->name == "service") { FullyQualifyClassName(original_package, xml::kSchemaAndroid, "name", child_el); } if (child_el->name == "activity-alias") { FullyQualifyClassName(original_package, xml::kSchemaAndroid, "targetActivity", child_el); } } } } return true; } bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) { TRACE_CALL(); xml::Element* root = xml::FindRootElement(doc->root.get()); if (!root || !root->namespace_uri.empty() || root->name != "manifest") { context->GetDiagnostics()->Error(DiagMessage(doc->file.source) << "root tag must be "); return false; } if ((options_.min_sdk_version_default || options_.target_sdk_version_default) && root->FindChild({}, "uses-sdk") == nullptr) { // Auto insert a element. This must be inserted before the // tag. The device runtime PackageParser will make SDK version // decisions while parsing . std::unique_ptr uses_sdk = util::make_unique(); uses_sdk->name = "uses-sdk"; root->InsertChild(0, std::move(uses_sdk)); } if (options_.compile_sdk_version) { xml::Attribute* attr = root->FindOrCreateAttribute(xml::kSchemaAndroid, "compileSdkVersion"); // Make sure we un-compile the value if it was set to something else. attr->compiled_value = {}; attr->value = options_.compile_sdk_version.value(); attr = root->FindOrCreateAttribute("", "platformBuildVersionCode"); // Make sure we un-compile the value if it was set to something else. attr->compiled_value = {}; attr->value = options_.compile_sdk_version.value(); } if (options_.compile_sdk_version_codename) { xml::Attribute* attr = root->FindOrCreateAttribute(xml::kSchemaAndroid, "compileSdkVersionCodename"); // Make sure we un-compile the value if it was set to something else. attr->compiled_value = {}; attr->value = options_.compile_sdk_version_codename.value(); attr = root->FindOrCreateAttribute("", "platformBuildVersionName"); // Make sure we un-compile the value if it was set to something else. attr->compiled_value = {}; attr->value = options_.compile_sdk_version_codename.value(); } xml::XmlActionExecutor executor; if (!BuildRules(&executor, context->GetDiagnostics())) { return false; } xml::XmlActionExecutorPolicy policy = options_.warn_validation ? xml::XmlActionExecutorPolicy::kAllowListWarning : xml::XmlActionExecutorPolicy::kAllowList; if (!executor.Execute(policy, context->GetDiagnostics(), doc)) { return false; } if (options_.rename_manifest_package) { // Rename manifest package outside of the XmlActionExecutor. // We need to extract the old package name and FullyQualify all class // names. if (!RenameManifestPackage(options_.rename_manifest_package.value(), root)) { return false; } } return true; } } // namespace aapt