Merge "Allow specifying R8 release version for run_on_as_app.py"
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
similarity index 100%
rename from infra/config/cr-buildbucket.cfg
rename to infra/config/global/cr-buildbucket.cfg
diff --git a/infra/config/luci-logdog.cfg b/infra/config/global/luci-logdog.cfg
similarity index 100%
rename from infra/config/luci-logdog.cfg
rename to infra/config/global/luci-logdog.cfg
diff --git a/infra/config/luci-milo.cfg b/infra/config/global/luci-milo.cfg
similarity index 100%
rename from infra/config/luci-milo.cfg
rename to infra/config/global/luci-milo.cfg
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
similarity index 100%
rename from infra/config/luci-scheduler.cfg
rename to infra/config/global/luci-scheduler.cfg
diff --git a/infra/config/project.cfg b/infra/config/global/project.cfg
similarity index 100%
rename from infra/config/project.cfg
rename to infra/config/global/project.cfg
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 6a423ec..4755dca 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -38,6 +38,7 @@
 import com.android.tools.r8.naming.ProguardMapSupplier;
 import com.android.tools.r8.naming.SeedMapper;
 import com.android.tools.r8.naming.SourceFileRewriter;
+import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
 import com.android.tools.r8.optimize.ClassAndMemberPublicizer;
 import com.android.tools.r8.optimize.MemberRebindingAnalysis;
 import com.android.tools.r8.optimize.VisibilityBridgeRemover;
@@ -631,6 +632,10 @@
         namingLens = new Minifier(appView.withLiveness(), rootSet, desugaredCallSites).run(timing);
         timing.end();
       } else {
+        if (appView.appInfo().hasLiveness()) {
+          // TODO(124726014): Rewrite signature annotations in lens rewriting instead of here?
+          new GenericSignatureRewriter(appView.withLiveness()).run();
+        }
         namingLens = NamingLens.getIdentityLens();
       }
 
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index b59b79b..d7426fc 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -6,14 +6,11 @@
 import static com.android.tools.r8.utils.DescriptorUtils.DESCRIPTOR_PACKAGE_SEPARATOR;
 import static com.android.tools.r8.utils.DescriptorUtils.INNER_CLASS_SEPARATOR;
 import static com.android.tools.r8.utils.DescriptorUtils.getClassBinaryNameFromDescriptor;
-import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromClassBinaryName;
 import static com.android.tools.r8.utils.DescriptorUtils.getPackageBinaryNameFromJavaType;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -22,22 +19,18 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.naming.signature.GenericSignatureAction;
-import com.android.tools.r8.naming.signature.GenericSignatureParser;
-import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
 import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
-import java.lang.reflect.GenericSignatureFormatError;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -45,8 +38,6 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 class ClassNameMinifier {
@@ -71,11 +62,6 @@
 
   private final Namespace topLevelState;
 
-  private final GenericSignatureRewriter genericSignatureRewriter = new GenericSignatureRewriter();
-
-  private final GenericSignatureParser<DexType> genericSignatureParser =
-      new GenericSignatureParser<>(genericSignatureRewriter);
-
   ClassNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
     this.appView = appView;
     this.appInfo = appView.appInfo();
@@ -97,6 +83,7 @@
     // Initialize top-level naming state.
     topLevelState = new Namespace(
         getPackageBinaryNameFromJavaType(options.getProguardConfiguration().getPackagePrefix()));
+
     states.put("", topLevelState);
   }
 
@@ -141,7 +128,7 @@
     timing.end();
 
     timing.begin("rename-generic");
-    renameTypesInGenericSignatures();
+    new GenericSignatureRewriter(appView, renaming).run();
     timing.end();
 
     timing.begin("rename-arrays");
@@ -192,100 +179,6 @@
     }
   }
 
