diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h index 1e488f70bd4d7..9db21aadb242f 100644 --- a/tools/aapt2/AppInfo.h +++ b/tools/aapt2/AppInfo.h @@ -23,30 +23,22 @@ namespace aapt { -/** - * Holds basic information about the app being built. Most of this information - * will come from the app's AndroidManifest. - */ +// Information relevant to building an app, parsed from the app's AndroidManifest.xml. struct AppInfo { - /** - * App's package name. - */ + // The app's package name. std::string package; - /** - * The App's minimum SDK version. - */ + // The app's minimum SDK version, if it is defined. Maybe min_sdk_version; - /** - * The Version code of the app. - */ + // The app's version code, if it is defined. Maybe version_code; - /** - * The revision code of the app. - */ + // The app's revision code, if it is defined. Maybe revision_code; + + // The app's split name, if it is a split. + Maybe split_name; }; } // namespace aapt diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index 10421115b6292..1b4d5bb4f6e1e 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -751,70 +751,67 @@ class LinkCommand { return true; } - Maybe ExtractAppInfoFromManifest(xml::XmlResource* xml_res, - IDiagnostics* diag) { + Maybe ExtractAppInfoFromManifest(xml::XmlResource* xml_res, IDiagnostics* diag) { // Make sure the first element is with package attribute. - if (xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get())) { - AppInfo app_info; - - if (!manifest_el->namespace_uri.empty() || - manifest_el->name != "manifest") { - diag->Error(DiagMessage(xml_res->file.source) - << "root tag must be "); - return {}; - } - - xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package"); - if (!package_attr) { - diag->Error(DiagMessage(xml_res->file.source) - << " must have a 'package' attribute"); - return {}; - } - - app_info.package = package_attr->value; - - if (xml::Attribute* version_code_attr = - manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) { - Maybe maybe_code = - ResourceUtils::ParseInt(version_code_attr->value); - if (!maybe_code) { - diag->Error(DiagMessage(xml_res->file.source.WithLine( - manifest_el->line_number)) - << "invalid android:versionCode '" - << version_code_attr->value << "'"); - return {}; - } - app_info.version_code = maybe_code.value(); - } - - if (xml::Attribute* revision_code_attr = - manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) { - Maybe maybe_code = - ResourceUtils::ParseInt(revision_code_attr->value); - if (!maybe_code) { - diag->Error(DiagMessage(xml_res->file.source.WithLine( - manifest_el->line_number)) - << "invalid android:revisionCode '" - << revision_code_attr->value << "'"); - return {}; - } - app_info.revision_code = maybe_code.value(); - } - - if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) { - if (xml::Attribute* min_sdk = uses_sdk_el->FindAttribute( - xml::kSchemaAndroid, "minSdkVersion")) { - app_info.min_sdk_version = min_sdk->value; - } - } - return app_info; + xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get()); + if (manifest_el == nullptr) { + return {}; } - return {}; + + AppInfo app_info; + + if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") { + diag->Error(DiagMessage(xml_res->file.source) << "root tag must be "); + return {}; + } + + xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package"); + if (!package_attr) { + diag->Error(DiagMessage(xml_res->file.source) + << " must have a 'package' attribute"); + return {}; + } + app_info.package = package_attr->value; + + if (xml::Attribute* version_code_attr = + manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) { + Maybe maybe_code = ResourceUtils::ParseInt(version_code_attr->value); + if (!maybe_code) { + diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) + << "invalid android:versionCode '" << version_code_attr->value << "'"); + return {}; + } + app_info.version_code = maybe_code.value(); + } + + if (xml::Attribute* revision_code_attr = + manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) { + Maybe maybe_code = ResourceUtils::ParseInt(revision_code_attr->value); + if (!maybe_code) { + diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) + << "invalid android:revisionCode '" << revision_code_attr->value << "'"); + return {}; + } + app_info.revision_code = maybe_code.value(); + } + + if (xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) { + if (!split_name_attr->value.empty()) { + app_info.split_name = split_name_attr->value; + } + } + + if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) { + if (xml::Attribute* min_sdk = + uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) { + app_info.min_sdk_version = min_sdk->value; + } + } + return app_info; } /** - * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it - * linked. + * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked. * Postcondition: ResourceTable has only one package left. All others are * stripped, or there is an error and false is returned. */ @@ -1367,45 +1364,44 @@ class LinkCommand { return true; } - std::unique_ptr GenerateSplitManifest( - const AppInfo& app_info, const SplitConstraints& constraints) { - std::unique_ptr doc = - util::make_unique(); + std::unique_ptr GenerateSplitManifest(const AppInfo& app_info, + const SplitConstraints& constraints) { + std::unique_ptr doc = util::make_unique(); - std::unique_ptr namespace_android = - util::make_unique(); + std::unique_ptr namespace_android = util::make_unique(); namespace_android->namespace_uri = xml::kSchemaAndroid; namespace_android->namespace_prefix = "android"; - std::unique_ptr manifest_el = - util::make_unique(); + std::unique_ptr manifest_el = util::make_unique(); manifest_el->name = "manifest"; - manifest_el->attributes.push_back( - xml::Attribute{"", "package", app_info.package}); + manifest_el->attributes.push_back(xml::Attribute{"", "package", app_info.package}); if (app_info.version_code) { - manifest_el->attributes.push_back( - xml::Attribute{xml::kSchemaAndroid, "versionCode", - std::to_string(app_info.version_code.value())}); + manifest_el->attributes.push_back(xml::Attribute{ + xml::kSchemaAndroid, "versionCode", std::to_string(app_info.version_code.value())}); } if (app_info.revision_code) { - manifest_el->attributes.push_back( - xml::Attribute{xml::kSchemaAndroid, "revisionCode", - std::to_string(app_info.revision_code.value())}); + manifest_el->attributes.push_back(xml::Attribute{ + xml::kSchemaAndroid, "revisionCode", std::to_string(app_info.revision_code.value())}); } std::stringstream split_name; + if (app_info.split_name) { + split_name << app_info.split_name.value() << "."; + } split_name << "config." << util::Joiner(constraints.configs, "_"); - manifest_el->attributes.push_back( - xml::Attribute{"", "split", split_name.str()}); + manifest_el->attributes.push_back(xml::Attribute{"", "split", split_name.str()}); - std::unique_ptr application_el = - util::make_unique(); + if (app_info.split_name) { + manifest_el->attributes.push_back( + xml::Attribute{"", "configForSplit", app_info.split_name.value()}); + } + + std::unique_ptr application_el = util::make_unique(); application_el->name = "application"; - application_el->attributes.push_back( - xml::Attribute{xml::kSchemaAndroid, "hasCode", "false"}); + application_el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, "hasCode", "false"}); manifest_el->AppendChild(std::move(application_el)); namespace_android->AppendChild(std::move(manifest_el)); diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 313fe453dff52..0c19c7ad7f32d 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -29,10 +29,7 @@ using android::StringPiece; namespace aapt { -/** - * This is how PackageManager builds class names from AndroidManifest.xml - * entries. - */ +// 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) @@ -90,6 +87,36 @@ static xml::XmlNodeAction::ActionFuncWithDiag RequiredAndroidAttribute(const std }; } +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, SourcePathDiagnostics* diag) { xml::Attribute* attr = el->FindAttribute({}, "package"); if (!attr) { @@ -97,31 +124,34 @@ static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) { << " 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"); + diag->Error(DiagMessage(el->line_number) + << "attribute 'package' in tag must not be a reference"); return false; } else if (!util::IsJavaPackageName(attr->value)) { diag->Error(DiagMessage(el->line_number) - << "attribute 'package' in tag is not a valid Java " - "package name: '" + << "attribute 'package' in tag is not a valid Java package name: '" << attr->value << "'"); 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. - */ +// 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); + std::unique_ptr result = ResourceUtils::TryParseBool(attr->value); if (!result) { - diag->Error(DiagMessage(el->line_number) - << "attribute coreApp must be a boolean"); + diag->Error(DiagMessage(el->line_number) << "attribute coreApp must be a boolean"); return false; } attr->compiled_value = std::move(result); @@ -172,8 +202,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, } if (options_.rename_instrumentation_target_package) { - if (!util::IsJavaPackageName( - options_.rename_instrumentation_target_package.value())) { + if (!util::IsJavaPackageName(options_.rename_instrumentation_target_package.value())) { diag->Error(DiagMessage() << "invalid instrumentation target package override '" << options_.rename_instrumentation_target_package.value() @@ -203,6 +232,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, // 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 { @@ -276,6 +306,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, manifest_action["compatible-screens"]["screen"]; manifest_action["supports-gl-texture"]; manifest_action["meta-data"] = meta_data_action; + manifest_action["uses-split"].Action(RequiredNameIsJavaPackage); // Application actions. xml::XmlNodeAction& application_action = manifest_action["application"]; @@ -311,15 +342,13 @@ class FullyQualifiedClassNameVisitor : public xml::Visitor { public: using xml::Visitor::Visit; - explicit FullyQualifiedClassNameVisitor(const StringPiece& package) - : package_(package) {} + explicit FullyQualifiedClassNameVisitor(const StringPiece& package) : package_(package) {} void Visit(xml::Element* el) override { for (xml::Attribute& attr : el->attributes) { if (attr.namespace_uri == xml::kSchemaAndroid && class_attributes_.find(attr.name) != class_attributes_.end()) { - if (Maybe new_value = - util::GetFullyQualifiedClassName(package_, attr.value)) { + if (Maybe new_value = util::GetFullyQualifiedClassName(package_, attr.value)) { attr.value = std::move(new_value.value()); } } @@ -334,8 +363,7 @@ class FullyQualifiedClassNameVisitor : public xml::Visitor { std::unordered_set class_attributes_ = {"name"}; }; -static bool RenameManifestPackage(const StringPiece& package_override, - xml::Element* manifest_el) { +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 @@ -358,8 +386,7 @@ bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) { return false; } - if ((options_.min_sdk_version_default || - options_.target_sdk_version_default) && + 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 @@ -374,8 +401,7 @@ bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) { return false; } - if (!executor.Execute(xml::XmlActionExecutorPolicy::kWhitelist, - context->GetDiagnostics(), doc)) { + if (!executor.Execute(xml::XmlActionExecutorPolicy::kWhitelist, context->GetDiagnostics(), doc)) { return false; } @@ -383,8 +409,7 @@ bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) { // 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)) { + if (!RenameManifestPackage(options_.rename_manifest_package.value(), root)) { return false; } }