Loading tools/aapt2/cmd/Convert.cpp +28 −0 Original line number Diff line number Diff line Loading @@ -21,7 +21,9 @@ #include "Diagnostics.h" #include "LoadedApk.h" #include "ValueVisitor.h" #include "android-base/file.h" #include "android-base/macros.h" #include "android-base/stringprintf.h" #include "androidfw/StringPiece.h" #include "cmd/Util.h" #include "format/binary/TableFlattener.h" Loading Loading @@ -353,6 +355,27 @@ int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer return 0; } bool ExtractResourceConfig(const std::string& path, IAaptContext* context, TableFlattenerOptions& out_options) { std::string content; if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) { context->GetDiagnostics()->Error(android::DiagMessage(path) << "failed reading config file"); return false; } std::unordered_set<ResourceName> resources_exclude_list; bool result = ParseResourceConfig(content, context, resources_exclude_list, out_options.name_collapse_exemptions); if (!result) { return false; } if (!resources_exclude_list.empty()) { context->GetDiagnostics()->Error(android::DiagMessage(path) << "Unsupported '#remove' directive in resource config."); return false; } return true; } const char* ConvertCommand::kOutputFormatProto = "proto"; const char* ConvertCommand::kOutputFormatBinary = "binary"; Loading Loading @@ -401,6 +424,11 @@ int ConvertCommand::Action(const std::vector<std::string>& args) { if (force_sparse_encoding_) { table_flattener_options_.sparse_entries = SparseEntriesMode::Forced; } if (resources_config_path_) { if (!ExtractResourceConfig(*resources_config_path_, &context, table_flattener_options_)) { return 1; } } return Convert(&context, apk.get(), writer.get(), format, table_flattener_options_, xml_flattener_options_); Loading tools/aapt2/cmd/Convert.h +20 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,25 @@ class ConvertCommand : public Command { android::base::StringPrintf("Preserve raw attribute values in xml files when using the" " '%s' output format", kOutputFormatBinary), &xml_flattener_options_.keep_raw_values); AddOptionalFlag("--resources-config-path", "Path to the resources.cfg file containing the list of resources and \n" "directives to each resource. \n" "Format: type/resource_name#[directive][,directive]", &resources_config_path_); AddOptionalSwitch( "--collapse-resource-names", "Collapses resource names to a single value in the key string pool. Resources can \n" "be exempted using the \"no_collapse\" directive in a file specified by " "--resources-config-path.", &table_flattener_options_.collapse_key_stringpool); AddOptionalSwitch( "--deduplicate-entry-values", "Whether to deduplicate pairs of resource entry and value for simple resources.\n" "This is recommended to be used together with '--collapse-resource-names' flag or for\n" "APKs where resource names are manually collapsed. For such APKs this flag allows to\n" "store the same resource value only once in resource table which decreases APK size.\n" "Has no effect on APKs where resource names are kept.", &table_flattener_options_.deduplicate_entry_values); AddOptionalSwitch("-v", "Enables verbose logging", &verbose_); } Loading @@ -66,6 +85,7 @@ class ConvertCommand : public Command { bool verbose_ = false; bool enable_sparse_encoding_ = false; bool force_sparse_encoding_ = false; std::optional<std::string> resources_config_path_; }; int Convert(IAaptContext* context, LoadedApk* input, IArchiveWriter* output_writer, Loading tools/aapt2/cmd/Convert_test.cpp +77 −0 Original line number Diff line number Diff line Loading @@ -17,13 +17,18 @@ #include "Convert.h" #include "LoadedApk.h" #include "test/Common.h" #include "test/Test.h" #include "ziparchive/zip_archive.h" using testing::AnyOfArray; using testing::Eq; using testing::Ne; using testing::Not; using testing::SizeIs; namespace aapt { using namespace aapt::test; using ConvertTest = CommandTestFixture; Loading Loading @@ -145,4 +150,76 @@ TEST_F(ConvertTest, DuplicateEntriesWrittenOnce) { EXPECT_THAT(count, Eq(1)); } TEST_F(ConvertTest, ConvertWithResourceNameCollapsing) { StdErrDiagnostics diag; const std::string compiled_files_dir = GetTestPath("compiled"); ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), R"(<resources> <string name="first">string</string> <string name="second">string</string> <string name="third">another string</string> <bool name="bool1">true</bool> <bool name="bool2">true</bool> <bool name="bool3">true</bool> <integer name="int1">10</integer> <integer name="int2">10</integer> </resources>)", compiled_files_dir, &diag)); std::string resource_config_path = GetTestPath("resource-config"); WriteFile(resource_config_path, "integer/int1#no_collapse\ninteger/int2#no_collapse"); const std::string proto_apk = GetTestPath("proto.apk"); std::vector<std::string> link_args = { "--proto-format", "--manifest", GetDefaultManifest(kDefaultPackageName), "-o", proto_apk, }; ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag)); const std::string binary_apk = GetTestPath("binary.apk"); std::vector<android::StringPiece> convert_args = {"-o", binary_apk, "--output-format", "binary", "--collapse-resource-names", "--deduplicate-entry-values", "--resources-config-path", resource_config_path, proto_apk}; ASSERT_THAT(ConvertCommand().Execute(convert_args, &std::cerr), Eq(0)); std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(binary_apk, &diag); for (const auto& package : apk->GetResourceTable()->packages) { for (const auto& type : package->types) { switch (type->named_type.type) { case ResourceType::kBool: EXPECT_THAT(type->entries, SizeIs(3)); for (const auto& entry : type->entries) { auto value = ValueCast<BinaryPrimitive>(entry->FindValue({})->value.get())->value; EXPECT_THAT(value.data, Eq(0xffffffffu)); } break; case ResourceType::kString: EXPECT_THAT(type->entries, SizeIs(3)); for (const auto& entry : type->entries) { auto value = ValueCast<String>(entry->FindValue({})->value.get())->value; EXPECT_THAT(entry->name, Not(AnyOfArray({"first", "second", "third"}))); EXPECT_THAT(*value, AnyOfArray({"string", "another string"})); } break; case ResourceType::kInteger: EXPECT_THAT(type->entries, SizeIs(2)); for (const auto& entry : type->entries) { auto value = ValueCast<BinaryPrimitive>(entry->FindValue({})->value.get())->value; EXPECT_THAT(entry->name, AnyOfArray({"int1", "int2"})); EXPECT_THAT(value.data, Eq(10)); } break; default: break; } } } } } // namespace aapt tools/aapt2/cmd/Optimize.cpp +2 −39 Original line number Diff line number Diff line Loading @@ -305,51 +305,14 @@ class Optimizer { OptimizeContext* context_; }; bool ParseConfig(const std::string& content, IAaptContext* context, OptimizeOptions* options) { size_t line_no = 0; for (StringPiece line : util::Tokenize(content, '\n')) { line_no++; line = util::TrimWhitespace(line); if (line.empty()) { continue; } auto split_line = util::Split(line, '#'); if (split_line.size() < 2) { context->GetDiagnostics()->Error(android::DiagMessage(line) << "No # found in line"); return false; } StringPiece resource_string = split_line[0]; StringPiece directives = split_line[1]; ResourceNameRef resource_name; if (!ResourceUtils::ParseResourceName(resource_string, &resource_name)) { context->GetDiagnostics()->Error(android::DiagMessage(line) << "Malformed resource name"); return false; } if (!resource_name.package.empty()) { context->GetDiagnostics()->Error(android::DiagMessage(line) << "Package set for resource. Only use type/name"); return false; } for (StringPiece directive : util::Tokenize(directives, ',')) { if (directive == "remove") { options->resources_exclude_list.insert(resource_name.ToResourceName()); } else if (directive == "no_collapse" || directive == "no_obfuscate") { options->table_flattener_options.name_collapse_exemptions.insert( resource_name.ToResourceName()); } } } return true; } bool ExtractConfig(const std::string& path, IAaptContext* context, OptimizeOptions* options) { std::string content; if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) { context->GetDiagnostics()->Error(android::DiagMessage(path) << "failed reading config file"); return false; } return ParseConfig(content, context, options); return ParseResourceConfig(content, context, options->resources_exclude_list, options->table_flattener_options.name_collapse_exemptions); } bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk, Loading tools/aapt2/cmd/Optimize.h +8 −0 Original line number Diff line number Diff line Loading @@ -123,6 +123,14 @@ class OptimizeCommand : public Command { AddOptionalFlag("--resource-path-shortening-map", "Path to output the map of old resource paths to shortened paths.", &options_.shortened_paths_map_path); AddOptionalSwitch( "--deduplicate-entry-values", "Whether to deduplicate pairs of resource entry and value for simple resources.\n" "This is recommended to be used together with '--collapse-resource-names' flag or for\n" "APKs where resource names are manually collapsed. For such APKs this flag allows to\n" "store the same resource value only once in resource table which decreases APK size.\n" "Has no effect on APKs where resource names are kept.", &options_.table_flattener_options.deduplicate_entry_values); AddOptionalSwitch("-v", "Enables verbose logging", &verbose_); } Loading Loading
tools/aapt2/cmd/Convert.cpp +28 −0 Original line number Diff line number Diff line Loading @@ -21,7 +21,9 @@ #include "Diagnostics.h" #include "LoadedApk.h" #include "ValueVisitor.h" #include "android-base/file.h" #include "android-base/macros.h" #include "android-base/stringprintf.h" #include "androidfw/StringPiece.h" #include "cmd/Util.h" #include "format/binary/TableFlattener.h" Loading Loading @@ -353,6 +355,27 @@ int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer return 0; } bool ExtractResourceConfig(const std::string& path, IAaptContext* context, TableFlattenerOptions& out_options) { std::string content; if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) { context->GetDiagnostics()->Error(android::DiagMessage(path) << "failed reading config file"); return false; } std::unordered_set<ResourceName> resources_exclude_list; bool result = ParseResourceConfig(content, context, resources_exclude_list, out_options.name_collapse_exemptions); if (!result) { return false; } if (!resources_exclude_list.empty()) { context->GetDiagnostics()->Error(android::DiagMessage(path) << "Unsupported '#remove' directive in resource config."); return false; } return true; } const char* ConvertCommand::kOutputFormatProto = "proto"; const char* ConvertCommand::kOutputFormatBinary = "binary"; Loading Loading @@ -401,6 +424,11 @@ int ConvertCommand::Action(const std::vector<std::string>& args) { if (force_sparse_encoding_) { table_flattener_options_.sparse_entries = SparseEntriesMode::Forced; } if (resources_config_path_) { if (!ExtractResourceConfig(*resources_config_path_, &context, table_flattener_options_)) { return 1; } } return Convert(&context, apk.get(), writer.get(), format, table_flattener_options_, xml_flattener_options_); Loading
tools/aapt2/cmd/Convert.h +20 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,25 @@ class ConvertCommand : public Command { android::base::StringPrintf("Preserve raw attribute values in xml files when using the" " '%s' output format", kOutputFormatBinary), &xml_flattener_options_.keep_raw_values); AddOptionalFlag("--resources-config-path", "Path to the resources.cfg file containing the list of resources and \n" "directives to each resource. \n" "Format: type/resource_name#[directive][,directive]", &resources_config_path_); AddOptionalSwitch( "--collapse-resource-names", "Collapses resource names to a single value in the key string pool. Resources can \n" "be exempted using the \"no_collapse\" directive in a file specified by " "--resources-config-path.", &table_flattener_options_.collapse_key_stringpool); AddOptionalSwitch( "--deduplicate-entry-values", "Whether to deduplicate pairs of resource entry and value for simple resources.\n" "This is recommended to be used together with '--collapse-resource-names' flag or for\n" "APKs where resource names are manually collapsed. For such APKs this flag allows to\n" "store the same resource value only once in resource table which decreases APK size.\n" "Has no effect on APKs where resource names are kept.", &table_flattener_options_.deduplicate_entry_values); AddOptionalSwitch("-v", "Enables verbose logging", &verbose_); } Loading @@ -66,6 +85,7 @@ class ConvertCommand : public Command { bool verbose_ = false; bool enable_sparse_encoding_ = false; bool force_sparse_encoding_ = false; std::optional<std::string> resources_config_path_; }; int Convert(IAaptContext* context, LoadedApk* input, IArchiveWriter* output_writer, Loading
tools/aapt2/cmd/Convert_test.cpp +77 −0 Original line number Diff line number Diff line Loading @@ -17,13 +17,18 @@ #include "Convert.h" #include "LoadedApk.h" #include "test/Common.h" #include "test/Test.h" #include "ziparchive/zip_archive.h" using testing::AnyOfArray; using testing::Eq; using testing::Ne; using testing::Not; using testing::SizeIs; namespace aapt { using namespace aapt::test; using ConvertTest = CommandTestFixture; Loading Loading @@ -145,4 +150,76 @@ TEST_F(ConvertTest, DuplicateEntriesWrittenOnce) { EXPECT_THAT(count, Eq(1)); } TEST_F(ConvertTest, ConvertWithResourceNameCollapsing) { StdErrDiagnostics diag; const std::string compiled_files_dir = GetTestPath("compiled"); ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), R"(<resources> <string name="first">string</string> <string name="second">string</string> <string name="third">another string</string> <bool name="bool1">true</bool> <bool name="bool2">true</bool> <bool name="bool3">true</bool> <integer name="int1">10</integer> <integer name="int2">10</integer> </resources>)", compiled_files_dir, &diag)); std::string resource_config_path = GetTestPath("resource-config"); WriteFile(resource_config_path, "integer/int1#no_collapse\ninteger/int2#no_collapse"); const std::string proto_apk = GetTestPath("proto.apk"); std::vector<std::string> link_args = { "--proto-format", "--manifest", GetDefaultManifest(kDefaultPackageName), "-o", proto_apk, }; ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag)); const std::string binary_apk = GetTestPath("binary.apk"); std::vector<android::StringPiece> convert_args = {"-o", binary_apk, "--output-format", "binary", "--collapse-resource-names", "--deduplicate-entry-values", "--resources-config-path", resource_config_path, proto_apk}; ASSERT_THAT(ConvertCommand().Execute(convert_args, &std::cerr), Eq(0)); std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(binary_apk, &diag); for (const auto& package : apk->GetResourceTable()->packages) { for (const auto& type : package->types) { switch (type->named_type.type) { case ResourceType::kBool: EXPECT_THAT(type->entries, SizeIs(3)); for (const auto& entry : type->entries) { auto value = ValueCast<BinaryPrimitive>(entry->FindValue({})->value.get())->value; EXPECT_THAT(value.data, Eq(0xffffffffu)); } break; case ResourceType::kString: EXPECT_THAT(type->entries, SizeIs(3)); for (const auto& entry : type->entries) { auto value = ValueCast<String>(entry->FindValue({})->value.get())->value; EXPECT_THAT(entry->name, Not(AnyOfArray({"first", "second", "third"}))); EXPECT_THAT(*value, AnyOfArray({"string", "another string"})); } break; case ResourceType::kInteger: EXPECT_THAT(type->entries, SizeIs(2)); for (const auto& entry : type->entries) { auto value = ValueCast<BinaryPrimitive>(entry->FindValue({})->value.get())->value; EXPECT_THAT(entry->name, AnyOfArray({"int1", "int2"})); EXPECT_THAT(value.data, Eq(10)); } break; default: break; } } } } } // namespace aapt
tools/aapt2/cmd/Optimize.cpp +2 −39 Original line number Diff line number Diff line Loading @@ -305,51 +305,14 @@ class Optimizer { OptimizeContext* context_; }; bool ParseConfig(const std::string& content, IAaptContext* context, OptimizeOptions* options) { size_t line_no = 0; for (StringPiece line : util::Tokenize(content, '\n')) { line_no++; line = util::TrimWhitespace(line); if (line.empty()) { continue; } auto split_line = util::Split(line, '#'); if (split_line.size() < 2) { context->GetDiagnostics()->Error(android::DiagMessage(line) << "No # found in line"); return false; } StringPiece resource_string = split_line[0]; StringPiece directives = split_line[1]; ResourceNameRef resource_name; if (!ResourceUtils::ParseResourceName(resource_string, &resource_name)) { context->GetDiagnostics()->Error(android::DiagMessage(line) << "Malformed resource name"); return false; } if (!resource_name.package.empty()) { context->GetDiagnostics()->Error(android::DiagMessage(line) << "Package set for resource. Only use type/name"); return false; } for (StringPiece directive : util::Tokenize(directives, ',')) { if (directive == "remove") { options->resources_exclude_list.insert(resource_name.ToResourceName()); } else if (directive == "no_collapse" || directive == "no_obfuscate") { options->table_flattener_options.name_collapse_exemptions.insert( resource_name.ToResourceName()); } } } return true; } bool ExtractConfig(const std::string& path, IAaptContext* context, OptimizeOptions* options) { std::string content; if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) { context->GetDiagnostics()->Error(android::DiagMessage(path) << "failed reading config file"); return false; } return ParseConfig(content, context, options); return ParseResourceConfig(content, context, options->resources_exclude_list, options->table_flattener_options.name_collapse_exemptions); } bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk, Loading
tools/aapt2/cmd/Optimize.h +8 −0 Original line number Diff line number Diff line Loading @@ -123,6 +123,14 @@ class OptimizeCommand : public Command { AddOptionalFlag("--resource-path-shortening-map", "Path to output the map of old resource paths to shortened paths.", &options_.shortened_paths_map_path); AddOptionalSwitch( "--deduplicate-entry-values", "Whether to deduplicate pairs of resource entry and value for simple resources.\n" "This is recommended to be used together with '--collapse-resource-names' flag or for\n" "APKs where resource names are manually collapsed. For such APKs this flag allows to\n" "store the same resource value only once in resource table which decreases APK size.\n" "Has no effect on APKs where resource names are kept.", &options_.table_flattener_options.deduplicate_entry_values); AddOptionalSwitch("-v", "Enables verbose logging", &verbose_); } Loading