Enable the AndroidResourceProcessingAction to accept splits.

These splits will be passed through to aapt, which will create
additional resource packages (and reduce the set found in the base
ap_) as a result.

--
MOS_MIGRATED_REVID=123995947
diff --git a/src/tools/android/java/com/google/devtools/build/android/AaptCommandBuilder.java b/src/tools/android/java/com/google/devtools/build/android/AaptCommandBuilder.java
index d1f5a54..b3c6747 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AaptCommandBuilder.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AaptCommandBuilder.java
@@ -13,6 +13,7 @@
 // limitations under the License.
 package com.google.devtools.build.android;
 
+import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 
@@ -20,33 +21,53 @@
 import com.android.sdklib.repository.FullRevision;
 
 import java.nio.file.Path;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
 import javax.annotation.Nullable;
 
+/**
+ * Builder for AAPT command lines, with support for making flags conditional on build tools version
+ * and variant type.
+ */
 class AaptCommandBuilder {
-  private final Path aapt;
-  private final String command;
-  private final List<String> flags = new ArrayList<>();
-  private final FullRevision buildToolsVersion;
-  private final Type variantType;
+  private final ImmutableList.Builder<String> flags = new ImmutableList.Builder<>();
+  private FullRevision buildToolsVersion;
+  private Type variantType;
 
-  AaptCommandBuilder(
-      Path aapt, @Nullable FullRevision buildToolsVersion, Type variantType, String command) {
-    this.aapt = aapt;
-    this.buildToolsVersion = buildToolsVersion;
-    this.variantType = variantType;
-    this.command = command;
+  public AaptCommandBuilder(Path aapt) {
+    flags.add(aapt.toString());
   }
 
-  AaptCommandBuilder add(String flag) {
+  /** Sets the build tools version to be used for {@link #whenVersionIsAtLeast}. */
+  AaptCommandBuilder forBuildToolsVersion(@Nullable FullRevision buildToolsVersion) {
+    Preconditions.checkState(
+        this.buildToolsVersion == null, "A build tools version was already specified.");
+    this.buildToolsVersion = buildToolsVersion;
+    return this;
+  }
+
+  /** Sets the variant type to be used for {@link #whenVariantIs}. */
+  AaptCommandBuilder forVariantType(Type variantType) {
+    Preconditions.checkNotNull(variantType);
+    Preconditions.checkState(this.variantType == null, "A variant type was already specified.");
+    this.variantType = variantType;
+    return this;
+  }
+
+  /** Adds a single flag to the builder. */
+  public AaptCommandBuilder add(String flag) {
     flags.add(flag);
     return this;
   }
 
-  AaptCommandBuilder add(String flag, @Nullable String value) {
+  /**
+   * Adds a flag to the builder, along with a string value. The two will be added as different words
+   * in the final command line. If the value is {@code null}, neither the flag nor the value will
+   * be added.
+   */
+  public AaptCommandBuilder add(String flag, @Nullable String value) {
+    Preconditions.checkNotNull(flag);
     if (!Strings.isNullOrEmpty(value)) {
       flags.add(flag);
       flags.add(value);
@@ -54,70 +75,168 @@
     return this;
   }
 
-  AaptCommandBuilder add(String flag, @Nullable Path path) {
+  /**
+   * Adds a flag to the builder, along with a path value. The path will be converted to a string
+   * using {@code toString}, then the flag and the path will be added to the final command line as
+   * different words. If the value is {@code null}, neither the flag nor the path will be added.
+   *
+   * @see #add(String,String)
+   */
+  public AaptCommandBuilder add(String flag, @Nullable Path path) {
+    Preconditions.checkNotNull(flag);
     if (path != null) {
       add(flag, path.toString());
     }
     return this;
   }
 
-  AaptCommandBuilder addRepeated(String flag, Collection<String> values) {
+  /**
+   * Adds a flag to the builder multiple times, once for each value in the given collection.
+   * {@code null} values will be skipped. If the collection is empty, nothing will be added.
+   * The values will be added in the source collection's iteration order.
+   *
+   * <p>ex. If {@code flag} is {@code "-0"} and {@code values} contains the values
+   * {@code "png"}, {@code null}, and {@code "gif"}, then four words will be added to the final
+   * command line: {@code "-0", "png", "-0", "gif"}.
+   */
+  public AaptCommandBuilder addRepeated(String flag, Collection<String> values) {
+    Preconditions.checkNotNull(flag);
     for (String value : values) {
       add(flag, value);
     }
     return this;
   }
 
-
-  AaptCommandBuilder maybeAdd(String flag, boolean condition) {
+  /** Adds the next flag to the builder only if the condition is true. */
+  public ConditionalAaptCommandBuilder when(boolean condition) {
     if (condition) {
-      add(flag);
+      return new SuccessfulConditionCommandBuilder(this);
+    } else {
+      return new FailedConditionCommandBuilder(this);
     }
-    return this;
   }
 
-  AaptCommandBuilder maybeAdd(String flag, Path directory, boolean condition) {
-    if (condition) {
-      add(flag, directory);
-    }
-    return this;
+  /** Adds the next flag to the builder only if the variant type is the passed-in type. */
+  public ConditionalAaptCommandBuilder whenVariantIs(Type type) {
+    Preconditions.checkNotNull(type);
+    return when(variantType == type);
   }
 
-  AaptCommandBuilder maybeAdd(String flag, @Nullable Path path, FullRevision requiredVersion) {
-    if (buildToolsVersion == null || buildToolsVersion.compareTo(requiredVersion) >= 0) {
-      add(flag, path);
-    }
-    return this;
+  /**
+   * Adds the next flag to the builder only if the build tools version is unspecified or is
+   * greater than or equal to the given version.
+   */
+  public ConditionalAaptCommandBuilder whenVersionIsAtLeast(FullRevision requiredVersion) {
+    Preconditions.checkNotNull(requiredVersion);
+    return when(buildToolsVersion == null || buildToolsVersion.compareTo(requiredVersion) >= 0);
   }
 
-  AaptCommandBuilder maybeAdd(String flag, FullRevision requiredVersion) {
-    if (buildToolsVersion == null || buildToolsVersion.compareTo(requiredVersion) >= 0) {
-      add(flag);
-    }
-    return this;
+  /** Assembles the full command line as a list. */
+  public List<String> build() {
+    return flags.build();
   }
 
-  AaptCommandBuilder maybeAdd(String flag, Type variant) {
-    if (variantType == variant) {
-      add(flag);
-    }
-    return this;
+  /**
+   * Wrapper for potentially adding flags to an AaptCommandBuilder based on a conditional.
+   */
+  public interface ConditionalAaptCommandBuilder {
+    /**
+     * Adds a single flag to the builder if the condition was true.
+     *
+     * @see AaptCommandBuilder#add(String)
+     */
+    AaptCommandBuilder thenAdd(String flag);
+
+    /**
+     * Adds a single flag and associated string value to the builder if the value is non-null and
+     * the condition was true.
+     *
+     * @see AaptCommandBuilder#add(String,String)
+     */
+    AaptCommandBuilder thenAdd(String flag, @Nullable String value);
+
+    /**
+     * Adds a single flag and associated path value to the builder if the value is non-null and the
+     * condition was true.
+     *
+     * @see AaptCommandBuilder#add(String,Path)
+     */
+    AaptCommandBuilder thenAdd(String flag, @Nullable Path value);
+
+    /**
+     * Adds the values in the collection to the builder, each preceded by the given flag,
+     * if the collection was non-empty and the condition was true.
+     *
+     * @see AaptCommandBuilder#addRepeated(String,Collection<String>)
+     */
+    AaptCommandBuilder thenAddRepeated(String flag, Collection<String> values);
   }
 
-  AaptCommandBuilder maybeAdd(String flag, @Nullable String value, Type variant) {
-    if (variantType == variant) {
-      add(flag, value);
+  /**
+   * Forwarding implementation of ConditionalAaptCommandBuilder returned when a condition is true.
+   */
+  private static class SuccessfulConditionCommandBuilder implements ConditionalAaptCommandBuilder {
+    private final AaptCommandBuilder originalCommandBuilder;
+
+    public SuccessfulConditionCommandBuilder(AaptCommandBuilder originalCommandBuilder) {
+      this.originalCommandBuilder = originalCommandBuilder;
     }
-    return this;
+
+    @Override
+    public AaptCommandBuilder thenAdd(String flag) {
+      return originalCommandBuilder.add(flag);
+    }
+
+    @Override
+    public AaptCommandBuilder thenAdd(String flag, @Nullable String value) {
+      return originalCommandBuilder.add(flag, value);
+    }
+
+    @Override
+    public AaptCommandBuilder thenAdd(String flag, @Nullable Path value) {
+      return originalCommandBuilder.add(flag, value);
+    }
+
+    @Override
+    public AaptCommandBuilder thenAddRepeated(String flag, Collection<String> values) {
+      return originalCommandBuilder.addRepeated(flag, values);
+    }
   }
 
-  List<String> build() {
-    return ImmutableList
-        .<String>builder()
-        .add(aapt.toString())
-        .add(command)
-        .addAll(flags)
-        .build();
+  /**
+   * Null implementation of ConditionalAaptCommandBuilder returned when a condition is false.
+   */
+  private static class FailedConditionCommandBuilder implements ConditionalAaptCommandBuilder {
+    private final AaptCommandBuilder originalCommandBuilder;
+
+    public FailedConditionCommandBuilder(AaptCommandBuilder originalCommandBuilder) {
+      this.originalCommandBuilder = originalCommandBuilder;
+    }
+
+    @Override
+    public AaptCommandBuilder thenAdd(String flag) {
+      Preconditions.checkNotNull(flag);
+      return originalCommandBuilder;
+    }
+
+    @Override
+    public AaptCommandBuilder thenAdd(String flag, @Nullable String value) {
+      Preconditions.checkNotNull(flag);
+      return originalCommandBuilder;
+    }
+
+    @Override
+    public AaptCommandBuilder thenAdd(String flag, @Nullable Path value) {
+      Preconditions.checkNotNull(flag);
+      return originalCommandBuilder;
+    }
+
+    @Override
+    public AaptCommandBuilder thenAddRepeated(String flag, Collection<String> values) {
+      Preconditions.checkNotNull(flag);
+      Preconditions.checkNotNull(values);
+      return originalCommandBuilder;
+    }
   }
 }
 
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
index 29112c9..373f161 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
@@ -174,7 +174,7 @@
         defaultValue = "",
         converter = CommaSeparatedOptionListConverter.class,
         category = "config",
-        help = "A list densities to filter the resource drawables by.")
+        help = "A list of densities to filter the resource drawables by.")
     public List<String> densities;
 
     @Option(name = "packageForR",
@@ -287,6 +287,7 @@
           options.packageForR,
           new FlagAaptOptions(aaptConfigOptions),
           aaptConfigOptions.resourceConfigs,
+          aaptConfigOptions.splits,
           processedManifestData,
           data,
           generatedSources,
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java
index 03709b7..33aa697 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java
@@ -25,6 +25,7 @@
 import com.google.common.collect.Multimap;
 import com.google.devtools.build.android.Converters.ExistingPathConverter;
 import com.google.devtools.build.android.Converters.FullRevisionConverter;
+import com.google.devtools.common.options.Converters.ColonSeparatedOptionListConverter;
 import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter;
 import com.google.devtools.common.options.Option;
 import com.google.devtools.common.options.OptionsBase;
@@ -169,6 +170,14 @@
         category = "config",
         help = "A list of resource config filters to pass to aapt.")
     public List<String> resourceConfigs;
+
+    @Option(name = "splits",
+        defaultValue = "",
+        converter = ColonSeparatedOptionListConverter.class,
+        category = "config",
+        help = "A list of splits to pass to aapt, separated by colons."
+            + " Each split is a list of qualifiers separated by commas.")
+    public List<String> splits;
   }
 
   /**
@@ -326,6 +335,7 @@
       String customPackageForR,
       AaptOptions aaptOptions,
       Collection<String> resourceConfigs,
+      Collection<String> splits,
       MergedAndroidData primaryData,
       List<DependencyAndroidData> dependencyData,
       Path sourceOut,
@@ -348,43 +358,49 @@
     }
 
     AaptCommandBuilder commandBuilder =
-        new AaptCommandBuilder(aapt, buildToolsVersion, variantType, "package")
-            // If the logger is verbose, set aapt to be verbose
-        .maybeAdd("-v", stdLogger.getLevel() == StdLogger.Level.VERBOSE)
+        new AaptCommandBuilder(aapt)
+        .forBuildToolsVersion(buildToolsVersion)
+        .forVariantType(variantType)
+        // first argument is the command to be executed, "package"
+        .add("package")
+        // If the logger is verbose, set aapt to be verbose
+        .when(stdLogger.getLevel() == StdLogger.Level.VERBOSE).thenAdd("-v")
         // Overwrite existing files, if they exist.
         .add("-f")
         // Resources are precrunched in the merge process.
         .add("--no-crunch")
         // Do not automatically generate versioned copies of vector XML resources.
-        .maybeAdd("--no-version-vectors", new FullRevision(23))
+        .whenVersionIsAtLeast(new FullRevision(23)).thenAdd("--no-version-vectors")
         // Add the android.jar as a base input.
         .add("-I", androidJar)
         // Add the manifest for validation.
         .add("-M", androidManifest.toAbsolutePath())
         // Maybe add the resources if they exist
-        .maybeAdd("-S", resourceDir, Files.isDirectory(resourceDir))
+        .when(Files.isDirectory(resourceDir)).thenAdd("-S", resourceDir)
         // Maybe add the assets if they exist
-        .maybeAdd("-A", assetsDir, Files.isDirectory(assetsDir))
+        .when(Files.isDirectory(assetsDir)).thenAdd("-A", assetsDir)
         // Outputs
-        .maybeAdd("-m", sourceOut != null)
-        .maybeAdd("-J", prepareOutputPath(sourceOut), sourceOut != null)
-        .maybeAdd("--output-text-symbols", prepareOutputPath(sourceOut), sourceOut != null)
-        .maybeAdd("-F", packageOut, packageOut != null)
+        .when(sourceOut != null).thenAdd("-m")
+        .add("-J", prepareOutputPath(sourceOut))
+        .add("--output-text-symbols", prepareOutputPath(sourceOut))
+        .add("-F", packageOut)
         .add("-G", proguardOut)
-        .maybeAdd("-D", mainDexProguardOut, new FullRevision(24))
+        .whenVersionIsAtLeast(new FullRevision(24)).thenAdd("-D", mainDexProguardOut)
         .add("-P", publicResourcesOut)
-        .maybeAdd("--debug-mode", debug)
+        .when(debug).thenAdd("--debug-mode")
         .add("--custom-package", customPackageForR)
         // If it is a library, do not generate final java ids.
-        .maybeAdd("--non-constant-id", VariantConfiguration.Type.LIBRARY)
+        .whenVariantIs(VariantConfiguration.Type.LIBRARY).thenAdd("--non-constant-id")
         .add("--ignore-assets", aaptOptions.getIgnoreAssets())
-        .maybeAdd("--error-on-missing-config-entry", aaptOptions.getFailOnMissingConfigEntry())
+        .when(aaptOptions.getFailOnMissingConfigEntry()).thenAdd("--error-on-missing-config-entry")
         // Never compress apks.
         .add("-0", "apk")
         // Add custom no-compress extensions.
         .addRepeated("-0", aaptOptions.getNoCompress())
         // Filter by resource configuration type.
-        .add("-c", Joiner.on(',').join(resourceConfigs));
+        .add("-c", Joiner.on(',').join(resourceConfigs))
+        // Split APKs if any splits were specified.
+        .whenVersionIsAtLeast(new FullRevision(23)).thenAddRepeated("--split", splits);
 
     new CommandLineRunner(stdLogger).runCmdLine(commandBuilder.build(), null);
 
diff --git a/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java b/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java
index 86ae74d..69317a1 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java
@@ -211,6 +211,7 @@
           null /* packageForR */,
           new FlagAaptOptions(aaptConfigOptions),
           aaptConfigOptions.resourceConfigs,
+          ImmutableList.<String>of() /* splits */,
           new MergedAndroidData(
               shrunkResources, resourceFiles.resolve("assets"), options.primaryManifest),
           ImmutableList.<DependencyAndroidData>of() /* libraries */,