Tests for installer-provided checksums.

Bug: 160605420
Test: atest ChecksumsTest

Change-Id: I112ba859456c4d8425498a5484bd746c5f19360b
diff --git a/tests/tests/content/src/android/content/pm/cts/ChecksumsTest.java b/tests/tests/content/src/android/content/pm/cts/ChecksumsTest.java
index 319f2e9..71067fc 100644
--- a/tests/tests/content/src/android/content/pm/cts/ChecksumsTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/ChecksumsTest.java
@@ -16,15 +16,17 @@
 
 package android.content.pm.cts;
 
+import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA256;
+import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA512;
+import static android.content.pm.Checksum.WHOLE_MD5;
+import static android.content.pm.Checksum.WHOLE_MERKLE_ROOT_4K_SHA256;
+import static android.content.pm.Checksum.WHOLE_SHA1;
+import static android.content.pm.Checksum.WHOLE_SHA256;
+import static android.content.pm.Checksum.WHOLE_SHA512;
 import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
-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.GET_SIGNING_CERTIFICATES;
+import static android.content.pm.PackageManager.TRUST_ALL;
 import static android.content.pm.PackageManager.TRUST_NONE;
-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 org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
@@ -37,13 +39,20 @@
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
-import android.content.pm.FileChecksum;
+import android.content.pm.ApkChecksum;
+import android.content.pm.Checksum;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.Session;
+import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager;
+import android.content.pm.Signature;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 import android.platform.test.annotations.AppModeFull;
+import android.util.ExceptionUtils;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -56,12 +65,20 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+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.List;
 import java.util.Objects;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
@@ -71,6 +88,7 @@
 @RunWith(AndroidJUnit4.class)
 @AppModeFull
 public class ChecksumsTest {
+    private static final String CTS_PACKAGE_NAME = "android.content.cts";
     private static final String V2V3_PACKAGE_NAME = "android.content.cts";
     private static final String V4_PACKAGE_NAME = "com.example.helloworld";
     private static final String FIXED_PACKAGE_NAME = "android.appsecurity.cts.tinyapp";
@@ -90,16 +108,29 @@
             "CtsPkgInstallTinyAppV2V3V4-Sha512withEC.apk";
     private static final String TEST_FIXED_APK_VERITY = "CtsPkgInstallTinyAppV2V3V4-Verity.apk";
 
+    private static final String TEST_FIXED_APK_V2_SHA256 =
+            "1eec9e86e322b8d7e48e255fc3f2df2dbc91036e63982ff9850597c6a37bbeb3";
+    private static final String TEST_FIXED_APK_SHA256 =
+            "91aa30c1ce8d0474052f71cb8210691d41f534989c5521e27e794ec4f754c5ef";
+    private static final String TEST_FIXED_APK_MD5 = "c19868da017dc01467169f8ea7c5bc57";
+    private static final Checksum[] TEST_FIXED_APK_DIGESTS = new Checksum[]{new Checksum(
+            WHOLE_SHA256, hexStringToBytes(TEST_FIXED_APK_SHA256)), new Checksum(WHOLE_MD5,
+            hexStringToBytes(TEST_FIXED_APK_MD5))};
+
     private static final int ALL_CHECKSUMS =
             WHOLE_MERKLE_ROOT_4K_SHA256 | WHOLE_MD5 | WHOLE_SHA1 | WHOLE_SHA256 | WHOLE_SHA512
                     | PARTIAL_MERKLE_ROOT_1M_SHA256 | PARTIAL_MERKLE_ROOT_1M_SHA512;
 
+    private static UiAutomation getUiAutomation() {
+        return InstrumentationRegistry.getInstrumentation().getUiAutomation();
+    }
+
     private static PackageManager getPackageManager() {
         return InstrumentationRegistry.getContext().getPackageManager();
     }
 
-    private static UiAutomation getUiAutomation() {
-        return InstrumentationRegistry.getInstrumentation().getUiAutomation();
+    private static PackageInstaller getPackageInstaller() {
+        return getPackageManager().getPackageInstaller();
     }
 
     @Before
@@ -123,7 +154,7 @@
         LocalIntentReceiver receiver = new LocalIntentReceiver();
         PackageManager pm = getPackageManager();
         pm.getChecksums(V2V3_PACKAGE_NAME, true, 0, TRUST_NONE, receiver.getIntentSender());
-        FileChecksum[] checksums = receiver.getResult();
+        ApkChecksum[] checksums = receiver.getResult();
         assertNotNull(checksums);
         assertEquals(checksums.length, 1);
         assertEquals(checksums[0].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA256);
@@ -138,7 +169,7 @@
         LocalIntentReceiver receiver = new LocalIntentReceiver();
         PackageManager pm = getPackageManager();
         pm.getChecksums(V4_PACKAGE_NAME, true, 0, TRUST_NONE, receiver.getIntentSender());
-        FileChecksum[] checksums = receiver.getResult();
+        ApkChecksum[] checksums = receiver.getResult();
         assertNotNull(checksums);
         assertEquals(checksums.length, 6);
         // v2/v3 signature use 1M merkle tree.
@@ -164,13 +195,12 @@
         LocalIntentReceiver receiver = new LocalIntentReceiver();
         PackageManager pm = getPackageManager();
         pm.getChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE, receiver.getIntentSender());
-        FileChecksum[] checksums = receiver.getResult();
+        ApkChecksum[] checksums = receiver.getResult();
         assertNotNull(checksums);
         assertEquals(checksums.length, 1);
         // v2/v3 signature use 1M merkle tree.
         assertEquals(checksums[0].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA256);
-        assertEquals(bytesToHexString(checksums[0].getValue()),
-                "1eec9e86e322b8d7e48e255fc3f2df2dbc91036e63982ff9850597c6a37bbeb3");
+        assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_V2_SHA256);
         assertNull(checksums[0].getSourceCertificate());
     }
 
