Expose '--force-sparse-encoding' flag for 'optimize' and 'convert' commands.

This allows to use sparse encoding for APKs that do not specify minSdk
version inside. This is common for split APKs generated by bundletool
when Android App Bundle (AAB) is split into multiple APKs.

Bug: b/223394605
Test: TableFlattener_test
Change-Id: I2a99a611f855cffbcea20018d869e1eb54d1b3d5
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index cf58f98..aeedf8b 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -395,6 +395,12 @@
                                     << 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 2cdb0c8..6c09649 100644
--- a/tools/aapt2/cmd/Convert.h
+++ b/tools/aapt2/cmd/Convert.h
@@ -34,10 +34,18 @@
     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 @@
   std::string output_path_;
   std::optional<std::string> 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 74a8bbd..116dcd6 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -2419,6 +2419,9 @@
                 << "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 a5623cb..6c8d143 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -69,6 +69,7 @@
   bool no_resource_removal = false;
   bool no_xml_namespaces = false;
   bool do_not_compress_anything = false;
+  bool use_sparse_encoding = false;
   std::unordered_set<std::string> extensions_to_not_compress;
   std::optional<std::regex> regex_to_not_compress;
 
@@ -156,8 +157,8 @@
             "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 4033983..e37c2d4 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -427,6 +427,13 @@
     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 ff63e8d..10b84b0c 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -61,6 +61,12 @@
 
   // Path to the output map of original resource paths to shortened paths.
   std::optional<std::string> 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 @@
         "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 "