-  private void parseError(
-      DexDefinition item, Origin origin, String signature, GenericSignatureFormatError e) {
-    StringBuilder message = new StringBuilder("Invalid signature '");
-    message.append(signature);
-    message.append("' for ");
-    if (item.isDexClass()) {
-      message.append("class ");
-      message.append((item.asDexClass()).getType().toSourceString());
-    } else if (item.isDexEncodedField()) {
-      message.append("field ");
-      message.append(item.toSourceString());
-    } else {
-      assert item.isDexEncodedMethod();
-      message.append("method ");
-      message.append(item.toSourceString());
-    }
-    message.append(".\n");
-    message.append("Signature is ignored and will not be present in the output.\n");
-    message.append("Parser error: ");
-    message.append(e.getMessage());
-    reporter.warning(new StringDiagnostic(message.toString(), origin));
-  }
-
-  private void renameTypesInGenericSignatures() {
-    for (DexClass clazz : appInfo.classes()) {
-      clazz.annotations =
-          rewriteGenericSignatures(
-              clazz.annotations,
-              genericSignatureParser::parseClassSignature,
-              (signature, e) -> parseError(clazz, clazz.getOrigin(), signature, e));
-      clazz.forEachField(
-          field ->
-              field.annotations =
-                  rewriteGenericSignatures(
-                      field.annotations,
-                      genericSignatureParser::parseFieldSignature,
-                      (signature, e) -> parseError(field, clazz.getOrigin(), signature, e)));
-      clazz.forEachMethod(
-          method ->
-              method.annotations =
-                  rewriteGenericSignatures(
-                      method.annotations,
-                      genericSignatureParser::parseMethodSignature,
-                      (signature, e) -> parseError(method, clazz.getOrigin(), signature, e)));
-    }
-  }
-
-  private DexAnnotationSet rewriteGenericSignatures(
-      DexAnnotationSet annotations,
-      Consumer<String> parser,
-      BiConsumer<String, GenericSignatureFormatError> parseError) {
-    // There can be no more than one signature annotation in an annotation set.
-    final int VALID = -1;
-    int invalid = VALID;
-    for (int i = 0; i < annotations.annotations.length && invalid == VALID; i++) {
-      DexAnnotation annotation = annotations.annotations[i];
-      if (DexAnnotation.isSignatureAnnotation(annotation, appInfo.dexItemFactory)) {
-        String signature = DexAnnotation.getSignature(annotation);
-        try {
-          parser.accept(signature);
-          annotations.annotations[i] = DexAnnotation.createSignatureAnnotation(
-              genericSignatureRewriter.getRenamedSignature(),
-              appInfo.dexItemFactory);
-        } catch (GenericSignatureFormatError e) {
-          parseError.accept(signature, e);
-          invalid = i;
-        }
-      }
-    }
-
-    // Return the rewritten signatures if it was valid and could be rewritten.
-    if (invalid == VALID) {
-      return annotations;
-    }
-    // Remove invalid signature if found.
-    DexAnnotation[] prunedAnnotations =
-        new DexAnnotation[annotations.annotations.length - 1];
-    int dest = 0;
-    for (int i = 0; i < annotations.annotations.length; i++) {
-      if (i != invalid) {
-        prunedAnnotations[dest++] = annotations.annotations[i];
-      }
-    }
-    assert dest == prunedAnnotations.length;
-    return new DexAnnotationSet(prunedAnnotations);
-  }
-
-  /**
-   * Registers the given type as used.
-   * <p>
-   * When {@link #keepInnerClassStructure} is true, keeping the name of an inner class will
-   * automatically also keep the name of the outer class, as otherwise the structure would be
-   * invalidated.
-   */
   private void registerClassAsUsed(DexType type) {
     renaming.put(type, type.descriptor);
     registerPackagePrefixesAsUsed(
@@ -526,73 +419,6 @@
     }
   }
 
