Build android_binary APKs with Singlejar by default.

This will improve android_binary build times and allow Bazel to remove a
dependency on our forked version of the deprecated ApkBuilderMain.

RELNOTES: None

PiperOrigin-RevId: 151749709
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java
index 019fe5c..2cd2cee 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java
@@ -403,7 +403,7 @@
     public ApkSigningMethod apkSigningMethod;
 
     @Option(name = "use_singlejar_apkbuilder",
-        defaultValue = "false",
+        defaultValue = "true",
         category = "undocumented",
         help = "Build Android APKs with SingleJar.")
     public boolean useSingleJarApkBuilder;
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
index 5a941bc..1da1347 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
@@ -113,7 +113,7 @@
         "    multidex = 'legacy',",
         "    main_dex_proguard_specs = ['a.spec'])");
 
-    Artifact intermediateJar = artifactByPath(getFilesToBuild(ct),
+    Artifact intermediateJar = artifactByPath(ImmutableList.of(getCompressedUnsignedApk(ct)),
         ".apk", ".dex.zip", ".dex.zip", "main_dex_list.txt", "_intermediate.jar");
     List<String> args = getGeneratingSpawnAction(intermediateJar).getArguments();
     MoreAsserts.assertContainsSublist(args, "-include", "java/a/a.spec");
@@ -131,8 +131,9 @@
         "cc_library(name = 'cc',",
         "           srcs = ['cc.cc'])");
 
-    Artifact jarShard = artifactByPath(getFilesToBuild(getConfiguredTarget("//java/a:a")),
-        "a.apk", ".apk", ".apk", "classes.dex.zip", "shard1.dex.zip", "shard1.jar");
+    Artifact jarShard = artifactByPath(
+        ImmutableList.of(getCompressedUnsignedApk(getConfiguredTarget("//java/a:a"))),
+        ".apk", "classes.dex.zip", "shard1.dex.zip", "shard1.jar");
     Iterable<Artifact> shardInputs = getGeneratingAction(jarShard).getInputs();
     assertThat(getFirstArtifactEndingWith(shardInputs, ".txt")).isNull();
   }
@@ -683,7 +684,7 @@
     Action jackDexAction =
         getGeneratingAction(
             artifactByPath(
-                getFilesToBuild(topTarget), "top.apk", ".apk", ".apk", "classes.dex.zip"));
+                ImmutableList.of(getCompressedUnsignedApk(topTarget)), ".apk", "classes.dex.zip"));
     Iterable<String> jackDexInputs = ActionsTestUtil.baseArtifactNames(jackDexAction.getInputs());
     assertThat(jackDexInputs).containsAllOf("libtop.jack", "libdep.jack");
     assertThat(jackDexInputs).doesNotContain("libneverlink.jack");
@@ -714,7 +715,7 @@
     Action jackDexAction =
         getGeneratingAction(
             artifactByPath(
-                getFilesToBuild(topTarget), "top.apk", ".apk", ".apk", "classes.dex.zip"));
+                ImmutableList.of(getCompressedUnsignedApk(topTarget)), ".apk", "classes.dex.zip"));
     Iterable<String> jackDexInputs = ActionsTestUtil.baseArtifactNames(jackDexAction.getInputs());
     assertThat(jackDexInputs).containsAllOf("transitive.pro_valid", "direct.pro");
   }
@@ -741,7 +742,7 @@
     Action jackDexAction =
         getGeneratingAction(
             artifactByPath(
-                getFilesToBuild(topTarget), "top.apk", ".apk", ".apk", "classes.dex.zip"));
+                ImmutableList.of(getCompressedUnsignedApk(topTarget)), ".apk", "classes.dex.zip"));
     Iterable<String> jackDexInputs = ActionsTestUtil.baseArtifactNames(jackDexAction.getInputs());
     assertThat(jackDexInputs).doesNotContain("transitive.pro_valid");
   }
@@ -793,8 +794,27 @@
     Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary));
     assertThat(getFirstArtifactEndingWith(artifacts, "signed_hello.apk")).isNull();
     SpawnAction unsignedApkAction = (SpawnAction) actionsTestUtil()
-        .getActionForArtifactEndingWith(artifacts, "hello_unsigned.apk");
-    assertThat(unsignedApkAction.getCommandFilename()).endsWith("sdk/apkbuilder");
+        .getActionForArtifactEndingWith(artifacts, "/hello_unsigned.apk");
+    assertTrue(
+        Iterables.any(
+            unsignedApkAction.getInputs(),
+            new Predicate<Artifact>() {
+              @Override
+              public boolean apply(Artifact artifact) {
+                return artifact.getFilename().equals("SingleJar_deploy.jar");
+              }
+            }));
+    SpawnAction compressedUnsignedApkAction = (SpawnAction) actionsTestUtil()
+        .getActionForArtifactEndingWith(artifacts, "compressed_hello_unsigned.apk");
+    assertTrue(
+        Iterables.any(
+            compressedUnsignedApkAction.getInputs(),
+            new Predicate<Artifact>() {
+              @Override
+              public boolean apply(Artifact artifact) {
+                return artifact.getFilename().equals("SingleJar_deploy.jar");
+              }
+            }));
     SpawnAction zipalignAction = (SpawnAction) actionsTestUtil()
         .getActionForArtifactEndingWith(artifacts, "zipaligned_hello.apk");
     assertThat(zipalignAction.getCommandFilename()).endsWith("sdk/zipalign");
@@ -1830,8 +1850,7 @@
     Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(target));
     Artifact shard1 = getFirstArtifactEndingWith(artifacts, "shard1.jar");
     Artifact shard2 = getFirstArtifactEndingWith(artifacts, "shard2.jar");
-    Artifact resourceJar = getFirstArtifactEndingWith(artifacts,
-        "java_resources.jar");
+    Artifact resourceJar = getFirstArtifactEndingWith(artifacts, "/java_resources.jar");
     expectedArguments.add("--output_jar");
     expectedArguments.add(shard1.getExecPathString());
     expectedArguments.add("--output_jar");
@@ -1881,7 +1900,7 @@
 
     // Verify that dex compilation is followed by the correct merge operation
     Action apkAction = getGeneratingAction(getFirstArtifactEndingWith(
-        getFilesToBuild(target), "a_unsigned.apk"));
+        actionsTestUtil().artifactClosureOf(getFilesToBuild(target)), "compressed_a_unsigned.apk"));
     Action mergeAction = getGeneratingAction(getFirstArtifactEndingWith(
         apkAction.getInputs(), "classes.dex.zip"));
     Iterable<Artifact> dexShards = Iterables.filter(
@@ -2143,7 +2162,7 @@
     useConfiguration(
         "--android_sdk=//sdk:sdk", "--experimental_android_use_singlejar_for_multidex");
     ConfiguredTarget a = getConfiguredTarget("//java/a:a");
-    Artifact intermediateJar = artifactByPath(getFilesToBuild(a),
+    Artifact intermediateJar = artifactByPath(ImmutableList.of(getCompressedUnsignedApk(a)),
         ".apk", ".dex.zip", ".dex.zip", "main_dex_list.txt", "_intermediate.jar");
     List<String> args = getGeneratingSpawnAction(intermediateJar).getArguments();
     assertContainsSublist(
@@ -2187,7 +2206,7 @@
     useConfiguration(
         "--android_sdk=//sdk:sdk", "--experimental_android_use_singlejar_for_multidex");
     ConfiguredTarget a = getConfiguredTarget("//java/a:a");
-    Artifact intermediateJar = artifactByPath(getFilesToBuild(a),
+    Artifact intermediateJar = artifactByPath(ImmutableList.of(getCompressedUnsignedApk(a)),
         ".apk", ".dex.zip", ".dex.zip", "main_dex_list.txt", "_intermediate.jar");
     List<String> args = getGeneratingSpawnAction(intermediateJar).getArguments();
     assertEquals(-1,
@@ -2209,7 +2228,7 @@
         "    manifest = 'AndroidManifest.xml',",
         "    multidex = 'legacy',)");
     ConfiguredTarget a = getConfiguredTarget("//java/foo:abin");
-    Artifact intermediateJar = artifactByPath(getFilesToBuild(a),
+    Artifact intermediateJar = artifactByPath(ImmutableList.of(getCompressedUnsignedApk(a)),
         ".apk", ".dex.zip", ".dex.zip", "main_dex_list.txt", "_intermediate.jar");
     List<String> args = getGeneratingSpawnAction(intermediateJar).getArguments();
     MoreAsserts.assertDoesNotContainSublist(
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBuildViewTestCase.java
index 504ae5d..fe53a86 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBuildViewTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBuildViewTestCase.java
@@ -18,7 +18,6 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.getFirstArtifactEndingWith;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
@@ -29,6 +28,7 @@
 import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.FileProvider;
 import com.google.devtools.build.lib.analysis.OutputFileConfiguredTarget;
 import com.google.devtools.build.lib.analysis.actions.SpawnAction;
 import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
@@ -46,10 +46,9 @@
 /** Common methods shared between Android related {@link BuildViewTestCase}s. */
 public abstract class AndroidBuildViewTestCase extends BuildViewTestCase {
   protected Iterable<Artifact> getNativeLibrariesInApk(ConfiguredTarget target) {
-    Action unsignedApkAction = actionsTestUtil().getActionForArtifactEndingWith(
-        getFilesToBuild(target), "_unsigned.apk");
+    SpawnAction compressedUnsignedApkaction = getCompressedUnsignedApkAction(target);
     ImmutableList.Builder<Artifact> result = ImmutableList.builder();
-    for (Artifact output : unsignedApkAction.getInputs()) {
+    for (Artifact output : compressedUnsignedApkaction.getInputs()) {
       if (!output.getExecPathString().endsWith(".so")) {
         continue;
       }
@@ -84,16 +83,33 @@
     return args.get(args.indexOf(flag) + 1);
   }
 
+  /**
+   * The unsigned APK is created in two actions. The first action adds everything that needs to be
+   * unconditionally compressed in the APK. The second action adds everything else, preserving their
+   * compression.
+   */
+  protected Artifact getCompressedUnsignedApk(ConfiguredTarget target) {
+    return artifactByPath(
+        actionsTestUtil().artifactClosureOf(getFinalUnsignedApk(target)),
+        "_unsigned.apk",
+        "_unsigned.apk");
+  }
+
+  protected SpawnAction getCompressedUnsignedApkAction(ConfiguredTarget target) {
+    return getGeneratingSpawnAction(getCompressedUnsignedApk(target));
+  }
+
+  protected Artifact getFinalUnsignedApk(ConfiguredTarget target) {
+    return getFirstArtifactEndingWith(
+        target.getProvider(FileProvider.class).getFilesToBuild(), "_unsigned.apk");
+  }
+
   protected Artifact getResourceApk(ConfiguredTarget target) {
-    Action unsignedApkAction = getGeneratingAction(
-        getFirstArtifactEndingWith(getFilesToBuild(target), "_unsigned.apk"));
-    Artifact binaryApk =
-        getFirstArtifactEndingWith(unsignedApkAction.getInputs(), ".apk");
     Artifact resourceApk =
-        getFirstArtifactEndingWith(unsignedApkAction.getInputs(), ".ap_");
-    assertTrue((binaryApk == null && resourceApk != null)
-        || (binaryApk != null && resourceApk == null));
-    return binaryApk == null ? resourceApk : binaryApk;
+        getFirstArtifactEndingWith(
+            getGeneratingAction(getFinalUnsignedApk(target)).getInputs(), ".ap_");
+    assertThat(resourceApk).isNotNull();
+    return resourceApk;
   }
 
   protected void assertProguardUsed(ConfiguredTarget binary) {