Verify the locale format within a localeConfig file
Leverage the existing mechanism for checking the locale format of the resources to check whether the inputs in the localeConfig file conforms to the BCP47 regular expressions. Bug: 208943132 Test: AAPT2 test Change-Id: If972b8cc89b1e5bab422be16cc13d471e39f036f
This commit is contained in:
@@ -1057,6 +1057,83 @@ class Linker {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerifyLocaleFormat(xml::XmlResource* manifest, 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;
|
||||
}
|
||||
|
||||
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) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& value : localeconfig_entry->values) {
|
||||
// Load an XML file which is linked from the localeConfig attribute.
|
||||
const std::string& path = value->value->GetSource().path;
|
||||
std::unique_ptr<xml::XmlResource> localeConfig_xml = LoadXml(path, diag);
|
||||
if (!localeConfig_xml) {
|
||||
diag->Error(DiagMessage(path) << "can't load the XML");
|
||||
return false;
|
||||
}
|
||||
|
||||
xml::Element* localeConfig_el = xml::FindRootElement(localeConfig_xml->root.get());
|
||||
if (!localeConfig_el) {
|
||||
diag->Error(DiagMessage(path) << "no root tag defined");
|
||||
return false;
|
||||
}
|
||||
if (localeConfig_el->name != "locale-config") {
|
||||
diag->Error(DiagMessage(path) << "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(DiagMessage(path) << "invalid configuration: " << locale_name);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
diag->Error(DiagMessage(path) << "the attribute android:name is not found");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
diag->Error(DiagMessage(path)
|
||||
<< "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);
|
||||
@@ -2180,6 +2257,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;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "LoadedApk.h"
|
||||
#include "test/Test.h"
|
||||
|
||||
using android::ConfigDescription;
|
||||
using testing::Eq;
|
||||
using testing::HasSubstr;
|
||||
using testing::IsNull;
|
||||
@@ -783,4 +784,51 @@ TEST_F(LinkTest, MacroSubstitution) {
|
||||
EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!"));
|
||||
}
|
||||
|
||||
TEST_F(LinkTest, ParseLocaleConfig) {
|
||||
StdErrDiagnostics diag;
|
||||
const std::string xml_values =
|
||||
R"(<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<locale android:name="pt"/>
|
||||
<locale android:name="chr"/>
|
||||
<locale android:name="chr-US"/>
|
||||
<locale android:name="zh-Hant"/>
|
||||
<locale android:name="es-419"/>
|
||||
<locale android:name="en-US"/>
|
||||
<locale android:name="zh-Hans-SG"/>
|
||||
</locale-config>)";
|
||||
|
||||
const std::string res = GetTestPath("test-res");
|
||||
ASSERT_TRUE(CompileFile(GetTestPath("res/xml/locale_config.xml"), xml_values, res, &diag));
|
||||
|
||||
const std::string out_apk = GetTestPath("out.apk");
|
||||
auto link_args = LinkCommandBuilder(this)
|
||||
.SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
|
||||
.AddCompiledResDir(res, &diag)
|
||||
.AddFlag("--no-auto-version")
|
||||
.Build(out_apk);
|
||||
ASSERT_TRUE(Link(link_args, &diag));
|
||||
|
||||
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
|
||||
ASSERT_THAT(apk, Ne(nullptr));
|
||||
|
||||
auto xml = apk->LoadXml("res/xml/locale_config.xml", &diag);
|
||||
ASSERT_THAT(xml, NotNull());
|
||||
EXPECT_THAT(xml->root->name, Eq("locale-config"));
|
||||
ASSERT_THAT(xml->root->children.size(), Eq(7));
|
||||
for (auto& node : xml->root->children) {
|
||||
const xml::Element* child_el = xml::NodeCast<xml::Element>(node.get());
|
||||
ASSERT_THAT(child_el, NotNull());
|
||||
EXPECT_THAT(child_el->name, Eq("locale"));
|
||||
|
||||
auto& xml_attrs = child_el->attributes;
|
||||
for (auto& attr : xml_attrs) {
|
||||
std::string locale = "b+";
|
||||
locale += attr.value;
|
||||
std::replace(locale.begin(), locale.end(), '-', '+');
|
||||
ConfigDescription config;
|
||||
ASSERT_TRUE(ConfigDescription::Parse(locale, &config));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
Reference in New Issue
Block a user