diff options
author | 2022-06-09 19:19:21 +0800 | |
---|---|---|
committer | 2022-06-10 23:51:55 +0800 | |
commit | ec67cb4990eac60b71caab6e3d94ab0733098128 (patch) | |
tree | 9de632d0aedbfa5dac82fea99437cee1b1359f62 | |
parent | c58eb89ed07c5b5c4d7ea251d4d33ef01383f0f5 (diff) |
Verify the locale format within a localeConfig file
Leverage the existing mechanism for validating the locale format to verify whether the locale format of the compiled localeConfig file follows the BCP47 reqular expressions
Bug: 208943132
Test: 1. atest aapt2_tests
2. Build the app bundle with the local aapt2 in the Android Studio
a. Make sure the app can be built pass with AGP 7.3.0-alpha06.
b. Make sure the app can be built pass by using the local aapt2 with gradle: set the android.aapt2FromMavenOverride=<path> variable in gradle.properties.
c. Revove the property above in gradle.properties then apply AGP 7.3.0-alpha07. Make sure the appt2 shows error(AAPT: error: failed to load XML file: No such file or directory) when building the app.
d. Add the locale aapt2 with gradle: set the android.aapt2FromMavenOverride=<path> variable in gradle.properties. Make sure the app can be built pass.
Change-Id: Ifa4b9123d36f9c2d450fdf16e1f52963962d7d81
-rw-r--r-- | tools/aapt2/cmd/Link.cpp | 110 | ||||
-rw-r--r-- | tools/aapt2/cmd/Link_test.cpp | 171 | ||||
-rw-r--r-- | tools/aapt2/integration-tests/CommandTests/android-33.jar (renamed from tools/aapt2/integration-tests/CommandTests/android-28.jar) | bin | 31949393 -> 27128945 bytes | |||
-rw-r--r-- | tools/aapt2/test/Fixture.cpp | 12 |
4 files changed, 287 insertions, 6 deletions
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index 2e841206c206..74a8bbd4fd0d 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -1066,6 +1066,112 @@ class Linker { return true; } + bool VerifyLocaleFormat(xml::XmlResource* manifest, android::IDiagnostics* diag) { + // Skip it if the Manifest doesn't declare the localeConfig attribute within the <application> + // element. + const xml::Element* application = manifest->root->FindChild("", "application"); + if (!application) { + return true; + } + const xml::Attribute* localeConfig = + application->FindAttribute(xml::kSchemaAndroid, "localeConfig"); + if (!localeConfig) { + return true; + } + + // Deserialize XML from the compiled file + if (localeConfig->compiled_value) { + const auto localeconfig_reference = ValueCast<Reference>(localeConfig->compiled_value.get()); + const auto localeconfig_entry = + ResolveTableEntry(context_, &final_table_, localeconfig_reference); + if (!localeconfig_entry) { + context_->GetDiagnostics()->Error( + android::DiagMessage(localeConfig->compiled_value->GetSource()) + << "no localeConfig entry"); + return false; + } + for (const auto& value : localeconfig_entry->values) { + const FileReference* file_ref = ValueCast<FileReference>(value->value.get()); + if (!file_ref) { + context_->GetDiagnostics()->Error( + android::DiagMessage(localeConfig->compiled_value->GetSource()) + << "no file reference"); + return false; + } + io::IFile* file = file_ref->file; + if (!file) { + context_->GetDiagnostics()->Error(android::DiagMessage(file_ref->GetSource()) + << "file not found"); + return false; + } + std::unique_ptr<io::IData> data = file->OpenAsData(); + if (!data) { + context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource()) + << "failed to open file"); + return false; + } + pb::XmlNode pb_xml_node; + if (!pb_xml_node.ParseFromArray(data->data(), static_cast<int>(data->size()))) { + context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource()) + << "failed to parse proto XML"); + return false; + } + + std::string error; + std::unique_ptr<xml::XmlResource> localeConfig_xml = + DeserializeXmlResourceFromPb(pb_xml_node, &error); + if (!localeConfig_xml) { + context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource()) + << "failed to deserialize proto XML: " << error); + return false; + } + xml::Element* localeConfig_el = xml::FindRootElement(localeConfig_xml->root.get()); + if (!localeConfig_el) { + diag->Error(android::DiagMessage(file->GetSource()) << "no root tag defined"); + return false; + } + if (localeConfig_el->name != "locale-config") { + diag->Error(android::DiagMessage(file->GetSource()) + << "invalid element name: " << localeConfig_el->name + << ", expected: locale-config"); + return false; + } + for (const xml::Element* child_el : localeConfig_el->GetChildElements()) { + if (child_el->name == "locale") { + if (const xml::Attribute* locale_name_attr = + child_el->FindAttribute(xml::kSchemaAndroid, "name")) { + const std::string& locale_name = locale_name_attr->value; + const std::string valid_name = ConvertToBCP47Tag(locale_name); + // Start to verify the locale format + ConfigDescription config; + if (!ConfigDescription::Parse(valid_name, &config)) { + diag->Error(android::DiagMessage(file->GetSource()) + << "invalid configuration: " << locale_name); + return false; + } + } else { + diag->Error(android::DiagMessage(file->GetSource()) + << "the attribute android:name is not found"); + return false; + } + } else { + diag->Error(android::DiagMessage(file->GetSource()) + << "invalid element name: " << child_el->name << ", expected: locale"); + return false; + } + } + } + } + return true; + } + + std::string ConvertToBCP47Tag(const std::string& locale) { + std::string bcp47tag = "b+"; + bcp47tag += locale; + std::replace(bcp47tag.begin(), bcp47tag.end(), '-', '+'); + return bcp47tag; + } + std::unique_ptr<IArchiveWriter> MakeArchiveWriter(const StringPiece& out) { if (options_.output_to_directory) { return CreateDirectoryArchiveWriter(context_->GetDiagnostics(), out); @@ -2195,6 +2301,10 @@ class Linker { return 1; } + if (!VerifyLocaleFormat(manifest_xml.get(), context_->GetDiagnostics())) { + return 1; + }; + if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(), &final_table_)) { return 1; } diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp index f20cf2c7cea3..254f3a546f99 100644 --- a/tools/aapt2/cmd/Link_test.cpp +++ b/tools/aapt2/cmd/Link_test.cpp @@ -785,4 +785,175 @@ TEST_F(LinkTest, MacroSubstitution) { EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!")); } +TEST_F(LinkTest, LocaleConfigVerification) { + StdErrDiagnostics diag; + const std::string compiled_files_dir = GetTestPath("compiled"); + + // Normal case + ASSERT_TRUE(CompileFile(GetTestPath("res/xml/locales_config.xml"), R"( + <locale-config xmlns:android="http://schemas.android.com/apk/res/android"> + <locale android:name="en-US"/> + <locale android:name="pt"/> + <locale android:name="es-419"/> + <locale android:name="zh-Hans-SG"/> + </locale-config>)", + compiled_files_dir, &diag)); + + const std::string localeconfig_manifest = GetTestPath("localeconfig_manifest.xml"); + WriteFile(localeconfig_manifest, android::base::StringPrintf(R"( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.aapt2.app"> + + <application + android:localeConfig="@xml/locales_config"> + </application> + </manifest>)")); + + const std::string out_apk = GetTestPath("out.apk"); + + auto link_args = LinkCommandBuilder(this) + .SetManifestFile(localeconfig_manifest) + .AddCompiledResDir(compiled_files_dir, &diag) + .Build(out_apk); + ASSERT_TRUE(Link(link_args, &diag)); + + // Empty locale list + ASSERT_TRUE(CompileFile(GetTestPath("res/xml/empty_locales_config.xml"), R"( + <locale-config xmlns:android="http://schemas.android.com/apk/res/android"> + </locale-config>)", + compiled_files_dir, &diag)); + + const std::string empty_localeconfig_manifest = GetTestPath("empty_localeconfig_manifest.xml"); + WriteFile(empty_localeconfig_manifest, android::base::StringPrintf(R"( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.aapt2.app"> + + <application + android:localeConfig="@xml/empty_locales_config"> + </application> + </manifest>)")); + + auto link1_args = LinkCommandBuilder(this) + .SetManifestFile(empty_localeconfig_manifest) + .AddCompiledResDir(compiled_files_dir, &diag) + .Build(out_apk); + ASSERT_TRUE(Link(link1_args, &diag)); +} + +TEST_F(LinkTest, LocaleConfigWrongTag) { + StdErrDiagnostics diag; + const std::string compiled_files_dir = GetTestPath("compiled"); + + // Invalid element: locale1-config + ASSERT_TRUE(CompileFile(GetTestPath("res/xml/wrong_locale_config.xml"), R"( + <locale1-config xmlns:android="http://schemas.android.com/apk/res/android"> + <locale android:name="en-US"/> + <locale android:name="pt"/> + <locale android:name="es-419"/> + <locale android:name="zh-Hans-SG"/> + </locale1-config>)", + compiled_files_dir, &diag)); + + const std::string locale1config_manifest = GetTestPath("locale1config_manifest.xml"); + WriteFile(locale1config_manifest, android::base::StringPrintf(R"( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.aapt2.app"> + + <application + android:localeConfig="@xml/wrong_locale_config"> + </application> + </manifest>)")); + + const std::string out_apk = GetTestPath("out.apk"); + auto link_args = LinkCommandBuilder(this) + .SetManifestFile(locale1config_manifest) + .AddCompiledResDir(compiled_files_dir, &diag) + .Build(out_apk); + ASSERT_FALSE(Link(link_args, &diag)); + + // Invalid element: locale1 + ASSERT_TRUE(CompileFile(GetTestPath("res/xml/wrong_locale.xml"), R"( + <locale-config xmlns:android="http://schemas.android.com/apk/res/android"> + <locale1 android:name="en-US"/> + <locale android:name="pt"/> + <locale android:name="es-419"/> + <locale android:name="zh-Hans-SG"/> + </locale-config>)", + compiled_files_dir, &diag)); + + const std::string locale1_manifest = GetTestPath("locale1_manifest.xml"); + WriteFile(locale1_manifest, android::base::StringPrintf(R"( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.aapt2.app"> + + <application + android:localeConfig="@xml/wrong_locale"> + </application> + </manifest>)")); + + auto link1_args = LinkCommandBuilder(this) + .SetManifestFile(locale1_manifest) + .AddCompiledResDir(compiled_files_dir, &diag) + .Build(out_apk); + ASSERT_FALSE(Link(link1_args, &diag)); + + // Invalid attribute: android:name1 + ASSERT_TRUE(CompileFile(GetTestPath("res/xml/wrong_attribute.xml"), R"( + <locale-config xmlns:android="http://schemas.android.com/apk/res/android"> + <locale android:name1="en-US"/> + <locale android:name="pt"/> + <locale android:name="es-419"/> + <locale android:name="zh-Hans-SG"/> + </locale-config>)", + compiled_files_dir, &diag)); + + const std::string wrong_attribute_manifest = GetTestPath("wrong_attribute_manifest.xml"); + WriteFile(wrong_attribute_manifest, android::base::StringPrintf(R"( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.aapt2.app"> + + <application + android:localeConfig="@xml/wrong_attribute"> + </application> + </manifest>)")); + + auto link2_args = LinkCommandBuilder(this) + .SetManifestFile(wrong_attribute_manifest) + .AddCompiledResDir(compiled_files_dir, &diag) + .Build(out_apk); + ASSERT_FALSE(Link(link2_args, &diag)); +} + +TEST_F(LinkTest, LocaleConfigWrongLocaleFormat) { + StdErrDiagnostics diag; + const std::string compiled_files_dir = GetTestPath("compiled"); + + // Invalid locale: en-U + ASSERT_TRUE(CompileFile(GetTestPath("res/xml/wrong_locale.xml"), R"( + <locale-config xmlns:android="http://schemas.android.com/apk/res/android"> + <locale android:name="en-U"/> + <locale android:name="pt"/> + <locale android:name="es-419"/> + <locale android:name="zh-Hans-SG"/> + </locale-config>)", + compiled_files_dir, &diag)); + + const std::string wrong_locale_manifest = GetTestPath("wrong_locale_manifest.xml"); + WriteFile(wrong_locale_manifest, android::base::StringPrintf(R"( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.aapt2.app"> + + <application + android:localeConfig="@xml/wrong_locale"> + </application> + </manifest>)")); + + const std::string out_apk = GetTestPath("out.apk"); + auto link_args = LinkCommandBuilder(this) + .SetManifestFile(wrong_locale_manifest) + .AddCompiledResDir(compiled_files_dir, &diag) + .Build(out_apk); + ASSERT_FALSE(Link(link_args, &diag)); +} + } // namespace aapt diff --git a/tools/aapt2/integration-tests/CommandTests/android-28.jar b/tools/aapt2/integration-tests/CommandTests/android-33.jar Binary files differindex ef7576d17c6d..08cf49d854e2 100644 --- a/tools/aapt2/integration-tests/CommandTests/android-28.jar +++ b/tools/aapt2/integration-tests/CommandTests/android-33.jar diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp index ff8b3683c577..dbc0e3643104 100644 --- a/tools/aapt2/test/Fixture.cpp +++ b/tools/aapt2/test/Fixture.cpp @@ -105,9 +105,9 @@ bool CommandTestFixture::Link(const std::vector<std::string>& args, android::IDi } // Link against the android SDK - std::string android_sdk = file::BuildPath({android::base::GetExecutableDirectory(), - "integration-tests", "CommandTests", - "android-28.jar"}); + std::string android_sdk = + file::BuildPath({android::base::GetExecutableDirectory(), "integration-tests", "CommandTests", + "android-33.jar"}); link_args.insert(link_args.end(), {"-I", android_sdk}); return LinkCommand(diag).Execute(link_args, &std::cerr) == 0; @@ -121,9 +121,9 @@ bool CommandTestFixture::Link(const std::vector<std::string>& args, } // Link against the android SDK - std::string android_sdk = file::BuildPath({android::base::GetExecutableDirectory(), - "integration-tests", "CommandTests", - "android-28.jar"}); + std::string android_sdk = + file::BuildPath({android::base::GetExecutableDirectory(), "integration-tests", "CommandTests", + "android-33.jar"}); link_args.insert(link_args.end(), {"-I", android_sdk}); // Add the files from the compiled resources directory to the link file arguments |