Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b30eb97 authored by Josh Hou's avatar Josh Hou Committed by Android (Google) Code Review
Browse files

Merge "Verify the locale format within a localeConfig file"

parents d1802054 ec67cb49
Loading
Loading
Loading
Loading
+110 −0
Original line number Diff line number Diff line
@@ -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;
    }
+171 −0
Original line number Diff line number Diff line
@@ -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
−4.6 MiB (25.9 MiB)

File changed and moved.

No diff preview for this file type.

+6 −6
Original line number Diff line number Diff line
@@ -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