diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp index cf58f989a7d42..aeedf8b5805e5 100644 --- a/tools/aapt2/cmd/Convert.cpp +++ b/tools/aapt2/cmd/Convert.cpp @@ -395,6 +395,12 @@ int ConvertCommand::Action(const std::vector& args) { << output_format_.value()); return 1; } + if (enable_sparse_encoding_) { + table_flattener_options_.sparse_entries = SparseEntriesMode::Enabled; + } + if (force_sparse_encoding_) { + table_flattener_options_.sparse_entries = SparseEntriesMode::Forced; + } return Convert(&context, apk.get(), writer.get(), format, table_flattener_options_, xml_flattener_options_); diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h index 2cdb0c87310fa..6c096496a1ea5 100644 --- a/tools/aapt2/cmd/Convert.h +++ b/tools/aapt2/cmd/Convert.h @@ -34,10 +34,18 @@ class ConvertCommand : public Command { AddOptionalFlag("--output-format", android::base::StringPrintf("Format of the output. " "Accepted values are '%s' and '%s'. When not set, defaults to '%s'.", kOutputFormatProto, kOutputFormatBinary, kOutputFormatBinary), &output_format_); - AddOptionalSwitch("--enable-sparse-encoding", + AddOptionalSwitch( + "--enable-sparse-encoding", "Enables encoding sparse entries using a binary search tree.\n" - "This decreases APK size at the cost of resource retrieval performance.", - &table_flattener_options_.use_sparse_entries); + "This decreases APK size at the cost of resource retrieval performance.\n" + "Only applies sparse encoding to Android O+ resources or all resources if minSdk of " + "the APK is O+", + &enable_sparse_encoding_); + AddOptionalSwitch("--force-sparse-encoding", + "Enables encoding sparse entries using a binary search tree.\n" + "This decreases APK size at the cost of resource retrieval performance.\n" + "Applies sparse encoding to all resources regardless of minSdk.", + &force_sparse_encoding_); AddOptionalSwitch("--keep-raw-values", android::base::StringPrintf("Preserve raw attribute values in xml files when using the" " '%s' output format", kOutputFormatBinary), @@ -56,6 +64,8 @@ class ConvertCommand : public Command { std::string output_path_; std::optional output_format_; bool verbose_ = false; + bool enable_sparse_encoding_ = false; + bool force_sparse_encoding_ = false; }; int Convert(IAaptContext* context, LoadedApk* input, IArchiveWriter* output_writer, diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index 74a8bbd4fd0d0..116dcd641bc11 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -2419,6 +2419,9 @@ int LinkCommand::Action(const std::vector& args) { << "the --merge-only flag can be only used when building a static library"); return 1; } + if (options_.use_sparse_encoding) { + options_.table_flattener_options.sparse_entries = SparseEntriesMode::Enabled; + } // The default build type. context.SetPackageType(PackageType::kApp); diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h index a5623cb1f1e16..6c8d1434fb88a 100644 --- a/tools/aapt2/cmd/Link.h +++ b/tools/aapt2/cmd/Link.h @@ -69,6 +69,7 @@ struct LinkOptions { bool no_resource_removal = false; bool no_xml_namespaces = false; bool do_not_compress_anything = false; + bool use_sparse_encoding = false; std::unordered_set extensions_to_not_compress; std::optional regex_to_not_compress; @@ -156,8 +157,8 @@ class LinkCommand : public Command { "defaults. Use this only when building runtime resource overlay packages.", &options_.no_resource_removal); AddOptionalSwitch("--enable-sparse-encoding", - "This decreases APK size at the cost of resource retrieval performance.", - &options_.table_flattener_options.use_sparse_entries); + "This decreases APK size at the cost of resource retrieval performance.", + &options_.use_sparse_encoding); AddOptionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01.", &legacy_x_flag_); AddOptionalSwitch("-z", "Require localization of strings marked 'suggested'.", diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp index 4033983f4c0f2..e37c2d450b732 100644 --- a/tools/aapt2/cmd/Optimize.cpp +++ b/tools/aapt2/cmd/Optimize.cpp @@ -427,6 +427,13 @@ int OptimizeCommand::Action(const std::vector& args) { return 1; } + if (options_.enable_sparse_encoding) { + options_.table_flattener_options.sparse_entries = SparseEntriesMode::Enabled; + } + if (options_.force_sparse_encoding) { + options_.table_flattener_options.sparse_entries = SparseEntriesMode::Forced; + } + if (target_densities_) { // Parse the target screen densities. for (const StringPiece& config_str : util::Tokenize(target_densities_.value(), ',')) { diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h index ff63e8dd76d47..10b84b0c6bdac 100644 --- a/tools/aapt2/cmd/Optimize.h +++ b/tools/aapt2/cmd/Optimize.h @@ -61,6 +61,12 @@ struct OptimizeOptions { // Path to the output map of original resource paths to shortened paths. std::optional shortened_paths_map_path; + + // Whether sparse encoding should be used for O+ resources. + bool enable_sparse_encoding = false; + + // Whether sparse encoding should be used for all resources. + bool force_sparse_encoding = false; }; class OptimizeCommand : public Command { @@ -96,10 +102,18 @@ class OptimizeCommand : public Command { "Comma separated list of artifacts to keep. If none are specified,\n" "all artifacts will be kept.", &kept_artifacts_); - AddOptionalSwitch("--enable-sparse-encoding", + AddOptionalSwitch( + "--enable-sparse-encoding", "Enables encoding sparse entries using a binary search tree.\n" - "This decreases APK size at the cost of resource retrieval performance.", - &options_.table_flattener_options.use_sparse_entries); + "This decreases APK size at the cost of resource retrieval performance.\n" + "Only applies sparse encoding to Android O+ resources or all resources if minSdk of " + "the APK is O+", + &options_.enable_sparse_encoding); + AddOptionalSwitch("--force-sparse-encoding", + "Enables encoding sparse entries using a binary search tree.\n" + "This decreases APK size at the cost of resource retrieval performance.\n" + "Applies sparse encoding to all resources regardless of minSdk.", + &options_.force_sparse_encoding); 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 " diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp index 4fb7ed19ed205..22f278cc0b222 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -231,14 +231,14 @@ struct OverlayableChunk { class PackageFlattener { public: PackageFlattener(IAaptContext* context, const ResourceTablePackageView& package, - const std::map* shared_libs, bool use_sparse_entries, - bool collapse_key_stringpool, + const std::map* shared_libs, + SparseEntriesMode sparse_entries, bool collapse_key_stringpool, const std::set& name_collapse_exemptions) : context_(context), diag_(context->GetDiagnostics()), package_(package), shared_libs_(shared_libs), - use_sparse_entries_(use_sparse_entries), + sparse_entries_(sparse_entries), collapse_key_stringpool_(collapse_key_stringpool), name_collapse_exemptions_(name_collapse_exemptions) { } @@ -367,10 +367,12 @@ class PackageFlattener { } } - bool sparse_encode = use_sparse_entries_; + bool sparse_encode = sparse_entries_ == SparseEntriesMode::Enabled || + sparse_entries_ == SparseEntriesMode::Forced; - if (context_->GetMinSdkVersion() == 0 && config.sdkVersion == 0) { - // Sparse encode if sdk version is not set in context and config. + if (sparse_entries_ == SparseEntriesMode::Forced || + (context_->GetMinSdkVersion() == 0 && config.sdkVersion == 0)) { + // Sparse encode if forced or sdk version is not set in context and config. } else { // Otherwise, only sparse encode if the entries will be read on platforms S_V2+. sparse_encode = sparse_encode && @@ -712,7 +714,7 @@ class PackageFlattener { android::IDiagnostics* diag_; const ResourceTablePackageView package_; const std::map* shared_libs_; - bool use_sparse_entries_; + SparseEntriesMode sparse_entries_; android::StringPool type_pool_; android::StringPool key_pool_; bool collapse_key_stringpool_; @@ -768,7 +770,7 @@ bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) { } PackageFlattener flattener(context, package, &table->included_packages_, - options_.use_sparse_entries, options_.collapse_key_stringpool, + options_.sparse_entries, options_.collapse_key_stringpool, options_.name_collapse_exemptions); if (!flattener.FlattenPackage(&package_buffer)) { return false; diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h index 1eec0e4d8c51f..c6d30335ed53d 100644 --- a/tools/aapt2/format/binary/TableFlattener.h +++ b/tools/aapt2/format/binary/TableFlattener.h @@ -29,12 +29,20 @@ namespace aapt { // preferred. constexpr const size_t kSparseEncodingThreshold = 60; +enum class SparseEntriesMode { + // Disables sparse encoding for entries. + Disabled, + // Enables sparse encoding for all entries for APKs with O+ minSdk. For APKs with minSdk less + // than O only applies sparse encoding for resource configuration available on O+. + Enabled, + // Enables sparse encoding for all entries regardless of minSdk. + Forced, +}; + struct TableFlattenerOptions { - // When true, types for configurations with a sparse set of entries are encoded + // When enabled, types for configurations with a sparse set of entries are encoded // as a sparse map of entry ID and offset to actual data. - // This is only available on platforms O+ and will only be respected when - // minSdk is O+. - bool use_sparse_entries = false; + SparseEntriesMode sparse_entries = SparseEntriesMode::Disabled; // When true, the key string pool in the final ResTable // is collapsed to a single entry. All resource entries diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp index f551bf61dc06b..b69daddf1c7ae 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -337,7 +337,7 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkO) { auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f); TableFlattenerOptions options; - options.use_sparse_entries = true; + options.sparse_entries = SparseEntriesMode::Enabled; std::string no_sparse_contents; ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents)); @@ -380,7 +380,29 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithConfigSdkVersionO) { auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f); TableFlattenerOptions options; - options.use_sparse_entries = true; + options.sparse_entries = SparseEntriesMode::Enabled; + + std::string no_sparse_contents; + ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents)); + + std::string sparse_contents; + ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents)); + + EXPECT_GT(no_sparse_contents.size(), sparse_contents.size()); +} + +TEST_F(TableFlattenerTest, FlattenSparseEntryRegardlessOfMinSdkWhenForced) { + std::unique_ptr context = test::ContextBuilder() + .SetCompilationPackage("android") + .SetPackageId(0x01) + .SetMinSdkVersion(SDK_LOLLIPOP) + .Build(); + + const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB"); + auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f); + + TableFlattenerOptions options; + options.sparse_entries = SparseEntriesMode::Forced; std::string no_sparse_contents; ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents)); @@ -399,7 +421,7 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithSdkVersionNotSet) { auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f); TableFlattenerOptions options; - options.use_sparse_entries = true; + options.sparse_entries = SparseEntriesMode::Enabled; std::string no_sparse_contents; ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents)); @@ -442,7 +464,7 @@ TEST_F(TableFlattenerTest, DoNotUseSparseEntryForDenseConfig) { auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.80f); TableFlattenerOptions options; - options.use_sparse_entries = true; + options.sparse_entries = SparseEntriesMode::Enabled; std::string no_sparse_contents; ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));