Add dex checksums to R8 build metadata

Change-Id: If408a65c9aa0ed604e31b4485be1d3820b64b501
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 25a5031..9554f4e 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -647,6 +647,7 @@
       ((DexIndexedConsumer) consumer)
           .accept(virtualFile.getId(), data, virtualFile.getClassDescriptors(), options.reporter);
     }
+    virtualFile.calculateChecksumForBuildMetadata(data, options);
     timing.end();
     // Release use of the backing buffer now that accept has returned.
     data.invalidate();
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index b58266c..2d96710 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -8,6 +8,7 @@
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
 
+import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.FeatureSplit;
 import com.android.tools.r8.debuginfo.DebugRepresentation;
 import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
@@ -47,6 +48,8 @@
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.Hashing;
 import it.unimi.dsi.fastutil.objects.Object2IntMap;
 import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
 import java.util.ArrayList;
@@ -81,6 +84,7 @@
   private final DexString primaryClassSynthesizingContextDescriptor;
   private DebugRepresentation debugRepresentation;
   private boolean startup = false;
+  private HashCode checksumForBuildMetadata;
 
   VirtualFile(int id, AppView<?> appView) {
     this(id, appView, null, null, StartupProfile.empty());
@@ -128,6 +132,18 @@
     }
   }
 
+  public HashCode getChecksumForBuildMetadata() {
+    return checksumForBuildMetadata;
+  }
+
+  public void calculateChecksumForBuildMetadata(ByteDataView data, InternalOptions options) {
+    if (options.r8BuildMetadataConsumer != null) {
+      checksumForBuildMetadata =
+          Hashing.sha256()
+              .hashBytes(data.getBuffer(), data.getOffset(), data.getOffset() + data.getLength());
+    }
+  }
+
   public int getId() {
     return id;
   }
diff --git a/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java b/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java
index 0057ddc..c3f13b0 100644
--- a/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java
+++ b/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.metadata.impl.R8StartupOptimizationOptionsImpl;
 import com.google.gson.GsonBuilder;
 import com.google.gson.JsonDeserializer;
+import java.util.List;
 
 @KeepForApi
 public interface R8BuildMetadata {
@@ -53,6 +54,11 @@
   R8BaselineProfileRewritingOptions getBaselineProfileRewritingOptions();
 
   /**
+   * @return null if not compiling to dex.
+   */
+  List<String> getDexChecksums();
+
+  /**
    * @return null if resource optimization is disabled.
    */
   R8ResourceOptimizationOptions getResourceOptimizationOptions();
diff --git a/src/main/java/com/android/tools/r8/metadata/impl/BuildMetadataFactory.java b/src/main/java/com/android/tools/r8/metadata/impl/BuildMetadataFactory.java
index ef67f53..69e906c 100644
--- a/src/main/java/com/android/tools/r8/metadata/impl/BuildMetadataFactory.java
+++ b/src/main/java/com/android/tools/r8/metadata/impl/BuildMetadataFactory.java
@@ -28,6 +28,7 @@
     return R8BuildMetadataImpl.builder()
         .setOptions(new R8OptionsImpl(options))
         .setBaselineProfileRewritingOptions(R8BaselineProfileRewritingOptionsImpl.create(options))
+        .applyIf(options.isGeneratingDex(), builder -> builder.setDexChecksums(virtualFiles))
         .setResourceOptimizationOptions(R8ResourceOptimizationOptionsImpl.create(options))
         .setStartupOptimizationOptions(
             R8StartupOptimizationOptionsImpl.create(options, virtualFiles))
diff --git a/src/main/java/com/android/tools/r8/metadata/impl/R8BuildMetadataImpl.java b/src/main/java/com/android/tools/r8/metadata/impl/R8BuildMetadataImpl.java
index 8edb511..441146d 100644
--- a/src/main/java/com/android/tools/r8/metadata/impl/R8BuildMetadataImpl.java
+++ b/src/main/java/com/android/tools/r8/metadata/impl/R8BuildMetadataImpl.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.metadata.impl;
 
+import com.android.tools.r8.dex.VirtualFile;
 import com.android.tools.r8.keepanno.annotations.AnnotationPattern;
 import com.android.tools.r8.keepanno.annotations.FieldAccessFlags;
 import com.android.tools.r8.keepanno.annotations.KeepConstraint;
@@ -13,9 +14,12 @@
 import com.android.tools.r8.metadata.R8Options;
 import com.android.tools.r8.metadata.R8ResourceOptimizationOptions;
 import com.android.tools.r8.metadata.R8StartupOptimizationOptions;
+import com.android.tools.r8.utils.ListUtils;
 import com.google.gson.Gson;
 import com.google.gson.annotations.Expose;
 import com.google.gson.annotations.SerializedName;
+import java.util.List;
+import java.util.function.Consumer;
 
 @UsedByReflection(
     description = "Keep and preserve @SerializedName for correct (de)serialization",
@@ -35,6 +39,10 @@
   private final R8BaselineProfileRewritingOptions baselineProfileRewritingOptions;
 
   @Expose
+  @SerializedName("dexChecksums")
+  private final List<String> dexChecksums;
+
+  @Expose
   @SerializedName("resourceOptimizationOptions")
   private final R8ResourceOptimizationOptions resourceOptimizationOptions;
 
@@ -49,11 +57,13 @@
   public R8BuildMetadataImpl(
       R8Options options,
       R8BaselineProfileRewritingOptions baselineProfileRewritingOptions,
+      List<String> dexChecksums,
       R8ResourceOptimizationOptions resourceOptimizationOptions,
       R8StartupOptimizationOptions startupOptimizationOptions,
       String version) {
     this.options = options;
     this.baselineProfileRewritingOptions = baselineProfileRewritingOptions;
+    this.dexChecksums = dexChecksums;
     this.resourceOptimizationOptions = resourceOptimizationOptions;
     this.startupOptimizationOptions = startupOptimizationOptions;
     this.version = version;
@@ -74,6 +84,11 @@
   }
 
   @Override
+  public List<String> getDexChecksums() {
+    return dexChecksums;
+  }
+
+  @Override
   public R8ResourceOptimizationOptions getResourceOptimizationOptions() {
     return resourceOptimizationOptions;
   }
@@ -97,10 +112,18 @@
 
     private R8Options options;
     private R8BaselineProfileRewritingOptions baselineProfileRewritingOptions;
+    private List<String> dexChecksums;
     private R8ResourceOptimizationOptions resourceOptimizationOptions;
     private R8StartupOptimizationOptions startupOptimizationOptions;
     private String version;
 
+    public Builder applyIf(boolean condition, Consumer<Builder> thenConsumer) {
+      if (condition) {
+        thenConsumer.accept(this);
+      }
+      return this;
+    }
+
     public Builder setOptions(R8Options options) {
       this.options = options;
       return this;
@@ -112,6 +135,12 @@
       return this;
     }
 
+    public Builder setDexChecksums(List<VirtualFile> virtualFiles) {
+      this.dexChecksums =
+          ListUtils.map(virtualFiles, vf -> vf.getChecksumForBuildMetadata().toString());
+      return this;
+    }
+
     public Builder setResourceOptimizationOptions(
         R8ResourceOptimizationOptions resourceOptimizationOptions) {
       this.resourceOptimizationOptions = resourceOptimizationOptions;
@@ -133,6 +162,7 @@
       return new R8BuildMetadataImpl(
           options,
           baselineProfileRewritingOptions,
+          dexChecksums,
           resourceOptimizationOptions,
           startupOptimizationOptions,
           version);