@@ -182,7 +212,7 @@
         LocalIntentReceiver receiver = new LocalIntentReceiver();
         PackageManager pm = getPackageManager();
         pm.getChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE, receiver.getIntentSender());
-        FileChecksum[] checksums = receiver.getResult();
+        ApkChecksum[] checksums = receiver.getResult();
         assertNotNull(checksums);
         assertEquals(checksums.length, 0);
     }
@@ -195,7 +225,7 @@
         LocalIntentReceiver receiver = new LocalIntentReceiver();
         PackageManager pm = getPackageManager();
         pm.getChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE, receiver.getIntentSender());
-        FileChecksum[] checksums = receiver.getResult();
+        ApkChecksum[] checksums = receiver.getResult();
         assertNotNull(checksums);
         assertEquals(checksums.length, 1);
         // v2/v3 signature use 1M merkle tree.
@@ -214,7 +244,7 @@
         LocalIntentReceiver receiver = new LocalIntentReceiver();
         PackageManager pm = getPackageManager();
         pm.getChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE, receiver.getIntentSender());
-        FileChecksum[] checksums = receiver.getResult();
+        ApkChecksum[] checksums = receiver.getResult();
         assertNotNull(checksums);
         // No usable hashes as verity-in-v2-signature does not cover the whole file.
         assertEquals(checksums.length, 0);
@@ -226,7 +256,7 @@
         PackageManager pm = getPackageManager();
         pm.getChecksums(V2V3_PACKAGE_NAME, true, ALL_CHECKSUMS, TRUST_NONE,
                 receiver.getIntentSender());
-        FileChecksum[] checksums = receiver.getResult();
+        ApkChecksum[] checksums = receiver.getResult();
         assertNotNull(checksums);
         assertEquals(checksums.length, 7);
         assertEquals(checksums[0].getKind(), WHOLE_MERKLE_ROOT_4K_SHA256);
@@ -247,27 +277,25 @@
         PackageManager pm = getPackageManager();
         pm.getChecksums(FIXED_PACKAGE_NAME, true, ALL_CHECKSUMS, TRUST_NONE,
                 receiver.getIntentSender());
-        FileChecksum[] checksums = receiver.getResult();
+        ApkChecksum[] checksums = receiver.getResult();
         assertNotNull(checksums);
         assertEquals(checksums.length, 7);
         assertEquals(checksums[0].getKind(), WHOLE_MERKLE_ROOT_4K_SHA256);
         assertEquals(bytesToHexString(checksums[0].getValue()),
                 "90553b8d221ab1b900b242a93e4cc659ace3a2ff1d5c62e502488b385854e66a");
         assertEquals(checksums[1].getKind(), WHOLE_MD5);
-        assertEquals(bytesToHexString(checksums[1].getValue()), "c19868da017dc01467169f8ea7c5bc57");
+        assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_MD5);
         assertEquals(checksums[2].getKind(), WHOLE_SHA1);
         assertEquals(bytesToHexString(checksums[2].getValue()),
                 "331eef6bc57671de28cbd7e32089d047285ade6a");
         assertEquals(checksums[3].getKind(), WHOLE_SHA256);
-        assertEquals(bytesToHexString(checksums[3].getValue()),
-                "91aa30c1ce8d0474052f71cb8210691d41f534989c5521e27e794ec4f754c5ef");
+        assertEquals(bytesToHexString(checksums[3].getValue()), TEST_FIXED_APK_SHA256);
         assertEquals(checksums[4].getKind(), WHOLE_SHA512);
         assertEquals(bytesToHexString(checksums[4].getValue()),
                 "b59467fe578ebc81974ab3aaa1e0d2a76fef3e4ea7212a6f2885cec1af5253571"
                         + "1e2e94496224cae3eba8dc992144ade321540ebd458ec5b9e6a4cc51170e018");
         assertEquals(checksums[5].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA256);
-        assertEquals(bytesToHexString(checksums[5].getValue()),
-                "1eec9e86e322b8d7e48e255fc3f2df2dbc91036e63982ff9850597c6a37bbeb3");
+        assertEquals(bytesToHexString(checksums[5].getValue()), TEST_FIXED_APK_V2_SHA256);
         assertEquals(checksums[6].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA512);
         assertEquals(bytesToHexString(checksums[6].getValue()),
                 "ef80a8630283f60108e8557c924307d0ccdfb6bbbf2c0176bd49af342f43bc84"
@@ -283,7 +311,7 @@
         PackageManager pm = getPackageManager();
         pm.getChecksums(FIXED_PACKAGE_NAME, true, ALL_CHECKSUMS, TRUST_NONE,
                 receiver.getIntentSender());
-        FileChecksum[] checksums = receiver.getResult();
+        ApkChecksum[] checksums = receiver.getResult();
         assertNotNull(checksums);
         assertEquals(checksums.length, 5);
         assertEquals(checksums[0].getKind(), WHOLE_MERKLE_ROOT_4K_SHA256);
@@ -314,7 +342,7 @@
         LocalIntentReceiver receiver = new LocalIntentReceiver();
         PackageManager pm = getPackageManager();
         pm.getChecksums(V4_PACKAGE_NAME, true, 0, TRUST_NONE, receiver.getIntentSender());
-        FileChecksum[] checksums = receiver.getResult();
+        ApkChecksum[] checksums = receiver.getResult();
         assertNotNull(checksums);
         assertEquals(checksums.length, 1);
         assertEquals(checksums[0].getKind(), WHOLE_MERKLE_ROOT_4K_SHA256);
@@ -331,7 +359,7 @@
         LocalIntentReceiver receiver = new LocalIntentReceiver();
         PackageManager pm = getPackageManager();
         pm.getChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE, receiver.getIntentSender());
-        FileChecksum[] checksums = receiver.getResult();
+        ApkChecksum[] checksums = receiver.getResult();
         assertNotNull(checksums);
         assertEquals(checksums.length, 1);
         assertEquals(checksums[0].getKind(), WHOLE_MERKLE_ROOT_4K_SHA256);
@@ -351,33 +379,323 @@
         PackageManager pm = getPackageManager();
         pm.getChecksums(FIXED_PACKAGE_NAME, true, ALL_CHECKSUMS, TRUST_NONE,
                 receiver.getIntentSender());
-        FileChecksum[] checksums = receiver.getResult();
+        ApkChecksum[] checksums = receiver.getResult();
         assertNotNull(checksums);
         assertEquals(checksums.length, 7);
         assertEquals(checksums[0].getKind(), WHOLE_MERKLE_ROOT_4K_SHA256);
         assertEquals(bytesToHexString(checksums[0].getValue()),
                 "90553b8d221ab1b900b242a93e4cc659ace3a2ff1d5c62e502488b385854e66a");
         assertEquals(checksums[1].getKind(), WHOLE_MD5);
-        assertEquals(bytesToHexString(checksums[1].getValue()), "c19868da017dc01467169f8ea7c5bc57");
+        assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_MD5);
         assertEquals(checksums[2].getKind(), WHOLE_SHA1);
         assertEquals(bytesToHexString(checksums[2].getValue()),
                 "331eef6bc57671de28cbd7e32089d047285ade6a");
         assertEquals(checksums[3].getKind(), WHOLE_SHA256);
-        assertEquals(bytesToHexString(checksums[3].getValue()),
-                "91aa30c1ce8d0474052f71cb8210691d41f534989c5521e27e794ec4f754c5ef");
+        assertEquals(bytesToHexString(checksums[3].getValue()), TEST_FIXED_APK_SHA256);
         assertEquals(checksums[4].getKind(), WHOLE_SHA512);
         assertEquals(bytesToHexString(checksums[4].getValue()),
                 "b59467fe578ebc81974ab3aaa1e0d2a76fef3e4ea7212a6f2885cec1af5253571"
                         + "1e2e94496224cae3eba8dc992144ade321540ebd458ec5b9e6a4cc51170e018");
         assertEquals(checksums[5].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA256);
-        assertEquals(bytesToHexString(checksums[5].getValue()),
-                "1eec9e86e322b8d7e48e255fc3f2df2dbc91036e63982ff9850597c6a37bbeb3");
+        assertEquals(bytesToHexString(checksums[5].getValue()), TEST_FIXED_APK_V2_SHA256);
         assertEquals(checksums[6].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA512);
         assertEquals(bytesToHexString(checksums[6].getValue()),
                 "ef80a8630283f60108e8557c924307d0ccdfb6bbbf2c0176bd49af342f43bc84"
                         + "5f2888afcb71524196dda0d6dd16a6a3292bb75b431b8ff74fb60d796e882f80");
     }
 
+    @Test
+    public void testInstallerChecksumsTrustNone() throws Exception {
+        installApkWithChecksums(TEST_FIXED_APK_DIGESTS);
+
+        LocalIntentReceiver receiver = new LocalIntentReceiver();
+        PackageManager pm = getPackageManager();
+        pm.getChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE, receiver.getIntentSender());
+        ApkChecksum[] checksums = receiver.getResult();
+        assertNotNull(checksums);
+        assertEquals(checksums.length, 1);
+        assertEquals(checksums[0].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA256);
+        assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_V2_SHA256);
+        assertNull(checksums[0].getSourceCertificate());
+    }
+
+    @Test
+    public void testInstallerChecksumsTrustAll() throws Exception {
+        installApkWithChecksums(TEST_FIXED_APK_DIGESTS);
+
+        LocalIntentReceiver receiver = new LocalIntentReceiver();
+        PackageManager pm = getPackageManager();
+        pm.getChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_ALL, receiver.getIntentSender());
+        ApkChecksum[] checksums = receiver.getResult();
+        assertNotNull(checksums);
+        // v2/v3+installer provided.
+        assertEquals(checksums.length, 3);
+
+        assertEquals(checksums[0].getKind(), WHOLE_MD5);
+        assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5);
+        assertEquals(checksums[0].getSplitName(), null);
+        assertNotNull(checksums[0].getSourceCertificate());
+        assertEquals(checksums[1].getKind(), WHOLE_SHA256);
+        assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256);
+        assertEquals(checksums[1].getSplitName(), null);
+        assertNotNull(checksums[1].getSourceCertificate());
+        assertEquals(checksums[2].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA256);
+        assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256);
+        assertEquals(checksums[2].getSplitName(), null);
+        assertNull(checksums[2].getSourceCertificate());
+    }
+
+    @Test
+    public void testInstallerChecksumsTrustInstaller() throws Exception {
+        installApkWithChecksums(TEST_FIXED_APK_DIGESTS);
+
+        // Using the installer's certificate(s).
+        PackageManager pm = getPackageManager();
+        PackageInfo packageInfo = pm.getPackageInfo(CTS_PACKAGE_NAME, GET_SIGNING_CERTIFICATES);
+        final List<Certificate> signatures = convertSignaturesToCertificates(
+                packageInfo.signingInfo.getApkContentsSigners());
+
+        LocalIntentReceiver receiver = new LocalIntentReceiver();
+        pm.getChecksums(FIXED_PACKAGE_NAME, true, 0, signatures, receiver.getIntentSender());
+        ApkChecksum[] checksums = receiver.getResult();
+        assertNotNull(checksums);
+        assertEquals(checksums.length, 3);
+        assertEquals(checksums[0].getKind(), WHOLE_MD5);
+        assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5);
+        assertEquals(checksums[0].getSplitName(), null);
+        assertNotNull(checksums[0].getSourceCertificate());
+        assertEquals(checksums[1].getKind(), WHOLE_SHA256);
+        assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256);
+        assertEquals(checksums[1].getSplitName(), null);
+        assertNotNull(checksums[1].getSourceCertificate());
+        assertEquals(checksums[2].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA256);
+        assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256);
+        assertEquals(checksums[2].getSplitName(), null);
+        assertNull(checksums[2].getSourceCertificate());
+    }
+
+    @Test
+    public void testInstallerChecksumsTrustWrongInstaller() throws Exception {
+        installApkWithChecksums(TEST_FIXED_APK_DIGESTS);
+
+        // Using certificates from a security app, not the installer (us).
+        PackageManager pm = getPackageManager();
+        PackageInfo packageInfo = pm.getPackageInfo(FIXED_PACKAGE_NAME, GET_SIGNING_CERTIFICATES);
+        final List<Certificate> signatures = convertSignaturesToCertificates(
+                packageInfo.signingInfo.getApkContentsSigners());
+
+        LocalIntentReceiver receiver = new LocalIntentReceiver();
+        pm.getChecksums(FIXED_PACKAGE_NAME, true, 0, signatures, receiver.getIntentSender());
+        ApkChecksum[] checksums = receiver.getResult();
+        assertNotNull(checksums);
+        assertEquals(checksums.length, 1);
+        assertEquals(checksums[0].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA256);
+        assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_V2_SHA256);
+        assertNull(checksums[0].getSourceCertificate());
+    }
+
+    @Test
+    public void testInstallerChecksumsTrustAllWrongName() throws Exception {
+        CommitIntentReceiver.checkFailure(
+                installApkWithChecksums(TEST_FIXED_APK, "apk", "wrong_name",
+                        TEST_FIXED_APK_DIGESTS),
+                "INSTALL_FAILED_SESSION_INVALID: Invalid checksum name(s): wrong_name");
+    }
+
+    @Test
+    public void testInstallerChecksumsUpdate() throws Exception {
+        Checksum[] digests_base = new Checksum[]{new Checksum(WHOLE_SHA256, hexStringToBytes(
+                "ed8c7ae1220fe16d558e00cfc37256e6f7088ab90eb04c1bfcb39922a8a5248e")),
+                new Checksum(WHOLE_MD5, hexStringToBytes("dd93e23bb8cdab0382fdca0d21a4f1cb"))};
+        Checksum[] digests_split0 = new Checksum[]{new Checksum(WHOLE_SHA256, hexStringToBytes(
+                "bd9b095a49a9068498b018ce8cb7cc18d411b13a5a5f7fb417d2ff9808ae838e")),
+                new Checksum(WHOLE_MD5, hexStringToBytes("f6430e1b795ce2658c49e68d15316b2d"))};
+        Checksum[] digests_split1 = new Checksum[]{new Checksum(WHOLE_SHA256, hexStringToBytes(
+                "f16898f43990c14585a900eda345c3a236c6224f63920d69cfe8a7afbc0c0ccf")),
+                new Checksum(WHOLE_MD5, hexStringToBytes("d1f4b00d034994663e84f907fe4bb664"))};
+
+        // Original package checksums: base + split0.
+        getUiAutomation().adoptShellPermissionIdentity();
+        try {
+            final PackageInstaller installer = getPackageInstaller();
+            final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+
+            final int sessionId = installer.createSession(params);
+            Session session = installer.openSession(sessionId);
+
+            writeFileToSession(session, "hw5", TEST_V4_APK);
+            session.addChecksums("hw5", Arrays.asList(digests_base));
+
+            writeFileToSession(session, "hw5_split0", TEST_V4_SPLIT0);
+            session.addChecksums("hw5_split0", Arrays.asList(digests_split0));
+
+            CommitIntentReceiver receiver = new CommitIntentReceiver();
+            session.commit(receiver.getIntentSender());
+            CommitIntentReceiver.checkSuccess(receiver.getResult());
+        } finally {
+            getUiAutomation().dropShellPermissionIdentity();
+        }
+
+        {
+            LocalIntentReceiver receiver = new LocalIntentReceiver();
+            PackageManager pm = getPackageManager();
+            pm.getChecksums(V4_PACKAGE_NAME, true, 0, TRUST_ALL, receiver.getIntentSender());
+            ApkChecksum[] checksums = receiver.getResult();
+            assertNotNull(checksums);
+            assertEquals(checksums.length, 6);
+            // base
+            assertEquals(checksums[0].getKind(), WHOLE_MD5);
+            assertEquals(checksums[0].getSplitName(), null);
+            assertEquals(bytesToHexString(checksums[0].getValue()),
+                    "dd93e23bb8cdab0382fdca0d21a4f1cb");
+            assertNotNull(checksums[0].getSourceCertificate());
+            assertEquals(checksums[1].getKind(), WHOLE_SHA256);
+            assertEquals(checksums[1].getSplitName(), null);
+            assertEquals(bytesToHexString(checksums[1].getValue()),
+                    "ed8c7ae1220fe16d558e00cfc37256e6f7088ab90eb04c1bfcb39922a8a5248e");
+            assertNotNull(checksums[1].getSourceCertificate());
+            assertEquals(checksums[2].getSplitName(), null);
+            assertEquals(checksums[2].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA256);
+            assertNull(checksums[2].getSourceCertificate());
+            // split0
+            assertEquals(checksums[3].getKind(), WHOLE_MD5);
+            assertEquals(checksums[3].getSplitName(), "config.hdpi");
+            assertEquals(bytesToHexString(checksums[3].getValue()),
+                    "f6430e1b795ce2658c49e68d15316b2d");
+            assertNotNull(checksums[3].getSourceCertificate());
+            assertEquals(checksums[4].getKind(), WHOLE_SHA256);
+            assertEquals(checksums[4].getSplitName(), "config.hdpi");
+            assertEquals(bytesToHexString(checksums[4].getValue()),
+                    "bd9b095a49a9068498b018ce8cb7cc18d411b13a5a5f7fb417d2ff9808ae838e");
+            assertNotNull(checksums[4].getSourceCertificate());
+            assertEquals(checksums[5].getSplitName(), "config.hdpi");
+            assertEquals(checksums[5].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA256);
+            assertNull(checksums[5].getSourceCertificate());
+        }
+
+        // Update the package with one split+checksums and another split without checksums.
+        getUiAutomation().adoptShellPermissionIdentity();
+        try {
+            final PackageInstaller installer = getPackageInstaller();
+            final SessionParams params = new SessionParams(SessionParams.MODE_INHERIT_EXISTING);
+            params.setAppPackageName(V4_PACKAGE_NAME);
+
+            final int sessionId = installer.createSession(params);
+            Session session = installer.openSession(sessionId);
+
+            writeFileToSession(session, "hw5_split1", TEST_V4_SPLIT1);
+            session.addChecksums("hw5_split1", Arrays.asList(digests_split1));
+
+            writeFileToSession(session, "hw5_split2", TEST_V4_SPLIT2);
+
+            CommitIntentReceiver receiver = new CommitIntentReceiver();
+            session.commit(receiver.getIntentSender());
+            CommitIntentReceiver.checkSuccess(receiver.getResult());
+        } finally {
+            getUiAutomation().dropShellPermissionIdentity();
+        }
+
+        {
+            LocalIntentReceiver receiver = new LocalIntentReceiver();
+            PackageManager pm = getPackageManager();
+            pm.getChecksums(V4_PACKAGE_NAME, true, 0, TRUST_ALL, receiver.getIntentSender());
+            ApkChecksum[] checksums = receiver.getResult();
+            assertNotNull(checksums);
+            assertEquals(checksums.length, 10);
+            // base
+            assertEquals(checksums[0].getKind(), WHOLE_MD5);
+            assertEquals(checksums[0].getSplitName(), null);
+            assertEquals(bytesToHexString(checksums[0].getValue()),
+                    "dd93e23bb8cdab0382fdca0d21a4f1cb");
+            assertNotNull(checksums[0].getSourceCertificate());
+            assertEquals(checksums[1].getKind(), WHOLE_SHA256);
+            assertEquals(checksums[1].getSplitName(), null);
+            assertEquals(bytesToHexString(checksums[1].getValue()),
+                    "ed8c7ae1220fe16d558e00cfc37256e6f7088ab90eb04c1bfcb39922a8a5248e");
+            assertNotNull(checksums[1].getSourceCertificate());
+            assertEquals(checksums[2].getSplitName(), null);
+            assertEquals(checksums[2].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA256);
+            assertNull(checksums[2].getSourceCertificate());
+            // split0
+            assertEquals(checksums[3].getKind(), WHOLE_MD5);
+            assertEquals(checksums[3].getSplitName(), "config.hdpi");
+            assertEquals(bytesToHexString(checksums[3].getValue()),
+                    "f6430e1b795ce2658c49e68d15316b2d");
+            assertNotNull(checksums[3].getSourceCertificate());
+            assertEquals(checksums[4].getKind(), WHOLE_SHA256);
+            assertEquals(checksums[4].getSplitName(), "config.hdpi");
+            assertEquals(bytesToHexString(checksums[4].getValue()),
+                    "bd9b095a49a9068498b018ce8cb7cc18d411b13a5a5f7fb417d2ff9808ae838e");
+            assertNotNull(checksums[4].getSourceCertificate());
+            assertEquals(checksums[5].getSplitName(), "config.hdpi");
+            assertEquals(checksums[5].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA256);
+            assertNull(checksums[5].getSourceCertificate());
+            // split1
+            assertEquals(checksums[6].getKind(), WHOLE_MD5);
+            assertEquals(checksums[6].getSplitName(), "config.mdpi");
+            assertEquals(bytesToHexString(checksums[6].getValue()),
+                    "d1f4b00d034994663e84f907fe4bb664");
+            assertNotNull(checksums[6].getSourceCertificate());
+            assertEquals(checksums[7].getKind(), WHOLE_SHA256);
+            assertEquals(checksums[7].getSplitName(), "config.mdpi");
+            assertEquals(bytesToHexString(checksums[7].getValue()),
+                    "f16898f43990c14585a900eda345c3a236c6224f63920d69cfe8a7afbc0c0ccf");
+            assertNotNull(checksums[7].getSourceCertificate());
+            assertEquals(checksums[8].getSplitName(), "config.mdpi");
+            assertEquals(checksums[8].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA256);
+            assertNull(checksums[8].getSourceCertificate());
+            // split2
+            assertEquals(checksums[9].getSplitName(), "config.xhdpi");
+            assertEquals(checksums[9].getKind(), PARTIAL_MERKLE_ROOT_1M_SHA256);
+            assertNull(checksums[9].getSourceCertificate());
+        }
+    }
+
+    private List<Certificate> convertSignaturesToCertificates(Signature[] signatures) {
+        try {
+            final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            ArrayList<Certificate> certs = new ArrayList<>(signatures.length);
+            for (Signature signature : signatures) {
+                final InputStream is = new ByteArrayInputStream(signature.toByteArray());
+                final X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
+                certs.add(cert);
+            }
+            return certs;
+        } catch (CertificateException e) {
+            throw ExceptionUtils.propagate(e);
+        }
+    }
+
+    private void installApkWithChecksums(Checksum[] checksums) throws Exception {
+        installApkWithChecksums("file", "file", checksums);
+    }
+
+    private void installApkWithChecksums(String apkName, String checksumsName, Checksum[] checksums)
+            throws Exception {
+        CommitIntentReceiver.checkSuccess(
+                installApkWithChecksums(TEST_FIXED_APK, apkName, checksumsName, checksums));
+    }
+
+    private Intent installApkWithChecksums(String apk, String apkName,
+            String checksumsName, Checksum[] checksums) throws Exception {
+        getUiAutomation().adoptShellPermissionIdentity();
+        try {
+            final PackageInstaller installer = getPackageInstaller();
+            final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+
+            final int sessionId = installer.createSession(params);
+            Session session = installer.openSession(sessionId);
+            writeFileToSession(session, apkName, apk);
+            session.addChecksums(checksumsName, Arrays.asList(checksums));
+
+            CommitIntentReceiver receiver = new CommitIntentReceiver();
+            session.commit(receiver.getIntentSender());
+            return receiver.getResult();
+        } finally {
+            getUiAutomation().dropShellPermissionIdentity();
+        }
+    }
+
     private static String readFullStream(InputStream inputStream) throws IOException {
         ByteArrayOutputStream result = new ByteArrayOutputStream();
         writeFullStream(inputStream, result, -1);
@@ -436,6 +754,15 @@
                 executeShellCommand("pm install-incremental -t -g " + String.join(" ", splits)));
     }
 
+    private static void writeFileToSession(PackageInstaller.Session session, String name,
+            String apk) throws IOException {
+        File file = new File(createApkPath(apk));
+        try (OutputStream os = session.openWrite(name, 0, file.length());
+             InputStream is = new FileInputStream(file)) {
+            writeFullStream(is, os, file.length());
+        }
+    }
+
     private String uninstallPackageSilently(String packageName) throws IOException {
         return executeShellCommand("pm uninstall " + packageName);
     }
@@ -452,13 +779,17 @@
         return HexDump.toHexString(bytes, 0, bytes.length, /*upperCase=*/ false);
     }
 
+    @Nonnull
+    private static byte[] hexStringToBytes(String hexString) {
+        return HexDump.hexStringToByteArray(hexString);
+    }
 
     private boolean checkIncrementalDeliveryFeature() {
         return getPackageManager().hasSystemFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY);
     }
 
     private static class LocalIntentReceiver {
-        private final LinkedBlockingQueue<FileChecksum[]> mResult = new LinkedBlockingQueue<>();
+        private final LinkedBlockingQueue<ApkChecksum[]> mResult = new LinkedBlockingQueue<>();
 
         private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
             @Override
@@ -467,9 +798,9 @@
                     Bundle options) {
                 Parcelable[] parcelables = intent.getParcelableArrayExtra(EXTRA_CHECKSUMS);
                 assertNotNull(parcelables);
-                FileChecksum[] checksums = Arrays.copyOf(parcelables, parcelables.length,
-                        FileChecksum[].class);
-                Arrays.sort(checksums, (FileChecksum lhs, FileChecksum rhs) ->  {
+                ApkChecksum[] checksums = Arrays.copyOf(parcelables, parcelables.length,
+                        ApkChecksum[].class);
+                Arrays.sort(checksums, (ApkChecksum lhs, ApkChecksum rhs) ->  {
                     final String lhsSplit = lhs.getSplitName();
                     final String rhsSplit = rhs.getSplitName();
                     if (Objects.equals(lhsSplit, rhsSplit)) {
@@ -491,7 +822,7 @@
             return new IntentSender((IIntentSender) mLocalSender);
         }
 
-        public FileChecksum[] getResult() {
+        public ApkChecksum[] getResult() {
             try {
                 return mResult.poll(5, TimeUnit.SECONDS);
             } catch (InterruptedException e) {
@@ -499,4 +830,48 @@
             }
         }
     }
+
+    private static class CommitIntentReceiver {
+        private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
+
+        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+            @Override
+            public void send(int code, Intent intent, String resolvedType, IBinder allowlistToken,
+                    IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+                try {
+                    mResult.offer(intent, 5, TimeUnit.SECONDS);
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+
+        public IntentSender getIntentSender() {
+            return new IntentSender((IIntentSender) mLocalSender);
+        }
+
+        public Intent getResult() {
+            try {
+                return mResult.take();
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public static void checkSuccess(Intent result) {
+            final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                    PackageInstaller.STATUS_FAILURE);
+            assertEquals(status, PackageInstaller.STATUS_SUCCESS,
+                    result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + " OR "
+                            + result.getExtras().get(Intent.EXTRA_INTENT));
+        }
+
+        public static void checkFailure(Intent result, String expectedStatus) {
+            final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                    PackageInstaller.STATUS_FAILURE);
+            assertEquals(status, PackageInstaller.STATUS_FAILURE);
+            assertEquals(result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE),
+                    expectedStatus);
+        }
+    }
 }