-  private class GenericSignatureRewriter implements GenericSignatureAction<DexType> {
-
-    private StringBuilder renamedSignature;
-
-    public String getRenamedSignature() {
-      return renamedSignature.toString();
-    }
-
-    @Override
-    public void parsedSymbol(char symbol) {
-      renamedSignature.append(symbol);
-    }
-
-    @Override
-    public void parsedIdentifier(String identifier) {
-      renamedSignature.append(identifier);
-    }
-
-    @Override
-    public DexType parsedTypeName(String name) {
-      DexType type = appInfo.dexItemFactory.createType(getDescriptorFromClassBinaryName(name));
-      type = appView.graphLense().lookupType(type);
-      DexString renamedDescriptor = renaming.getOrDefault(type, type.descriptor);
-      renamedSignature.append(getClassBinaryNameFromDescriptor(renamedDescriptor.toString()));
-      return type;
-    }
-
-    @Override
-    public DexType parsedInnerTypeName(DexType enclosingType, String name) {
-      assert enclosingType.isClassType();
-      String enclosingDescriptor = enclosingType.toDescriptorString();
-      DexType type =
-          appInfo.dexItemFactory.createType(
-              getDescriptorFromClassBinaryName(
-                  getClassBinaryNameFromDescriptor(enclosingDescriptor)
-                      + Minifier.INNER_CLASS_SEPARATOR
-                      + name));
-      String enclosingRenamedBinaryName =
-          getClassBinaryNameFromDescriptor(
-              renaming.getOrDefault(enclosingType, enclosingType.descriptor).toString());
-      type = appView.graphLense().lookupType(type);
-      DexString renamedDescriptor = renaming.get(type);
-      if (renamedDescriptor != null) {
-        // Pick the renamed inner class from the fully renamed binary name.
-        String fullRenamedBinaryName =
-            getClassBinaryNameFromDescriptor(renamedDescriptor.toString());
-        renamedSignature.append(
-            fullRenamedBinaryName.substring(enclosingRenamedBinaryName.length() + 1));
-      } else {
-        // Did not find the class - keep the inner class name as is.
-        // TODO(110085899): Warn about missing classes in signatures?
-        renamedSignature.append(name);
-      }
-      return type;
-    }
-
-    @Override
-    public void start() {
-      renamedSignature = new StringBuilder();
-    }
-
-    @Override
-    public void stop() {
-      // nothing to do
-    }
-  }
-
   /**
    * Compute parent package prefix from the given package prefix.
    *
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index af18595..be4fd39 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -34,8 +34,6 @@
 
 public class Minifier {
 
-  static final char INNER_CLASS_SEPARATOR = '$';
-
   private final AppView<AppInfoWithLiveness> appView;
   private final AppInfoWithLiveness appInfo;
   private final RootSet rootSet;
@@ -129,7 +127,7 @@
       String innerName = attribute.getInnerName().toString();
       int lengthOfPrefix = inner.length() - innerName.length();
       if (lengthOfPrefix < 0
-          || inner.lastIndexOf(INNER_CLASS_SEPARATOR, lengthOfPrefix - 1) < 0
+          || inner.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR, lengthOfPrefix - 1) < 0
           || !inner.endsWith(innerName)) {
         return lookupSimpleName(innerType, options.itemFactory);
       }
@@ -141,7 +139,7 @@
       if (inner.equals(innerTypeMapped)) {
         return attribute.getInnerName();
       }
-      int index = innerTypeMapped.lastIndexOf(INNER_CLASS_SEPARATOR);
+      int index = innerTypeMapped.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
       if (index < 0) {
         // TODO(b/120639028): Replace this by "assert false" and remove the testing option.
         // Hitting means we have converted a proper Outer$Inner relationship to an invalid one.
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
new file mode 100644
index 0000000..e55fc6e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -0,0 +1,208 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.signature;
+
+import static com.android.tools.r8.utils.DescriptorUtils.getClassBinaryNameFromDescriptor;
+import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromClassBinaryName;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinition;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.Maps;
+import java.lang.reflect.GenericSignatureFormatError;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class GenericSignatureRewriter {
+
+  private final AppView<AppInfoWithLiveness> appView;
+  private final AppInfoWithLiveness appInfo;
+  private final Map<DexType, DexString> renaming;
+  private final Reporter reporter;
+
+  public GenericSignatureRewriter(AppView<AppInfoWithLiveness> appView) {
+    this(appView, Maps.newIdentityHashMap());
+  }
+
+  public GenericSignatureRewriter(
+      AppView<AppInfoWithLiveness> appView, Map<DexType, DexString> renaming) {
+    this.appView = appView;
+    this.appInfo = appView.appInfo();
+    this.renaming = renaming;
+    this.reporter = appView.options().reporter;
+  }
+
+  public void run() {
+    final GenericSignatureCollector genericSignatureCollector = new GenericSignatureCollector();
+    final GenericSignatureParser<DexType> genericSignatureParser =
+        new GenericSignatureParser<>(genericSignatureCollector);
+
+    for (DexClass clazz : appInfo.classes()) {
+      clazz.annotations =
+          rewriteGenericSignatures(
+              clazz.annotations,
+              genericSignatureParser::parseClassSignature,
+              genericSignatureCollector::getRenamedSignature,
+              (signature, e) -> parseError(clazz, clazz.getOrigin(), signature, e));
+      clazz.forEachField(
+          field ->
+              field.annotations =
+                  rewriteGenericSignatures(
+                      field.annotations,
+                      genericSignatureParser::parseFieldSignature,
+                      genericSignatureCollector::getRenamedSignature,
+                      (signature, e) -> parseError(field, clazz.getOrigin(), signature, e)));
+      clazz.forEachMethod(
+          method ->
+              method.annotations =
+                  rewriteGenericSignatures(
+                      method.annotations,
+                      genericSignatureParser::parseMethodSignature,
+                      genericSignatureCollector::getRenamedSignature,
+                      (signature, e) -> parseError(method, clazz.getOrigin(), signature, e)));
+    }
+  }
+
+  private DexAnnotationSet rewriteGenericSignatures(
+      DexAnnotationSet annotations,
+      Consumer<String> parser,
+      Supplier<String> collector,
+      BiConsumer<String, GenericSignatureFormatError> parseError) {
+    // There can be no more than one signature annotation in an annotation set.
+    final int VALID = -1;
+    int invalid = VALID;
+    for (int i = 0; i < annotations.annotations.length && invalid == VALID; i++) {
+      DexAnnotation annotation = annotations.annotations[i];
+      if (DexAnnotation.isSignatureAnnotation(annotation, appInfo.dexItemFactory)) {
+        String signature = DexAnnotation.getSignature(annotation);
+        try {
+          parser.accept(signature);
+          annotations.annotations[i] = DexAnnotation.createSignatureAnnotation(
+              collector.get(),
+              appInfo.dexItemFactory);
+        } catch (GenericSignatureFormatError e) {
+          parseError.accept(signature, e);
+          invalid = i;
+        }
+      }
+    }
+
+    // Return the rewritten signatures if it was valid and could be rewritten.
+    if (invalid == VALID) {
+      return annotations;
+    }
+    // Remove invalid signature if found.
+    DexAnnotation[] prunedAnnotations =
+        new DexAnnotation[annotations.annotations.length - 1];
+    int dest = 0;
+    for (int i = 0; i < annotations.annotations.length; i++) {
+      if (i != invalid) {
+        prunedAnnotations[dest++] = annotations.annotations[i];
+      }
+    }
+    assert dest == prunedAnnotations.length;
+    return new DexAnnotationSet(prunedAnnotations);
+  }
+
+  private void parseError(
+      DexDefinition item, Origin origin, String signature, GenericSignatureFormatError e) {
+    StringBuilder message = new StringBuilder("Invalid signature '");
+    message.append(signature);
+    message.append("' for ");
+    if (item.isDexClass()) {
+      message.append("class ");
+      message.append((item.asDexClass()).getType().toSourceString());
+    } else if (item.isDexEncodedField()) {
+      message.append("field ");
+      message.append(item.toSourceString());
+    } else {
+      assert item.isDexEncodedMethod();
+      message.append("method ");
+      message.append(item.toSourceString());
+    }
+    message.append(".\n");
+    message.append("Signature is ignored and will not be present in the output.\n");
+    message.append("Parser error: ");
+    message.append(e.getMessage());
+    reporter.warning(new StringDiagnostic(message.toString(), origin));
+  }
+
+  private class GenericSignatureCollector implements GenericSignatureAction<DexType> {
+    private StringBuilder renamedSignature;
+
+    public String getRenamedSignature() {
+      return renamedSignature.toString();
+    }
+
+    @Override
+    public void parsedSymbol(char symbol) {
+      renamedSignature.append(symbol);
+    }
+
+    @Override
+    public void parsedIdentifier(String identifier) {
+      renamedSignature.append(identifier);
+    }
+
+    @Override
+    public DexType parsedTypeName(String name) {
+      DexType type = appInfo.dexItemFactory.createType(getDescriptorFromClassBinaryName(name));
+      type = appView.graphLense().lookupType(type);
+      DexString renamedDescriptor = renaming.getOrDefault(type, type.descriptor);
+      renamedSignature.append(getClassBinaryNameFromDescriptor(renamedDescriptor.toString()));
+      return type;
+    }
+
+    @Override
+    public DexType parsedInnerTypeName(DexType enclosingType, String name) {
+      assert enclosingType.isClassType();
+      String enclosingDescriptor = enclosingType.toDescriptorString();
+      DexType type =
+          appInfo.dexItemFactory.createType(
+              getDescriptorFromClassBinaryName(
+                  getClassBinaryNameFromDescriptor(enclosingDescriptor)
+                      + DescriptorUtils.INNER_CLASS_SEPARATOR
+                      + name));
+      String enclosingRenamedBinaryName =
+          getClassBinaryNameFromDescriptor(
+              renaming.getOrDefault(enclosingType, enclosingType.descriptor).toString());
+      type = appView.graphLense().lookupType(type);
+      DexString renamedDescriptor = renaming.get(type);
+      if (renamedDescriptor != null) {
+        // Pick the renamed inner class from the fully renamed binary name.
+        String fullRenamedBinaryName =
+            getClassBinaryNameFromDescriptor(renamedDescriptor.toString());
+        renamedSignature.append(
+            fullRenamedBinaryName.substring(enclosingRenamedBinaryName.length() + 1));
+      } else {
+        // Did not find the class - keep the inner class name as is.
+        // TODO(110085899): Warn about missing classes in signatures?
+        renamedSignature.append(name);
+      }
+      return type;
+    }
+
+    @Override
+    public void start() {
+      renamedSignature = new StringBuilder();
+    }
+
+    @Override
+    public void stop() {
+      // nothing to do
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 85c4876..64a9c67 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -288,8 +288,7 @@
   public LineNumberOptimization lineNumberOptimization = LineNumberOptimization.ON;
 
   public static boolean shouldEnableKeepRuleSynthesisForRecompilation() {
-    return Version.isDev()
-        && System.getProperty("com.android.tools.r8.keepRuleSynthesisForRecompilation") != null;
+    return System.getProperty("com.android.tools.r8.keepRuleSynthesisForRecompilation") != null;
   }
 
   private static Set<String> getExtensiveLoggingFilter() {
diff --git a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
index 37a0215..fc74d19 100644
--- a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
+++ b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
@@ -4,9 +4,9 @@
 
 package com.android.tools.r8.naming.b124357885;
 
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.allOf;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueArray;
 import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
@@ -26,8 +27,21 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
+@RunWith(Parameterized.class)
 public class B124357885Test extends TestBase {
+  public final boolean minification;
+
+  @Parameterized.Parameters(name = "Minification: {0}")
+  public static Boolean[] data() {
+    return BooleanUtils.values();
+  }
+
+  public B124357885Test(boolean minification) {
+    this.minification = minification;
+  }
 
   private void checkSignatureAnnotation(CodeInspector inspector, AnnotationSubject signature) {
     DexAnnotationElement[] elements = signature.getAnnotation().elements;
@@ -59,12 +73,13 @@
         .addProgramClasses(Main.class, Service.class, Foo.class, FooImpl.class)
         .addKeepMainRule(Main.class)
         .addKeepRules("-keepattributes Signature,InnerClasses,EnclosingMethod")
+        .minification(minification)
         .compile()
         .inspect(inspector -> {
-          assertThat(inspector.clazz(Main.class), allOf(isPresent(), not(isRenamed())));
-          assertThat(inspector.clazz(Service.class), allOf(isPresent(), isRenamed()));
+          assertThat(inspector.clazz(Main.class), isNotRenamed());
+          assertThat(inspector.clazz(Service.class), isRenamed(minification));
           assertThat(inspector.clazz(Foo.class), not(isPresent()));
-          assertThat(inspector.clazz(FooImpl.class), allOf(isPresent(), isRenamed()));
+          assertThat(inspector.clazz(FooImpl.class), isRenamed(minification));
           // TODO(124477502): Using uniqueMethodWithName("fooList") does not work.
           assertEquals(1, inspector.clazz(Service.class).allMethods().size());
           MethodSubject fooList = inspector.clazz(Service.class).allMethods().get(0);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index 103ffb9..417e40e 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -146,6 +146,10 @@
     };
   }
 
+  public static Matcher<Subject> isRenamed(boolean isRenamed) {
+    return isRenamed ? isRenamed() : isNotRenamed();
+  }
+
   public static Matcher<MemberSubject> isStatic() {
     return new TypeSafeMatcher<MemberSubject>() {
       @Override
diff --git a/third_party/opensource_apps.tar.gz.sha1 b/third_party/opensource_apps.tar.gz.sha1
index 4ee5113..1695642 100644
--- a/third_party/opensource_apps.tar.gz.sha1
+++ b/third_party/opensource_apps.tar.gz.sha1
@@ -1 +1 @@
-efed1b4bf62340b626cf2d0addb4f334664c0727
\ No newline at end of file
+682d306b6e9a1c6356f1dbfe23f5acef4bc23a2f
\ No newline at end of file
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index 1c6446e..ac27750 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -30,8 +30,7 @@
   WORKING_DIR = os.environ['R8_BENCHMARK_DIR']
 
 # For running on Golem all APPS are bundled as an x20-dependency and then copied
-# to WORKING_DIR. To make it easier to update the app-bundle, remove the folder
-# WORKING_DIR and then run run_on_as_app.py --download-only.
+# to WORKING_DIR. To update the app-bundle use 'run_on_as_app_x20_packager.py'.
 APPS = {
   # 'app-name': {
   #     'git_repo': ...
@@ -103,7 +102,7 @@
       'app_id': 'io.rover.app.debug',
       'app_module': 'debug-app',
       'git_repo': 'https://github.com/mkj-gram/rover-android.git',
-      'revision': 'd2e876e597b3af7eab406e38a0e08327a38bd942',
+      'revision': '859af82ba56fe9035ae9949156c7a88e6012d930',
   },
   'Signal-Android': {
       'app_id': 'org.thoughtcrime.securesms',
@@ -111,7 +110,7 @@
       'flavor': 'play',
       'git_repo': 'https://github.com/mkj-gram/Signal-Android.git',
       'main_dex_rules': 'multidex-config.pro',
-      'revision': '85e1a10993e5e9ffe923f0798b26cbc44068ba31',
+      'revision': 'a45d0c1fed20fa39e8b9445fe7790326f46b3166',
       'releaseTarget': 'assemblePlayRelease',
       'signed-apk-name': 'Signal-play-release-4.32.7.apk',
   },
@@ -215,14 +214,14 @@
 def IsTrackedByGit(file):
   return subprocess.check_output(['git', 'ls-files', file]).strip() != ''
 
-def GitClone(git_url, revision, checkout_dir, options):
+def GitClone(git_url, revision, checkout_dir, quiet):
   result = subprocess.check_output(
       ['git', 'clone', git_url, checkout_dir]).strip()
   head_rev = utils.get_HEAD_sha1_for_checkout(checkout_dir)
   if revision == head_rev:
     return result
   warn('Target revision is not head in {}.'.format(checkout_dir))
-  with utils.ChangedWorkingDirectory(checkout_dir, quiet=options.quiet):
+  with utils.ChangedWorkingDirectory(checkout_dir, quiet=quiet):
     subprocess.check_output(['git', 'reset', '--hard', revision])
   return result
 
@@ -290,7 +289,8 @@
 
   if not os.path.exists(checkout_dir) and not options.golem:
     with utils.ChangedWorkingDirectory(WORKING_DIR, quiet=options.quiet):
-      GitClone(config['git_repo'], config['revision'], checkout_dir, options)
+      GitClone(
+          config['git_repo'], config['revision'], checkout_dir, options.quiet)
 
   checkout_rev = utils.get_HEAD_sha1_for_checkout(checkout_dir)
   if config['revision'] != checkout_rev:
@@ -365,7 +365,12 @@
           # Sanity check that keep rules have changed.
           with open(ext_proguard_config_file) as new:
             with open(proguard_config_file) as old:
-              assert(sum(1 for line in new) > sum(1 for line in old))
+              assert(
+                  sum(1 for line in new
+                      if line.strip() and '-printconfiguration' not in line)
+                  >
+                  sum(1 for line in old
+                      if line.strip() and '-printconfiguration' not in line))
 
           # Extract min-sdk and target-sdk
           (min_sdk, compile_sdk) = as_utils.GetMinAndCompileSdk(app, config,
@@ -748,13 +753,13 @@
       options.shrinker.remove('r8-nolib-full')
   return (options, args)
 
-def download_apps(options):
+def download_apps(quiet):
   # Download apps and place in build
   with utils.ChangedWorkingDirectory(WORKING_DIR):
     for app, config in APPS.iteritems():
       app_dir = os.path.join(WORKING_DIR, app)
       if not os.path.exists(app_dir):
-        GitClone(config['git_repo'], config['revision'], app_dir, options)
+        GitClone(config['git_repo'], config['revision'], app_dir, quiet)
 
 
 def main(argv):
@@ -770,7 +775,7 @@
     os.makedirs(WORKING_DIR)
 
   if options.download_only:
-    download_apps(options)
+    download_apps(options.quiet)
     return
 
   with utils.TempDir() as temp_dir:
diff --git a/tools/run_on_as_app_x20_packager.py b/tools/run_on_as_app_x20_packager.py
new file mode 100755
index 0000000..5c84626
--- /dev/null
+++ b/tools/run_on_as_app_x20_packager.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+# Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import os
+import run_on_as_app
+import shutil
+import sys
+import upload_to_x20
+import utils
+
+def main():
+  # We need prodaccess to upload to x20
+  utils.check_prodacces()
+
+  working_dir = run_on_as_app.WORKING_DIR
+
+  print 'Removing directories that do not match checked out revision'
+  with utils.ChangedWorkingDirectory(working_dir):
+    for app, config in run_on_as_app.APPS.iteritems():
+      app_dir = os.path.join(working_dir, app)
+      if os.path.exists(app_dir) \
+          and utils.get_HEAD_sha1_for_checkout(app_dir) != config['revision']:
+        print 'Removing %s' % app_dir
+        shutil.rmtree(app_dir)
+
+  print 'Downloading all missing apps'
+  run_on_as_app.download_apps(quiet=False)
+
+  # Package all files as x20 dependency
+  parent_dir = os.path.dirname(working_dir)
+  with utils.ChangedWorkingDirectory(parent_dir):
+    print 'Creating archive for opensource_apps (this may take some time)'
+    working_dir_name = os.path.basename(working_dir)
+    app_dirs = [working_dir_name + '/' + name
+                for name in run_on_as_app.APPS.keys()]
+    filename = utils.create_archive("opensource_apps", app_dirs)
+    sha1 = utils.get_sha1(filename)
+    dest = os.path.join(upload_to_x20.GMSCORE_DEPS, sha1)
+    upload_to_x20.uploadFile(filename, dest)
+    sha1_file = '%s.sha1' % filename
+    with open(sha1_file, 'w') as output:
+      output.write(sha1)
+    shutil.move(sha1_file,
+                os.path.join(utils.THIRD_PARTY, 'opensource_apps.tar.gz.sha1'))
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/tools/utils.py b/tools/utils.py
index 892fe7d..f6e0a70 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -271,9 +271,13 @@
   subprocess.check_call(cmd)
 
 def create_archive(name):
+  return create_archive(name, [name])
+
+def create_archive(name, sources):
   tarname = '%s.tar.gz' % name
   with tarfile.open(tarname, 'w:gz') as tar:
-    tar.add(name)
+    for source in sources:
+      tar.add(source)
   return tarname
 
 def extract_dir(filename):
@@ -288,6 +292,9 @@
   with tarfile.open(filename, 'r:gz') as tar:
     tar.extractall(path=dirname)
 
+def check_prodacces():
+  subprocess.check_call(['prodaccess'])
+
 # Note that gcs is eventually consistent with regards to list operations.
 # This is not a problem in our case, but don't ever use this method
 # for synchronization.