Let blaze obfuscate manual main_dex_list according to proguard map.

PiperOrigin-RevId: 199529974
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
index 96d4fb3..f00416a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
@@ -1044,6 +1044,9 @@
                 proguardedJar,
                 mainDexProguardSpec,
                 proguardOutputMap);
+      } else if (multidexMode == MultidexMode.MANUAL_MAIN_DEX) {
+        mainDexList =
+            transformDexListThroughProguardMapAction(ruleContext, proguardOutputMap, mainDexList);
       }
 
       Artifact classesDex = getDxArtifact(ruleContext, "classes.dex.zip");
@@ -1791,6 +1794,32 @@
     return mainDexList;
   }
 
+  /** Transforms manual main_dex_list through proguard obfuscation map. */
+  static Artifact transformDexListThroughProguardMapAction(
+      RuleContext ruleContext, @Nullable Artifact proguardOutputMap, Artifact mainDexList)
+      throws InterruptedException {
+    if (proguardOutputMap == null) {
+      return mainDexList;
+    }
+    Artifact obfuscatedMainDexList = AndroidBinary.getDxArtifact(ruleContext, "main_dex_list.txt");
+    SpawnAction.Builder actionBuilder =
+        new SpawnAction.Builder()
+            .setMnemonic("MainDexProguardClasses")
+            .setProgressMessage("Obfuscating main dex classes list")
+            .setExecutable(ruleContext.getExecutablePrerequisite("$dex_list_obfuscator", Mode.HOST))
+            .addInput(mainDexList)
+            .addInput(proguardOutputMap)
+            .addOutput(obfuscatedMainDexList)
+            .addCommandLine(
+                CustomCommandLine.builder()
+                    .addExecPath("--input", mainDexList)
+                    .addExecPath("--output", obfuscatedMainDexList)
+                    .addExecPath("--obfuscation_map", proguardOutputMap)
+                    .build());
+    ruleContext.registerAction(actionBuilder.build(ruleContext));
+    return obfuscatedMainDexList;
+  }
+
   public static Artifact createMainDexProguardSpec(Label label, ActionConstructionContext context) {
     return ProguardHelper.getProguardConfigArtifact(label, context, "main_dex");
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
index 1c5d0116..a9264b0 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
@@ -944,6 +944,11 @@
           .add(attr("proguard_apply_dictionary", LABEL).legacyAllowAnyFileType())
           .add(attr(":extra_proguard_specs", LABEL_LIST).value(JavaSemantics.EXTRA_PROGUARD_SPECS))
           .add(
+              attr("$dex_list_obfuscator", LABEL)
+                  .cfg(HostTransition.INSTANCE)
+                  .exec()
+                  .value(env.getToolsLabel("//tools/android:dex_list_obfuscator")))
+          .add(
               attr(":bytecode_optimizers", LABEL_LIST)
                   .cfg(HostTransition.INSTANCE)
                   .value(JavaSemantics.BYTECODE_OPTIMIZERS))
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
index af2220d..3b75799 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
@@ -272,7 +272,8 @@
         .add("sh_binary(name = 'instrumentation_test_check', srcs = ['empty.sh'])")
         .add("package_group(name = 'android_device_whitelist', packages = ['//...'])")
         .add("package_group(name = 'export_deps_whitelist', packages = ['//...'])")
-        .add("android_tools_defaults_jar(name = 'android_jar')");
+        .add("android_tools_defaults_jar(name = 'android_jar')")
+        .add("sh_binary(name = 'dex_list_obfuscator', srcs = ['empty.sh'])");
 
     return androidBuildContents.build();
   }
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 f894e4d..6c17d94 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
@@ -180,6 +180,34 @@
   }
 
   @Test
+  public void testMainDexListObfuscation() throws Exception {
+    useConfiguration("--noincremental_dexing");
+    scratch.file("/java/a/list.txt");
+    ConfiguredTarget ct =
+        scratchConfiguredTarget(
+            "java/a",
+            "a",
+            "android_binary(",
+            "    name = 'a',",
+            "    srcs = ['A.java'],",
+            "    manifest = 'AndroidManifest.xml',",
+            "    multidex = 'manual_main_dex',",
+            "    proguard_generate_mapping = 1,",
+            "    main_dex_list = 'list.txt')");
+
+    Artifact obfuscatedDexList =
+        artifactByPath(
+            ImmutableList.of(getCompressedUnsignedApk(ct)),
+            ".apk",
+            ".dex.zip",
+            ".dex.zip",
+            "main_dex_list.txt");
+    List<String> args = getGeneratingSpawnActionArgs(obfuscatedDexList);
+    assertThat(args.get(0)).contains("dex_list_obfuscator");
+    MoreAsserts.assertContainsSublist(args, "--input", "java/a/list.txt");
+  }
+
+  @Test
   public void testNonLegacyNativeDepsDoesNotPolluteDexSharding() throws Exception {
     scratch.file("java/a/BUILD",
         "android_binary(name = 'a',",
diff --git a/tools/android/BUILD.tools b/tools/android/BUILD.tools
index 4828323..8def828 100644
--- a/tools/android/BUILD.tools
+++ b/tools/android/BUILD.tools
@@ -444,3 +444,9 @@
     name = "export_deps_whitelist",
     packages = ["//..."],
 )
+
+sh_binary(
+    name = "dex_list_obfuscator",
+    srcs = ["dex_list_obfuscator.sh"],
+    visibility = ["//visibility:public"],
+)
\ No newline at end of file
diff --git a/tools/android/dex_list_obfuscator.sh b/tools/android/dex_list_obfuscator.sh
new file mode 100644
index 0000000..ce7e526
--- /dev/null
+++ b/tools/android/dex_list_obfuscator.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+# Copyright 2018 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Dummy dex list obfuscator doing nothing
+# Should be updated to contain an app, that can obfuscate main dex keep list
+# according to the proguard map.
+
+set -eu
+input=
+output=
+while [[ "$#" -gt 0 ]]; do
+  arg="$1"; shift;
+  case "${arg}" in
+    --input) input="$1"; shift ;;
+    --output) output="$1"; shift ;;
+    ---obfuscation_map=*) shift ;;
+    *) echo "Unknown flag: ${arg}"; exit 1 ;;
+  esac
+done
+
+echo "WARNING: This is just no-op version of the list obfuscator."
+echo "It is invoked, because main_dex_list and proguard were both used."
+echo "If proguard obfuscates a class, it will not be kept in the main dex even"
+echo "if the original name was in the main_dex_list."
+echo "The main_dex_list (provided as --input) should be obfuscated using the"
+echo "map provided as --obfuscation_map parameter."
+echo "If no obfuscation of main dex classes is performed, then noop is OK."
+
+cp $input $output