Loading tools/aapt2/cmd/Link.cpp +110 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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; } Loading tools/aapt2/cmd/Link_test.cpp +171 −0 Original line number Diff line number Diff line Loading @@ -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 tools/aapt2/integration-tests/CommandTests/android-28.jar→tools/aapt2/integration-tests/CommandTests/android-33.jar −4.6 MiB (25.9 MiB) File changed and moved.No diff preview for this file type. View original file View changed file tools/aapt2/test/Fixture.cpp +6 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading Loading
tools/aapt2/cmd/Link.cpp +110 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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; } Loading
tools/aapt2/cmd/Link_test.cpp +171 −0 Original line number Diff line number Diff line Loading @@ -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
tools/aapt2/integration-tests/CommandTests/android-28.jar→tools/aapt2/integration-tests/CommandTests/android-33.jar −4.6 MiB (25.9 MiB) File changed and moved.No diff preview for this file type. View original file View changed file
tools/aapt2/test/Fixture.cpp +6 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading