APK digest API and initial implementation.

Bug: 160605420
Test: atest ChecksumsTest

Change-Id: I08ef0b131c44313f2c6acddad00dfa03598cc1ff
diff --git a/api/current.txt b/api/current.txt
index c6af6be..e5baa7c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11617,6 +11617,16 @@
     field public int version;
   }
 
+  public final class FileChecksum implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getKind();
+    method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
+    method @Nullable public String getSplitName();
+    method @NonNull public byte[] getValue();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.FileChecksum> CREATOR;
+  }
+
   public final class InstallSourceInfo implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public String getInitiatingPackageName();
@@ -11992,6 +12002,7 @@
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public CharSequence getBackgroundPermissionOptionLabel();
     method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
+    method public void getChecksums(@NonNull String, boolean, int, @Nullable java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
     method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
@@ -12082,6 +12093,7 @@
     field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
     field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
     field public static final int DONT_KILL_APP = 1; // 0x1
+    field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
     field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
     field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
     field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
@@ -12236,6 +12248,8 @@
     field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
     field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
+    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
+    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
     field public static final int PERMISSION_DENIED = -1; // 0xffffffff
     field public static final int PERMISSION_GRANTED = 0; // 0x0
     field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
@@ -12245,9 +12259,16 @@
     field public static final int SIGNATURE_SECOND_NOT_SIGNED = -2; // 0xfffffffe
     field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc
     field public static final int SYNCHRONOUS = 2; // 0x2
+    field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL;
+    field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE;
     field public static final int VERIFICATION_ALLOW = 1; // 0x1
     field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
     field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
+    field public static final int WHOLE_MD5 = 2; // 0x2
+    field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
+    field public static final int WHOLE_SHA1 = 4; // 0x4
+    field public static final int WHOLE_SHA256 = 8; // 0x8
+    field public static final int WHOLE_SHA512 = 16; // 0x10
   }
 
   public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 340d5a1..2780036 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -73,6 +73,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelableException;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
@@ -107,7 +108,11 @@
 
 import libcore.util.EmptyArray;
 
+import java.io.IOException;
 import java.lang.ref.WeakReference;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -135,6 +140,12 @@
     // Default flags to use with PackageManager when no flags are given.
     private static final int sDefaultFlags = GET_SHARED_LIBRARY_FILES;
 
+    /** Default set of checksums - includes all available checksums.
+     * @see PackageManager#getChecksums  */
+    private static final int DEFAULT_CHECKSUMS =
+            WHOLE_MERKLE_ROOT_4K_SHA256 | WHOLE_MD5 | WHOLE_SHA1 | WHOLE_SHA256 | WHOLE_SHA512
+                    | PARTIAL_MERKLE_ROOT_1M_SHA256 | PARTIAL_MERKLE_ROOT_1M_SHA512;
+
     // Name of the resource which provides background permission button string
     public static final String APP_PERMISSION_BUTTON_ALLOW_ALWAYS =
             "app_permission_button_allow_always";
@@ -945,6 +956,39 @@
         }
     }
 
+    private static List<byte[]> encodeCertificates(List<Certificate> certs) throws
+            CertificateEncodingException {
+        if (certs == null) {
+            return null;
+        }
+        List<byte[]> result = new ArrayList<>(certs.size());
+        for (Certificate cert : certs) {
+            if (!(cert instanceof X509Certificate)) {
+                throw new CertificateEncodingException("Only X509 certificates supported.");
+            }
+            result.add(cert.getEncoded());
+        }
+        return result;
+    }
+
+    @Override
+    public void getChecksums(@NonNull String packageName, boolean includeSplits,
+            @FileChecksumKind int required, @Nullable List<Certificate> trustedInstallers,
+            @NonNull IntentSender statusReceiver)
+            throws CertificateEncodingException, IOException, NameNotFoundException {
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(statusReceiver);
+        try {
+            mPM.getChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
+                    encodeCertificates(trustedInstallers), statusReceiver, getUserId());
+        } catch (ParcelableException e) {
+            e.maybeRethrow(PackageManager.NameNotFoundException.class);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Wrap the cached value in a class that does deep compares on string
      * arrays.  The comparison is needed only for the verification mode of
diff --git a/core/java/android/content/pm/FileChecksum.aidl b/core/java/android/content/pm/FileChecksum.aidl
new file mode 100644
index 0000000..109f211
--- /dev/null
+++ b/core/java/android/content/pm/FileChecksum.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package android.content.pm;
+
+parcelable FileChecksum;
+
diff --git a/core/java/android/content/pm/FileChecksum.java b/core/java/android/content/pm/FileChecksum.java
new file mode 100644
index 0000000..55430c2
--- /dev/null
+++ b/core/java/android/content/pm/FileChecksum.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentSender;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+/**
+ * A typed checksum.
+ *
+ * @see PackageManager#getChecksums(String, boolean, int, List, IntentSender)
+ */
+@DataClass(genHiddenConstructor = true)
+public final class FileChecksum implements Parcelable {
+    /**
+     * Checksum for which split. Null indicates base.apk.
+     */
+    private final @Nullable String mSplitName;
+    /**
+     * Checksum kind.
+     */
+    private final @PackageManager.FileChecksumKind int mKind;
+    /**
+     * Checksum value.
+     */
+    private final @NonNull byte[] mValue;
+    /**
+     * For Installer-provided checksums, certificate of the Installer/AppStore.
+     */
+    private final @Nullable byte[] mSourceCertificate;
+
+    /**
+     * Constructor, internal use only
+     *
+     * @hide
+     */
+    public FileChecksum(@Nullable String splitName, @PackageManager.FileChecksumKind int kind,
+            @NonNull byte[] value) {
+        this(splitName, kind, value, (byte[]) null);
+    }
+
+    /**
+     * Constructor, internal use only
+     *
+     * @hide
+     */
+    public FileChecksum(@Nullable String splitName, @PackageManager.FileChecksumKind int kind,
+            @NonNull byte[] value, @Nullable Certificate sourceCertificate)
+            throws CertificateEncodingException {
+        this(splitName, kind, value,
+                (sourceCertificate != null) ? sourceCertificate.getEncoded() : null);
+    }
+
+    /**
+     * Certificate of the source of this checksum.
+     * @throws CertificateException in case when certificate can't be re-created from serialized
+     * data.
+     */
+    public @Nullable Certificate getSourceCertificate() throws CertificateException {
+        if (mSourceCertificate == null) {
+            return null;
+        }
+        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        final InputStream is = new ByteArrayInputStream(mSourceCertificate);
+        final X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
+        return cert;
+    }
+
+
+
+    // Code below generated by codegen v1.0.15.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/FileChecksum.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new FileChecksum.
+     *
+     * @param splitName
+     *   Checksum for which split. Null indicates base.apk.
+     * @param kind
+     *   Checksum kind.
+     * @param value
+     *   Checksum value.
+     * @param sourceCertificate
+     *   For Installer-provided checksums, certificate of the Installer/AppStore.
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public FileChecksum(
+            @Nullable String splitName,
+            @PackageManager.FileChecksumKind int kind,
+            @NonNull byte[] value,
+            @Nullable byte[] sourceCertificate) {
+        this.mSplitName = splitName;
+        this.mKind = kind;
+        com.android.internal.util.AnnotationValidations.validate(
+                PackageManager.FileChecksumKind.class, null, mKind);
+        this.mValue = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mValue);
+        this.mSourceCertificate = sourceCertificate;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Checksum for which split. Null indicates base.apk.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getSplitName() {
+        return mSplitName;
+    }
+
+    /**
+     * Checksum kind.
+     */
+    @DataClass.Generated.Member
+    public @PackageManager.FileChecksumKind int getKind() {
+        return mKind;
+    }
+
+    /**
+     * Checksum value.
+     */
+    @DataClass.Generated.Member
+    public @NonNull byte[] getValue() {
+        return mValue;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mSplitName != null) flg |= 0x1;
+        if (mSourceCertificate != null) flg |= 0x8;
+        dest.writeByte(flg);
+        if (mSplitName != null) dest.writeString(mSplitName);
+        dest.writeInt(mKind);
+        dest.writeByteArray(mValue);
+        if (mSourceCertificate != null) dest.writeByteArray(mSourceCertificate);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ FileChecksum(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        String splitName = (flg & 0x1) == 0 ? null : in.readString();
+        int kind = in.readInt();
+        byte[] value = in.createByteArray();
+        byte[] sourceCertificate = (flg & 0x8) == 0 ? null : in.createByteArray();
+
+        this.mSplitName = splitName;
+        this.mKind = kind;
+        com.android.internal.util.AnnotationValidations.validate(
+                PackageManager.FileChecksumKind.class, null, mKind);
+        this.mValue = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mValue);
+        this.mSourceCertificate = sourceCertificate;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<FileChecksum> CREATOR
+            = new Parcelable.Creator<FileChecksum>() {
+        @Override
+        public FileChecksum[] newArray(int size) {
+            return new FileChecksum[size];
+        }
+
+        @Override
+        public FileChecksum createFromParcel(@NonNull Parcel in) {
+            return new FileChecksum(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1598322801861L,
+            codegenVersion = "1.0.15",
+            sourceFile = "frameworks/base/core/java/android/content/pm/FileChecksum.java",
+            inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.content.pm.PackageManager.FileChecksumKind int mKind\nprivate final @android.annotation.NonNull byte[] mValue\nprivate final @android.annotation.Nullable byte[] mSourceCertificate\npublic @android.annotation.Nullable java.security.cert.Certificate getSourceCertificate()\nclass FileChecksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 6a8dd81..1f8cee2 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -743,6 +743,8 @@
 
     void notifyPackagesReplacedReceived(in String[] packages);
 
+    void getChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
+
     //------------------------------------------------------------------------
     //
     // The following binder interfaces have been moved to IPermissionManager
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7b2955d..da8d15a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -79,8 +79,11 @@
 import dalvik.system.VMRuntime;
 
 import java.io.File;
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
@@ -3305,6 +3308,13 @@
     public static final String EXTRA_FAILURE_EXISTING_PERMISSION
             = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
 
+    /**
+     * Extra field name for the ID of a package pending verification. Passed to
+     * a package verifier and is used to call back to
+     * @see #getChecksums
+     */
+    public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
+
    /**
     * Permission flag: The permission is set in its current state
     * by the user and apps can still request it at runtime.
@@ -7842,6 +7852,114 @@
     }
 
     /**
+     * Root SHA256 hash of a 4K Merkle tree computed over all file bytes.
+     * <a href="https://source.android.com/security/apksigning/v4">See APK Signature Scheme V4</a>.
+     * <a href="https://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git/tree/Documentation/filesystems/fsverity.rst">See fs-verity</a>.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001;
+
+    /**
+     * MD5 hash computed over all file bytes.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_MD5 = 0x00000002;
+
+    /**
+     * SHA1 hash computed over all file bytes.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_SHA1 = 0x00000004;
+
+    /**
+     * SHA256 hash computed over all file bytes.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_SHA256 = 0x00000008;
+
+    /**
+     * SHA512 hash computed over all file bytes.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_SHA512 = 0x00000010;
+
+    /**
+     * Root SHA256 hash of a 1M Merkle tree computed over protected content.
+     * Excludes signing block.
+     * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
+     *
+     * @see #getChecksums
+     */
+    public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020;
+
+    /**
+     * Root SHA512 hash of a 1M Merkle tree computed over protected content.
+     * Excludes signing block.
+     * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
+     *
+     * @see #getChecksums
+     */
+    public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"WHOLE_", "PARTIAL_"}, value = {
+            WHOLE_MERKLE_ROOT_4K_SHA256,
+            WHOLE_MD5,
+            WHOLE_SHA1,
+            WHOLE_SHA256,
+            WHOLE_SHA512,
+            PARTIAL_MERKLE_ROOT_1M_SHA256,
+            PARTIAL_MERKLE_ROOT_1M_SHA512,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FileChecksumKind {}
+
+    /**
+     * Trust any Installer to provide checksums for the package.
+     * @see #getChecksums
+     */
+    public static final @Nullable List<Certificate> TRUST_ALL = null;
+
+    /**
+     * Don't trust any Installer to provide checksums for the package.
+     * This effectively disables optimized Installer-enforced checksums.
+     * @see #getChecksums
+     */
+    public static final @NonNull List<Certificate> TRUST_NONE = Collections.emptyList();
+
+    /**
+     * Returns the checksums for APKs within a package.
+     *
+     * By default returns all readily available checksums:
+     * - enforced by platform,
+     * - enforced by installer.
+     * If caller needs a specific checksum kind, they can specify it as required.
+     *
+     * @param packageName whose checksums to return.
+     * @param includeSplits whether to include checksums for non-base splits.
+     * @param required explicitly request the checksum kinds. Will incur significant
+     *                 CPU/memory/disk usage.
+     * @param trustedInstallers for checksums enforced by Installer, which ones to be trusted.
+     *                          {@link #TRUST_ALL} will return checksums from any Installer,
+     *                          {@link #TRUST_NONE} disables optimized Installer-enforced checksums.
+     * @param statusReceiver called once when the results are available as
+     *                       {@link #EXTRA_CHECKSUMS} of type FileChecksum[].
+     * @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
+     * @throws NameNotFoundException if a package with the given name cannot be found on the system.
+     */
+    public void getChecksums(@NonNull String packageName, boolean includeSplits,
+            @FileChecksumKind int required, @Nullable List<Certificate> trustedInstallers,
+            @NonNull IntentSender statusReceiver)
+            throws CertificateEncodingException, IOException, NameNotFoundException {
+        throw new UnsupportedOperationException("getChecksums not implemented in subclass");
+    }
+
+    /**
      * @return the default text classifier package name, or null if there's none.
      *
      * @hide
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 6e34666..f74990a 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -149,7 +149,7 @@
      * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
      * @throws IOException if an I/O error occurs while reading the APK file.
      */
-    private static SignatureInfo findSignature(RandomAccessFile apk)
+    public static SignatureInfo findSignature(RandomAccessFile apk)
             throws IOException, SignatureNotFoundException {
         return ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V2_BLOCK_ID);
     }
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 9357285..5f963b0 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -142,7 +142,7 @@
      * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3.
      * @throws IOException if an I/O error occurs while reading the APK file.
      */
-    private static SignatureInfo findSignature(RandomAccessFile apk)
+    public static SignatureInfo findSignature(RandomAccessFile apk)
             throws IOException, SignatureNotFoundException {
         return ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V3_BLOCK_ID);
     }
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index e0258f7..02edb7e 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -92,6 +92,20 @@
     private static PackageParser.SigningDetails verifySignatures(String apkPath,
             @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
             throws PackageParserException {
+        return verifySignaturesInternal(apkPath, minSignatureSchemeVersion,
+                verifyFull).signingDetails;
+    }
+
+    /**
+     * Verifies the provided APK using all allowed signing schemas.
+     * @return the certificates associated with each signer and content digests.
+     * @param verifyFull whether to verify all contents of this APK or just collect certificates.
+     * @throws PackageParserException if there was a problem collecting certificates
+     * @hide
+     */
+    public static SigningDetailsWithDigests verifySignaturesInternal(String apkPath,
+            @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
+            throws PackageParserException {
 
         if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V4) {
             // V3 and before are older than the requested minimum signing version
@@ -121,7 +135,7 @@
         return verifyV3AndBelowSignatures(apkPath, minSignatureSchemeVersion, verifyFull);
     }
 
-    private static PackageParser.SigningDetails verifyV3AndBelowSignatures(String apkPath,
+    private static SigningDetailsWithDigests verifyV3AndBelowSignatures(String apkPath,
             @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
             throws PackageParserException {
         // try v3
@@ -174,7 +188,7 @@
      * @throws SignatureNotFoundException if there are no V4 signatures in the APK
      * @throws PackageParserException     if there was a problem collecting certificates
      */
-    private static PackageParser.SigningDetails verifyV4Signature(String apkPath,
+    private static SigningDetailsWithDigests verifyV4Signature(String apkPath,
             @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
             throws SignatureNotFoundException, PackageParserException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV4" : "certsOnlyV4");
@@ -234,8 +248,8 @@
                 }
             }
 
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V4);
+            return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+                    SignatureSchemeVersion.SIGNING_BLOCK_V4), vSigner.contentDigests);
         } catch (SignatureNotFoundException e) {
             throw e;
         } catch (Exception e) {
@@ -256,8 +270,8 @@
      * @throws SignatureNotFoundException if there are no V3 signatures in the APK
      * @throws PackageParserException     if there was a problem collecting certificates
      */
-    private static PackageParser.SigningDetails verifyV3Signature(String apkPath,
-            boolean verifyFull) throws SignatureNotFoundException, PackageParserException {
+    private static SigningDetailsWithDigests verifyV3Signature(String apkPath, boolean verifyFull)
+            throws SignatureNotFoundException, PackageParserException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV3" : "certsOnlyV3");
         try {
             ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
@@ -275,8 +289,9 @@
                     pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
                 }
             }
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs);
+            return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+                    SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs),
+                    vSigner.contentDigests);
         } catch (SignatureNotFoundException e) {
             throw e;
         } catch (Exception e) {
@@ -297,15 +312,16 @@
      * @throws SignatureNotFoundException if there are no V2 signatures in the APK
      * @throws PackageParserException     if there was a problem collecting certificates
      */
-    private static PackageParser.SigningDetails verifyV2Signature(String apkPath,
-            boolean verifyFull) throws SignatureNotFoundException, PackageParserException {
+    private static SigningDetailsWithDigests verifyV2Signature(String apkPath, boolean verifyFull)
+            throws SignatureNotFoundException, PackageParserException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV2" : "certsOnlyV2");
         try {
-            Certificate[][] signerCerts = verifyFull ? ApkSignatureSchemeV2Verifier.verify(apkPath)
-                    : ApkSignatureSchemeV2Verifier.unsafeGetCertsWithoutVerification(apkPath);
+            ApkSignatureSchemeV2Verifier.VerifiedSigner vSigner =
+                    ApkSignatureSchemeV2Verifier.verify(apkPath, verifyFull);
+            Certificate[][] signerCerts = vSigner.certs;
             Signature[] signerSigs = convertToSignatures(signerCerts);
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V2);
+            return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+                    SignatureSchemeVersion.SIGNING_BLOCK_V2), vSigner.contentDigests);
         } catch (SignatureNotFoundException e) {
             throw e;
         } catch (Exception e) {
@@ -324,8 +340,7 @@
      * @param verifyFull whether to verify all contents of this APK or just collect certificates.
      * @throws PackageParserException if there was a problem collecting certificates
      */
-    private static PackageParser.SigningDetails verifyV1Signature(
-            String apkPath, boolean verifyFull)
+    private static SigningDetailsWithDigests verifyV1Signature(String apkPath, boolean verifyFull)
             throws PackageParserException {
         StrictJarFile jarFile = null;
 
@@ -391,7 +406,8 @@
                     }
                 }
             }
-            return new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR);
+            return new SigningDetailsWithDigests(
+                    new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR), null);
         } catch (GeneralSecurityException e) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
                     "Failed to collect certificates from " + apkPath, e);
@@ -542,4 +558,27 @@
             return null;
         }
     }
+
+    /**
+     * Extended signing details.
+     * @hide for internal use only.
+     */
+    public static class SigningDetailsWithDigests {
+        public final PackageParser.SigningDetails signingDetails;
+
+        /**
+         * APK Signature Schemes v2/v3/v4 might contain multiple content digests.
+         * SignatureVerifier usually chooses one of them to verify.
+         * For certain signature schemes, e.g. v4, this digest is verified continuously.
+         * For others, e.g. v2, the caller has to specify if they want to verify.
+         * Please refer to documentation for more details.
+         */
+        public final Map<Integer, byte[]> contentDigests;
+
+        SigningDetailsWithDigests(PackageParser.SigningDetails signingDetails,
+                Map<Integer, byte[]> contentDigests) {
+            this.signingDetails = signingDetails;
+            this.contentDigests = contentDigests;
+        }
+    }
 }
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 990092c..021f232 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -39,7 +39,7 @@
  *
  * @hide for internal use only.
  */
-final class ApkSigningBlockUtils {
+public final class ApkSigningBlockUtils {
 
     private ApkSigningBlockUtils() {
     }
@@ -146,6 +146,37 @@
             Map<Integer, byte[]> expectedDigests,
             FileDescriptor apkFileDescriptor,
             SignatureInfo signatureInfo) throws SecurityException {
+        int[] digestAlgorithms = new int[expectedDigests.size()];
+        int digestAlgorithmCount = 0;
+        for (int digestAlgorithm : expectedDigests.keySet()) {
+            digestAlgorithms[digestAlgorithmCount] = digestAlgorithm;
+            digestAlgorithmCount++;
+        }
+        byte[][] actualDigests;
+        try {
+            actualDigests = computeContentDigestsPer1MbChunk(digestAlgorithms, apkFileDescriptor,
+                    signatureInfo);
+        } catch (DigestException e) {
+            throw new SecurityException("Failed to compute digest(s) of contents", e);
+        }
+        for (int i = 0; i < digestAlgorithms.length; i++) {
+            int digestAlgorithm = digestAlgorithms[i];
+            byte[] expectedDigest = expectedDigests.get(digestAlgorithm);
+            byte[] actualDigest = actualDigests[i];
+            if (!MessageDigest.isEqual(expectedDigest, actualDigest)) {
+                throw new SecurityException(
+                        getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
+                                + " digest of contents did not verify");
+            }
+        }
+    }
+
+    /**
+     * Calculate digests using digestAlgorithms for apkFileDescriptor.
+     * This will skip signature block described by signatureInfo.
+     */
+    public static byte[][] computeContentDigestsPer1MbChunk(int[] digestAlgorithms,
+            FileDescriptor apkFileDescriptor, SignatureInfo signatureInfo) throws DigestException {
         // We need to verify the integrity of the following three sections of the file:
         // 1. Everything up to the start of the APK Signing Block.
         // 2. ZIP Central Directory.
@@ -156,6 +187,7 @@
         // avoid wasting physical memory. In most APK verification scenarios, the contents of the
         // APK are already there in the OS's page cache and thus mmap does not use additional
         // physical memory.
+
         DataSource beforeApkSigningBlock =
                 new MemoryMappedFileDataSource(apkFileDescriptor, 0,
                         signatureInfo.apkSigningBlockOffset);
@@ -171,31 +203,8 @@
         ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, signatureInfo.apkSigningBlockOffset);
         DataSource eocd = new ByteBufferDataSource(eocdBuf);
 
-        int[] digestAlgorithms = new int[expectedDigests.size()];
-        int digestAlgorithmCount = 0;
-        for (int digestAlgorithm : expectedDigests.keySet()) {
-            digestAlgorithms[digestAlgorithmCount] = digestAlgorithm;
-            digestAlgorithmCount++;
-        }
-        byte[][] actualDigests;
-        try {
-            actualDigests =
-                    computeContentDigestsPer1MbChunk(
-                            digestAlgorithms,
-                            new DataSource[] {beforeApkSigningBlock, centralDir, eocd});
-        } catch (DigestException e) {
-            throw new SecurityException("Failed to compute digest(s) of contents", e);
-        }
-        for (int i = 0; i < digestAlgorithms.length; i++) {
-            int digestAlgorithm = digestAlgorithms[i];
-            byte[] expectedDigest = expectedDigests.get(digestAlgorithm);
-            byte[] actualDigest = actualDigests[i];
-            if (!MessageDigest.isEqual(expectedDigest, actualDigest)) {
-                throw new SecurityException(
-                        getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
-                                + " digest of contents did not verify");
-            }
-        }
+        return computeContentDigestsPer1MbChunk(digestAlgorithms,
+                new DataSource[]{beforeApkSigningBlock, centralDir, eocd});
     }
 
     private static byte[][] computeContentDigestsPer1MbChunk(
@@ -417,14 +426,10 @@
     static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0423;
     static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0425;
 
-    static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
-    static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
-    static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3;
-    static final int CONTENT_DIGEST_SHA256 = 4;
-
-    private static final int[] V4_CONTENT_DIGEST_ALGORITHMS =
-            {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
-                    CONTENT_DIGEST_CHUNKED_SHA256};
+    public static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
+    public static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
+    public static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3;
+    public static final int CONTENT_DIGEST_SHA256 = 4;
 
     static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) {
         int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1);
diff --git a/core/java/android/util/apk/SignatureInfo.java b/core/java/android/util/apk/SignatureInfo.java
index 8e1233a..7638293 100644
--- a/core/java/android/util/apk/SignatureInfo.java
+++ b/core/java/android/util/apk/SignatureInfo.java
@@ -16,15 +16,18 @@
 
 package android.util.apk;
 
+import android.annotation.NonNull;
+
 import java.nio.ByteBuffer;
 
 /**
  * APK Signature Scheme v2 block and additional information relevant to verifying the signatures
  * contained in the block against the file.
+ * @hide
  */
-class SignatureInfo {
+public class SignatureInfo {
     /** Contents of APK Signature Scheme v2 block. */
-    public final ByteBuffer signatureBlock;
+    public final @NonNull ByteBuffer signatureBlock;
 
     /** Position of the APK Signing Block in the file. */
     public final long apkSigningBlockOffset;
@@ -36,10 +39,10 @@
     public final long eocdOffset;
 
     /** Contents of ZIP End of Central Directory (EoCD) of the file. */
-    public final ByteBuffer eocd;
+    public final @NonNull ByteBuffer eocd;
 
-    SignatureInfo(ByteBuffer signatureBlock, long apkSigningBlockOffset, long centralDirOffset,
-            long eocdOffset, ByteBuffer eocd) {
+    SignatureInfo(@NonNull ByteBuffer signatureBlock, long apkSigningBlockOffset,
+            long centralDirOffset, long eocdOffset, @NonNull ByteBuffer eocd) {
         this.signatureBlock = signatureBlock;
         this.apkSigningBlockOffset = apkSigningBlockOffset;
         this.centralDirOffset = centralDirOffset;
diff --git a/core/java/android/util/apk/VerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java
index e81e3f7..4596c6e 100644
--- a/core/java/android/util/apk/VerityBuilder.java
+++ b/core/java/android/util/apk/VerityBuilder.java
@@ -116,6 +116,34 @@
     }
 
     /**
+     * Generates the fs-verity hash tree. It is the actual verity tree format on disk, as is
+     * re-generated on device.
+     *
+     * The tree is built bottom up. The bottom level has 256-bit digest for each 4 KB block in the
+     * input file.  If the total size is larger than 4 KB, take this level as input and repeat the
+     * same procedure, until the level is within 4 KB.  If salt is given, it will apply to each
+     * digestion before the actual data.
+     *
+     * The returned root hash is calculated from the last level of 4 KB chunk, similarly with salt.
+     *
+     * @return the root hash of the generated hash tree.
+     */
+    public static byte[] generateFsVerityRootHash(@NonNull String apkPath, byte[] salt,
+            @NonNull ByteBufferFactory bufferFactory)
+            throws IOException, NoSuchAlgorithmException, DigestException {
+        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+            int[] levelOffset = calculateVerityLevelOffset(apk.length());
+            int merkleTreeSize = levelOffset[levelOffset.length - 1];
+
+            ByteBuffer output = bufferFactory.create(
+                    merkleTreeSize
+                            + CHUNK_SIZE_BYTES);  // maximum size of apk-verity metadata
+            output.order(ByteOrder.LITTLE_ENDIAN);
+            ByteBuffer tree = slice(output, 0, merkleTreeSize);
+            return generateFsVerityTreeInternal(apk, salt, levelOffset, tree);
+        }
+    }
+    /**
      * Calculates the apk-verity root hash for integrity measurement.  This needs to be consistent
      * to what kernel returns.
      */
@@ -259,9 +287,10 @@
     // thus the syscall overhead is not too big.
     private static final int MMAP_REGION_SIZE_BYTES = 1024 * 1024;
 
-    private static void generateFsVerityDigestAtLeafLevel(RandomAccessFile file, ByteBuffer output)
+    private static void generateFsVerityDigestAtLeafLevel(RandomAccessFile file,
+            @Nullable byte[] salt, ByteBuffer output)
             throws IOException, NoSuchAlgorithmException, DigestException {
-        BufferedDigester digester = new BufferedDigester(null /* salt */, output);
+        BufferedDigester digester = new BufferedDigester(salt, output);
 
         // 1. Digest the whole file by chunks.
         consumeByChunk(digester,
@@ -325,6 +354,35 @@
     }
 
     @NonNull
+    private static byte[] generateFsVerityTreeInternal(@NonNull RandomAccessFile apk,
+            @Nullable byte[] salt, @NonNull int[] levelOffset, @NonNull ByteBuffer output)
+            throws IOException, NoSuchAlgorithmException, DigestException {
+        // 1. Digest the apk to generate the leaf level hashes.
+        generateFsVerityDigestAtLeafLevel(apk, salt,
+                slice(output, levelOffset[levelOffset.length - 2],
+                        levelOffset[levelOffset.length - 1]));
+
+        // 2. Digest the lower level hashes bottom up.
+        for (int level = levelOffset.length - 3; level >= 0; level--) {
+            ByteBuffer inputBuffer = slice(output, levelOffset[level + 1], levelOffset[level + 2]);
+            ByteBuffer outputBuffer = slice(output, levelOffset[level], levelOffset[level + 1]);
+
+            DataSource source = new ByteBufferDataSource(inputBuffer);
+            BufferedDigester digester = new BufferedDigester(salt, outputBuffer);
+            consumeByChunk(digester, source, CHUNK_SIZE_BYTES);
+            digester.assertEmptyBuffer();
+            digester.fillUpLastOutputChunk();
+        }
+
+        // 3. Digest the first block (i.e. first level) to generate the root hash.
+        byte[] rootHash = new byte[DIGEST_SIZE_BYTES];
+        BufferedDigester digester = new BufferedDigester(salt, ByteBuffer.wrap(rootHash));
+        digester.consume(slice(output, 0, CHUNK_SIZE_BYTES));
+        digester.assertEmptyBuffer();
+        return rootHash;
+    }
+
+    @NonNull
     private static byte[] generateVerityTreeInternal(@NonNull RandomAccessFile apk,
             @Nullable SignatureInfo signatureInfo, @Nullable byte[] salt,
             @NonNull int[] levelOffset, @NonNull ByteBuffer output)
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index e0ebec6..e1b3151 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -11617,6 +11617,16 @@
     field public int version;
   }
 
+  public final class FileChecksum implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getKind();
+    method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
+    method @Nullable public String getSplitName();
+    method @NonNull public byte[] getValue();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.FileChecksum> CREATOR;
+  }
+
   public final class InstallSourceInfo implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public String getInitiatingPackageName();
@@ -11992,6 +12002,7 @@
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public CharSequence getBackgroundPermissionOptionLabel();
     method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
+    method public void getChecksums(@NonNull String, boolean, int, @Nullable java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
     method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
@@ -12082,6 +12093,7 @@
     field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
     field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
     field public static final int DONT_KILL_APP = 1; // 0x1
+    field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
     field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
     field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
     field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
@@ -12236,6 +12248,8 @@
     field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
     field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
+    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
+    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
     field public static final int PERMISSION_DENIED = -1; // 0xffffffff
     field public static final int PERMISSION_GRANTED = 0; // 0x0
     field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
@@ -12245,9 +12259,16 @@
     field public static final int SIGNATURE_SECOND_NOT_SIGNED = -2; // 0xfffffffe
     field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc
     field public static final int SYNCHRONOUS = 2; // 0x2
+    field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL;
+    field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE;
     field public static final int VERIFICATION_ALLOW = 1; // 0x1
     field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
     field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
+    field public static final int WHOLE_MD5 = 2; // 0x2
+    field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
+    field public static final int WHOLE_SHA1 = 4; // 0x4
+    field public static final int WHOLE_SHA256 = 8; // 0x8
+    field public static final int WHOLE_SHA512 = 16; // 0x10
   }
 
   public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
new file mode 100644
index 0000000..d8745ab
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA256;
+import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA512;
+import static android.content.pm.PackageManager.WHOLE_MD5;
+import static android.content.pm.PackageManager.WHOLE_MERKLE_ROOT_4K_SHA256;
+import static android.content.pm.PackageManager.WHOLE_SHA1;
+import static android.content.pm.PackageManager.WHOLE_SHA256;
+import static android.content.pm.PackageManager.WHOLE_SHA512;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
+
+import android.annotation.Nullable;
+import android.content.pm.FileChecksum;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.apk.ApkSignatureSchemeV2Verifier;
+import android.util.apk.ApkSignatureSchemeV3Verifier;
+import android.util.apk.ApkSignatureSchemeV4Verifier;
+import android.util.apk.ApkSignatureVerifier;
+import android.util.apk.ApkSigningBlockUtils;
+import android.util.apk.ByteBufferFactory;
+import android.util.apk.SignatureInfo;
+import android.util.apk.SignatureNotFoundException;
+import android.util.apk.VerityBuilder;
+
+import com.android.server.security.VerityUtils;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides checksums for APK.
+ */
+public class ApkChecksums {
+    static final String TAG = "ApkChecksums";
+
+    // MessageDigest algorithms.
+    static final String ALGO_MD5 = "MD5";
+    static final String ALGO_SHA1 = "SHA1";
+    static final String ALGO_SHA256 = "SHA256";
+    static final String ALGO_SHA512 = "SHA512";
+
+    /**
+     * Fetch or calculate checksums for the specific file.
+     *
+     * @param split             split name, null for base
+     * @param file              to fetch checksums for
+     * @param optional          mask to fetch readily available checksums
+     * @param required          mask to forcefully calculate if not available
+     * @param trustedInstallers array of certificate to trust, two specific cases:
+     *                          null - trust anybody,
+     *                          [] - trust nobody.
+     */
+    public static List<FileChecksum> getFileChecksums(String split, File file,
+            @PackageManager.FileChecksumKind int optional,
+            @PackageManager.FileChecksumKind int required,
+            @Nullable Certificate[] trustedInstallers) {
+        final String filePath = file.getAbsolutePath();
+        Map<Integer, FileChecksum> checksums = new ArrayMap<>();
+        final int kinds = (optional | required);
+        // System enforced: FSI or v2/v3/v4 signatures.
+        if ((kinds & WHOLE_MERKLE_ROOT_4K_SHA256) != 0) {
+            // Hashes in fs-verity and IncFS are always verified.
+            FileChecksum checksum = extractHashFromFS(split, filePath);
+            if (checksum != null) {
+                checksums.put(checksum.getKind(), checksum);
+            }
+        }
+        if ((kinds & (PARTIAL_MERKLE_ROOT_1M_SHA256 | PARTIAL_MERKLE_ROOT_1M_SHA512)) != 0) {
+            Map<Integer, FileChecksum> v2v3checksums = extractHashFromV2V3Signature(
+                    split, filePath, kinds);
+            if (v2v3checksums != null) {
+                checksums.putAll(v2v3checksums);
+            }
+        }
+
+        // TODO(b/160605420): Installer provided.
+        // TODO(b/160605420): Wait for Incremental to be fully loaded.
+
+        // Manually calculating required checksums if not readily available.
+        if ((required & WHOLE_MERKLE_ROOT_4K_SHA256) != 0 && !checksums.containsKey(
+                WHOLE_MERKLE_ROOT_4K_SHA256)) {
+            try {
+                byte[] generatedRootHash = VerityBuilder.generateFsVerityRootHash(
+                        filePath, /*salt=*/null,
+                        new ByteBufferFactory() {
+                            @Override
+                            public ByteBuffer create(int capacity) {
+                                return ByteBuffer.allocate(capacity);
+                            }
+                        });
+                checksums.put(WHOLE_MERKLE_ROOT_4K_SHA256,
+                        new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, generatedRootHash));
+            } catch (IOException | NoSuchAlgorithmException | DigestException e) {
+                Slog.e(TAG, "Error calculating WHOLE_MERKLE_ROOT_4K_SHA256", e);
+            }
+        }
+
+        calculateChecksumIfRequested(checksums, split, file, required, WHOLE_MD5);
+        calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA1);
+        calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA256);
+        calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA512);
+
+        calculatePartialChecksumsIfRequested(checksums, split, file, required);
+
+        return new ArrayList<>(checksums.values());
+    }
+
+    private static FileChecksum extractHashFromFS(String split, String filePath) {
+        // verity first
+        {
+            byte[] hash = VerityUtils.getFsverityRootHash(filePath);
+            if (hash != null) {
+                return new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash);
+            }
+        }
+        // v4 next
+        try {
+            ApkSignatureSchemeV4Verifier.VerifiedSigner signer =
+                    ApkSignatureSchemeV4Verifier.extractCertificates(filePath);
+            byte[] hash = signer.contentDigests.getOrDefault(CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
+                    null);
+            if (hash != null) {
+                return new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash);
+            }
+        } catch (SignatureNotFoundException e) {
+            // Nothing
+        } catch (SecurityException e) {
+            Slog.e(TAG, "V4 signature error", e);
+        }
+        return null;
+    }
+
+    private static Map<Integer, FileChecksum> extractHashFromV2V3Signature(
+            String split, String filePath, int kinds) {
+        Map<Integer, byte[]> contentDigests = null;
+        try {
+            contentDigests = ApkSignatureVerifier.verifySignaturesInternal(filePath,
+                    PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
+                    false).contentDigests;
+        } catch (PackageParser.PackageParserException e) {
+            Slog.e(TAG, "Signature verification error", e);
+        }
+
+        if (contentDigests == null) {
+            return null;
+        }
+
+        Map<Integer, FileChecksum> checksums = new ArrayMap<>();
+        if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0) {
+            byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA256, null);
+            if (hash != null) {
+                checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA256,
+                        new FileChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA256, hash));
+            }
+        }
+        if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0) {
+            byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA512, null);
+            if (hash != null) {
+                checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA512,
+                        new FileChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA512, hash));
+            }
+        }
+        return checksums;
+    }
+
+    private static String getMessageDigestAlgoForChecksumKind(int kind)
+            throws NoSuchAlgorithmException {
+        switch (kind) {
+            case WHOLE_MD5:
+                return ALGO_MD5;
+            case WHOLE_SHA1:
+                return ALGO_SHA1;
+            case WHOLE_SHA256:
+                return ALGO_SHA256;
+            case WHOLE_SHA512:
+                return ALGO_SHA512;
+            default:
+                throw new NoSuchAlgorithmException("Invalid checksum kind: " + kind);
+        }
+    }
+
+    private static void calculateChecksumIfRequested(Map<Integer, FileChecksum> checksums,
+            String split, File file, int required, int kind) {
+        if ((required & kind) != 0 && !checksums.containsKey(kind)) {
+            final byte[] checksum = getFileChecksum(file, kind);
+            if (checksum != null) {
+                checksums.put(kind, new FileChecksum(split, kind, checksum));
+            }
+        }
+    }
+
+    private static byte[] getFileChecksum(File file, int kind) {
+        try (FileInputStream fis = new FileInputStream(file);
+             BufferedInputStream bis = new BufferedInputStream(fis)) {
+            byte[] dataBytes = new byte[512 * 1024];
+            int nread = 0;
+
+            final String algo = getMessageDigestAlgoForChecksumKind(kind);
+            MessageDigest md = MessageDigest.getInstance(algo);
+            while ((nread = bis.read(dataBytes)) != -1) {
+                md.update(dataBytes, 0, nread);
+            }
+
+            return md.digest();
+        } catch (IOException e) {
+            Slog.e(TAG, "Error reading " + file.getAbsolutePath() + " to compute hash.", e);
+            return null;
+        } catch (NoSuchAlgorithmException e) {
+            Slog.e(TAG, "Device does not support MessageDigest algorithm", e);
+            return null;
+        }
+    }
+
+    private static int[] getContentDigestAlgos(boolean needSignatureSha256,
+            boolean needSignatureSha512) {
+        if (needSignatureSha256 && needSignatureSha512) {
+            // Signature block present, but no digests???
+            return new int[]{CONTENT_DIGEST_CHUNKED_SHA256, CONTENT_DIGEST_CHUNKED_SHA512};
+        } else if (needSignatureSha256) {
+            return new int[]{CONTENT_DIGEST_CHUNKED_SHA256};
+        } else {
+            return new int[]{CONTENT_DIGEST_CHUNKED_SHA512};
+        }
+    }
+
+    private static int getChecksumKindForContentDigestAlgo(int contentDigestAlgo) {
+        switch (contentDigestAlgo) {
+            case CONTENT_DIGEST_CHUNKED_SHA256:
+                return PARTIAL_MERKLE_ROOT_1M_SHA256;
+            case CONTENT_DIGEST_CHUNKED_SHA512:
+                return PARTIAL_MERKLE_ROOT_1M_SHA512;
+            default:
+                return -1;
+        }
+    }
+
+    private static void calculatePartialChecksumsIfRequested(Map<Integer, FileChecksum> checksums,
+            String split, File file, int required) {
+        boolean needSignatureSha256 =
+                (required & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0 && !checksums.containsKey(
+                        PARTIAL_MERKLE_ROOT_1M_SHA256);
+        boolean needSignatureSha512 =
+                (required & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0 && !checksums.containsKey(
+                        PARTIAL_MERKLE_ROOT_1M_SHA512);
+        if (!needSignatureSha256 && !needSignatureSha512) {
+            return;
+        }
+
+        try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
+            SignatureInfo signatureInfo = null;
+            try {
+                signatureInfo = ApkSignatureSchemeV3Verifier.findSignature(raf);
+            } catch (SignatureNotFoundException e) {
+                try {
+                    signatureInfo = ApkSignatureSchemeV2Verifier.findSignature(raf);
+                } catch (SignatureNotFoundException ee) {
+                }
+            }
+            if (signatureInfo == null) {
+                Slog.e(TAG, "V2/V3 signatures not found in " + file.getAbsolutePath());
+                return;
+            }
+
+            final int[] digestAlgos = getContentDigestAlgos(needSignatureSha256,
+                    needSignatureSha512);
+            byte[][] digests = ApkSigningBlockUtils.computeContentDigestsPer1MbChunk(digestAlgos,
+                    raf.getFD(), signatureInfo);
+            for (int i = 0, size = digestAlgos.length; i < size; ++i) {
+                int checksumKind = getChecksumKindForContentDigestAlgo(digestAlgos[i]);
+                if (checksumKind != -1) {
+                    checksums.put(checksumKind, new FileChecksum(split, checksumKind, digests[i]));
+                }
+            }
+        } catch (IOException | DigestException e) {
+            Slog.e(TAG, "Error computing hash.", e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c05bc45..bb7b63b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -40,6 +40,7 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
 import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -168,6 +169,7 @@
 import android.content.pm.DataLoaderType;
 import android.content.pm.FallbackCategoryProvider;
 import android.content.pm.FeatureInfo;
+import android.content.pm.FileChecksum;
 import android.content.pm.IDexModuleRegisterCallback;
 import android.content.pm.IPackageChangeObserver;
 import android.content.pm.IPackageDataObserver;
@@ -254,6 +256,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Parcel;
+import android.os.ParcelableException;
 import android.os.PatternMatcher;
 import android.os.PersistableBundle;
 import android.os.Process;
@@ -394,6 +397,7 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -404,7 +408,10 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.PublicKey;
 import java.security.SecureRandom;
+import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -2443,6 +2450,83 @@
         mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
     }
 
+    @Override
+    public void getChecksums(@NonNull String packageName, boolean includeSplits,
+            @PackageManager.FileChecksumKind int optional,
+            @PackageManager.FileChecksumKind int required, @Nullable List trustedInstallers,
+            @NonNull IntentSender statusReceiver, int userId) {
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(statusReceiver);
+
+        final ApplicationInfo applicationInfo = getApplicationInfoInternal(packageName, 0,
+                Binder.getCallingUid(), userId);
+        if (applicationInfo == null) {
+            throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
+        }
+
+        List<Pair<String, File>> filesToChecksum = new ArrayList<>();
+
+        // Adding base split.
+        filesToChecksum.add(Pair.create(null, new File(applicationInfo.sourceDir)));
+
+        // Adding other splits.
+        if (includeSplits && applicationInfo.splitNames != null) {
+            for (int i = 0, size = applicationInfo.splitNames.length; i < size; ++i) {
+                filesToChecksum.add(Pair.create(applicationInfo.splitNames[i],
+                        new File(applicationInfo.splitSourceDirs[i])));
+            }
+        }
+
+        for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
+            final File file = filesToChecksum.get(i).second;
+            if (!file.exists()) {
+                throw new IllegalStateException("File not found: " + file.getPath());
+            }
+        }
+
+        final Certificate[] trustedCerts = (trustedInstallers != null) ? decodeCertificates(
+                trustedInstallers) : null;
+        final Context context = mContext;
+
+        mInjector.getBackgroundExecutor().execute(() -> {
+            final Intent intent = new Intent();
+            List<FileChecksum> result = new ArrayList<>();
+            for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
+                final String split = filesToChecksum.get(i).first;
+                final File file = filesToChecksum.get(i).second;
+                try {
+                    result.addAll(ApkChecksums.getFileChecksums(split, file, optional, required,
+                            trustedCerts));
+                } catch (Throwable e) {
+                    Slog.e(TAG, "Checksum calculation error", e);
+                }
+            }
+            intent.putExtra(EXTRA_CHECKSUMS,
+                    result.toArray(new FileChecksum[result.size()]));
+
+            try {
+                statusReceiver.sendIntent(context, 1, intent, null, null);
+            } catch (SendIntentException e) {
+                Slog.w(TAG, e);
+            }
+        });
+    }
+
+    private static @NonNull Certificate[] decodeCertificates(@NonNull List certs) {
+        try {
+            final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            final Certificate[] result = new Certificate[certs.size()];
+            for (int i = 0, size = certs.size(); i < size; ++i) {
+                final InputStream is = new ByteArrayInputStream((byte[]) certs.get(i));
+                final X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
+                result[i] = cert;
+            }
+            return result;
+        } catch (CertificateException e) {
+            throw ExceptionUtils.propagate(e);
+        }
+    }
+
     /**
      * Gets the type of the external storage a package is installed on.
      * @param packageVolume The storage volume of the package.
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 2b793c8..f204aa2 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -52,6 +52,9 @@
     /** The maximum size of signature file.  This is just to avoid potential abuse. */
     private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192;
 
+    /** SHA256 hash size. */
+    private static final int HASH_SIZE_BYTES = 32;
+
     private static final boolean DEBUG = false;
 
     /** Returns true if the given file looks like containing an fs-verity signature. */
@@ -90,8 +93,23 @@
         return (retval == 1);
     }
 
+    /** Returns hash of a root node for the fs-verity enabled file. */
+    public static byte[] getFsverityRootHash(@NonNull String filePath) {
+        byte[] result = new byte[HASH_SIZE_BYTES];
+        int retval = measureFsverityNative(filePath, result);
+        if (retval < 0) {
+            if (retval != -OsConstants.ENODATA) {
+                Slog.e(TAG, "Failed to measure fs-verity, errno " + -retval + ": " + filePath);
+            }
+            return null;
+        }
+        return result;
+    }
+
     private static native int enableFsverityNative(@NonNull String filePath,
             @NonNull byte[] pkcs7Signature);
+    private static native int measureFsverityNative(@NonNull String filePath,
+            @NonNull byte[] digest);
     private static native int statxForFsverityNative(@NonNull String filePath);
 
     /**
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index 0277f16..46e6f91 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -33,6 +33,8 @@
 
 #include <android-base/unique_fd.h>
 
+#include <type_traits>
+
 namespace android {
 
 namespace {
@@ -53,7 +55,7 @@
 
     fsverity_enable_arg arg = {};
     arg.version = 1;
-    arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
+    arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; // hardcoded in measureFsverity below
     arg.block_size = 4096;
     arg.salt_size = 0;
     arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr);
@@ -85,9 +87,41 @@
     return (out.stx_attributes & STATX_ATTR_VERITY) != 0;
 }
 
+int measureFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath, jbyteArray digest) {
+    static constexpr auto kDigestSha256 = 32;
+    using Storage = std::aligned_storage_t<sizeof(fsverity_digest) + kDigestSha256>;
+
+    Storage bytes;
+    fsverity_digest *data = reinterpret_cast<fsverity_digest *>(&bytes);
+    data->digest_size = kDigestSha256; // the only input/output parameter
+
+    ScopedUtfChars path(env, filePath);
+    ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
+    if (rfd.get() < 0) {
+        return rfd.get();
+    }
+    if (auto err = ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data); err < 0) {
+        return err;
+    }
+
+    if (data->digest_algorithm != FS_VERITY_HASH_ALG_SHA256) {
+        return -EINVAL;
+    }
+
+    if (digest != nullptr && data->digest_size > 0) {
+        auto digestSize = env->GetArrayLength(digest);
+        if (data->digest_size > digestSize) {
+            return -E2BIG;
+        }
+        env->SetByteArrayRegion(digest, 0, data->digest_size, (const jbyte *)data->digest);
+    }
+
+    return 0;
+}
 const JNINativeMethod sMethods[] = {
         {"enableFsverityNative", "(Ljava/lang/String;[B)I", (void *)enableFsverity},
         {"statxForFsverityNative", "(Ljava/lang/String;)I", (void *)statxForFsverity},
+        {"measureFsverityNative", "(Ljava/lang/String;[B)I", (void *)measureFsverity},
 };
 
 }  // namespace