Installer digests for Incremental installations.

Bug: 160605420
Test: atest ChecksumsTest
Change-Id: I9d46c218cccf87781e9b33711c4d02d94bf824f5
diff --git a/api/current.txt b/api/current.txt
index 0f4cffc..405f6b7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11451,6 +11451,7 @@
     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 getSourcePackageName();
     method @Nullable public String getSplitName();
     method @NonNull public byte[] getValue();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -11559,6 +11560,7 @@
   }
 
   public final class Checksum implements android.os.Parcelable {
+    ctor public Checksum(int, @NonNull byte[]);
     method public int describeContents();
     method public int getKind();
     method @NonNull public byte[] getValue();
@@ -11566,9 +11568,9 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Checksum> CREATOR;
     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 WHOLE_MD5 = 2; // 0x2
+    field @Deprecated 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 @Deprecated 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
   }
@@ -11864,7 +11866,7 @@
 
   public static class PackageInstaller.Session implements java.io.Closeable {
     method public void abandon();
-    method public void addChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>) throws java.io.IOException;
+    method @Deprecated public void addChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>) throws java.io.IOException;
     method public void addChildSessionId(int);
     method public void close();
     method public void commit(@NonNull android.content.IntentSender);
diff --git a/core/java/android/content/pm/ApkChecksum.java b/core/java/android/content/pm/ApkChecksum.java
index e087c44..bf67841 100644
--- a/core/java/android/content/pm/ApkChecksum.java
+++ b/core/java/android/content/pm/ApkChecksum.java
@@ -50,7 +50,11 @@
      */
     private final @NonNull Checksum mChecksum;
     /**
-     * For Installer-provided checksums, certificate of the Installer/AppStore.
+     * For Installer-provided checksums, package name of the Installer.
+     */
+    private final @Nullable String mSourcePackageName;
+    /**
+     * For Installer-provided checksums, certificate of the Installer.
      */
     private final @Nullable byte[] mSourceCertificate;
 
@@ -61,7 +65,7 @@
      */
     public ApkChecksum(@Nullable String splitName, @Checksum.Kind int kind,
             @NonNull byte[] value) {
-        this(splitName, new Checksum(kind, value), (byte[]) null);
+        this(splitName, new Checksum(kind, value), (String) null, (byte[]) null);
     }
 
     /**
@@ -69,10 +73,10 @@
      *
      * @hide
      */
-    public ApkChecksum(@Nullable String splitName, @Checksum.Kind int kind,
-            @NonNull byte[] value, @Nullable Certificate sourceCertificate)
+    public ApkChecksum(@Nullable String splitName, @Checksum.Kind int kind, @NonNull byte[] value,
+            @Nullable String sourcePackageName, @Nullable Certificate sourceCertificate)
             throws CertificateEncodingException {
-        this(splitName, new Checksum(kind, value),
+        this(splitName, new Checksum(kind, value), sourcePackageName,
                 (sourceCertificate != null) ? sourceCertificate.getEncoded() : null);
     }
 
@@ -136,19 +140,23 @@
      *   Checksum for which split. Null indicates base.apk.
      * @param checksum
      *   Checksum.
+     * @param sourcePackageName
+     *   For Installer-provided checksums, package name of the Installer.
      * @param sourceCertificate
-     *   For Installer-provided checksums, certificate of the Installer/AppStore.
+     *   For Installer-provided checksums, certificate of the Installer.
      * @hide
      */
     @DataClass.Generated.Member
     public ApkChecksum(
             @Nullable String splitName,
             @NonNull Checksum checksum,
+            @Nullable String sourcePackageName,
             @Nullable byte[] sourceCertificate) {
         this.mSplitName = splitName;
         this.mChecksum = checksum;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mChecksum);
+        this.mSourcePackageName = sourcePackageName;
         this.mSourceCertificate = sourceCertificate;
 
         // onConstructed(); // You can define this method to get a callback
@@ -162,6 +170,14 @@
         return mSplitName;
     }
 
+    /**
+     * For Installer-provided checksums, package name of the Installer.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getSourcePackageName() {
+        return mSourcePackageName;
+    }
+
     @Override
     @DataClass.Generated.Member
     public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -170,10 +186,12 @@
 
         byte flg = 0;
         if (mSplitName != null) flg |= 0x1;
-        if (mSourceCertificate != null) flg |= 0x4;
+        if (mSourcePackageName != null) flg |= 0x4;
+        if (mSourceCertificate != null) flg |= 0x8;
         dest.writeByte(flg);
         if (mSplitName != null) dest.writeString(mSplitName);
         dest.writeTypedObject(mChecksum, flags);
+        if (mSourcePackageName != null) dest.writeString(mSourcePackageName);
         if (mSourceCertificate != null) dest.writeByteArray(mSourceCertificate);
     }
 
@@ -191,12 +209,14 @@
         byte flg = in.readByte();
         String splitName = (flg & 0x1) == 0 ? null : in.readString();
         Checksum checksum = (Checksum) in.readTypedObject(Checksum.CREATOR);
-        byte[] sourceCertificate = (flg & 0x4) == 0 ? null : in.createByteArray();
+        String sourcePackageName = (flg & 0x4) == 0 ? null : in.readString();
+        byte[] sourceCertificate = (flg & 0x8) == 0 ? null : in.createByteArray();
 
         this.mSplitName = splitName;
         this.mChecksum = checksum;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mChecksum);
+        this.mSourcePackageName = sourcePackageName;
         this.mSourceCertificate = sourceCertificate;
 
         // onConstructed(); // You can define this method to get a callback
@@ -217,10 +237,10 @@
     };
 
     @DataClass.Generated(
-            time = 1599845645160L,
+            time = 1600407436287L,
             codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/content/pm/ApkChecksum.java",
-            inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.NonNull android.content.pm.Checksum mChecksum\nprivate final @android.annotation.Nullable byte[] mSourceCertificate\npublic @android.content.pm.Checksum.Kind int getKind()\npublic @android.annotation.NonNull byte[] getValue()\npublic @android.annotation.Nullable byte[] getSourceCertificateBytes()\npublic @android.annotation.Nullable java.security.cert.Certificate getSourceCertificate()\nclass ApkChecksum extends java.lang.Object implements [android.os.Parcelable]\[email protected](genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.NonNull android.content.pm.Checksum mChecksum\nprivate final @android.annotation.Nullable java.lang.String mSourcePackageName\nprivate final @android.annotation.Nullable byte[] mSourceCertificate\npublic @android.content.pm.Checksum.Kind int getKind()\npublic @android.annotation.NonNull byte[] getValue()\npublic @android.annotation.Nullable byte[] getSourceCertificateBytes()\npublic @android.annotation.Nullable java.security.cert.Certificate getSourceCertificate()\nclass ApkChecksum extends java.lang.Object implements [android.os.Parcelable]\[email protected](genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/Checksum.java b/core/java/android/content/pm/Checksum.java
index 123aadd..10ca5ca 100644
--- a/core/java/android/content/pm/Checksum.java
+++ b/core/java/android/content/pm/Checksum.java
@@ -32,14 +32,19 @@
  *
  * @see PackageInstaller.Session#addChecksums(String, List)
  */
-@DataClass(genHiddenConstructor = true, genConstDefs = false)
+@DataClass(genConstDefs = false)
 public final class Checksum implements Parcelable {
     /**
      * 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>.
      *
+     * Recommended for all new applications.
+     * Can be used by kernel to enforce authenticity and integrity of the APK.
+     * <a href="https://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git/tree/Documentation/filesystems/fsverity.rst#">See fs-verity for details</a>
+     *
      * @see PackageManager#getChecksums
+     * @see PackageInstaller.Session#addChecksums
      */
     public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001;
 
@@ -47,20 +52,31 @@
      * MD5 hash computed over all file bytes.
      *
      * @see PackageManager#getChecksums
+     * @see PackageInstaller.Session#addChecksums
+     * @deprecated Use SHA2 family of hashes (SHA256/SHA512) instead.
+     *             MD5 is cryptographically broken and unsuitable for further use.
+     *             Provided for completeness' sake and to support legacy usecases.
      */
+    @Deprecated
     public static final int WHOLE_MD5 = 0x00000002;
 
     /**
      * SHA1 hash computed over all file bytes.
      *
      * @see PackageManager#getChecksums
+     * @see PackageInstaller.Session#addChecksums
+     * @deprecated Use SHA2 family of hashes (SHA256/SHA512) instead.
+     *             SHA1 is broken and should not be used.
+     *             Provided for completeness' sake and to support legacy usecases.
      */
+    @Deprecated
     public static final int WHOLE_SHA1 = 0x00000004;
 
     /**
      * SHA256 hash computed over all file bytes.
      *
      * @see PackageManager#getChecksums
+     * @see PackageInstaller.Session#addChecksums
      */
     public static final int WHOLE_SHA256 = 0x00000008;
 
@@ -68,6 +84,7 @@
      * SHA512 hash computed over all file bytes.
      *
      * @see PackageManager#getChecksums
+     * @see PackageInstaller.Session#addChecksums
      */
     public static final int WHOLE_SHA512 = 0x00000010;
 
@@ -77,6 +94,7 @@
      * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
      *
      * @see PackageManager#getChecksums
+     * @see PackageInstaller.Session#addChecksums
      */
     public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020;
 
@@ -86,6 +104,7 @@
      * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
      *
      * @see PackageManager#getChecksums
+     * @see PackageInstaller.Session#addChecksums
      */
     public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040;
 
@@ -113,6 +132,7 @@
 
 
 
+
     // Code below generated by codegen v1.0.15.
     //
     // DO NOT MODIFY!
@@ -133,7 +153,6 @@
      *   Checksum kind.
      * @param value
      *   Checksum value.
-     * @hide
      */
     @DataClass.Generated.Member
     public Checksum(
@@ -214,10 +233,10 @@
     };
 
     @DataClass.Generated(
-            time = 1599845646883L,
+            time = 1600717052366L,
             codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/content/pm/Checksum.java",
-            inputSignatures = "public static final  int WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final  int WHOLE_MD5\npublic static final  int WHOLE_SHA1\npublic static final  int WHOLE_SHA256\npublic static final  int WHOLE_SHA512\npublic static final  int PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final  int PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Kind int mKind\nprivate final @android.annotation.NonNull byte[] mValue\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\[email protected](genHiddenConstructor=true, genConstDefs=false)")
+            inputSignatures = "public static final  int WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int WHOLE_MD5\npublic static final @java.lang.Deprecated int WHOLE_SHA1\npublic static final  int WHOLE_SHA256\npublic static final  int WHOLE_SHA512\npublic static final  int PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final  int PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Kind int mKind\nprivate final @android.annotation.NonNull byte[] mValue\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\[email protected](genConstDefs=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 75ee9b7..da56abf 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1227,7 +1227,10 @@
          *                  IntentSender)}.
          * @throws SecurityException if called after the session has been
          *                           committed or abandoned.
+         * @deprecated  use platform-enforced checksums e.g.
+         *              {@link Checksum#WHOLE_MERKLE_ROOT_4K_SHA256}
          */
+        @Deprecated
         public void addChecksums(@NonNull String name, @NonNull List<Checksum> checksums)
                 throws IOException {
             Objects.requireNonNull(name);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bbb9ab4..f0b2329 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -7873,6 +7873,9 @@
      * - enforced by installer.
      * If caller needs a specific checksum kind, they can specify it as required.
      *
+     * <b>Caution: Android can not verify installer-provided checksums. Make sure you specify
+     * trusted installers.</b>
+     *
      * @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
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index ad9a162..52475e9 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -69,7 +69,7 @@
     /**
      * Creates a file under a storage.
      */
-    int makeFile(int storageId, in @utf8InCpp String path, in IncrementalNewFileParams params);
+    int makeFile(int storageId, in @utf8InCpp String path, in IncrementalNewFileParams params, in @nullable byte[] content);
 
     /**
      * Creates a file under a storage. Content of the file is from a range inside another file.
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index dcbbd71..284c2df 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -42,6 +42,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.List;
+import java.util.UUID;
 
 /**
  * This class manages storage instances used during a package installation session.
@@ -139,7 +140,7 @@
         final String apkName = apk.name;
         final File targetFile = new File(mStageDir, apkName);
         if (!targetFile.exists()) {
-            mDefaultStorage.makeFile(apkName, apk.size, null, apk.metadata, apk.signature);
+            mDefaultStorage.makeFile(apkName, apk.size, null, apk.metadata, apk.signature, null);
         }
     }
 
@@ -153,6 +154,13 @@
     }
 
     /**
+     * Creates file in default storage and sets its content.
+     */
+    public void makeFile(@NonNull String name, @NonNull byte[] content) throws IOException {
+        mDefaultStorage.makeFile(name, content.length, UUID.randomUUID(), null, null, content);
+    }
+
+    /**
      * Permanently disables readlogs.
      */
     public void disableReadLogs() {
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index f835412..a1c3cc6 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -170,9 +170,10 @@
      * @param size             Size of the new file in bytes.
      * @param metadata         Metadata bytes.
      * @param v4signatureBytes Serialized V4SignatureProto.
+     * @param content          Optionally set file content.
      */
     public void makeFile(@NonNull String path, long size, @Nullable UUID id,
-            @Nullable byte[] metadata, @Nullable byte[] v4signatureBytes)
+            @Nullable byte[] metadata, @Nullable byte[] v4signatureBytes, @Nullable byte[] content)
             throws IOException {
         try {
             if (id == null && metadata == null) {
@@ -184,7 +185,7 @@
             params.metadata = (metadata == null ? new byte[0] : metadata);
             params.fileId = idToBytes(id);
             params.signature = v4signatureBytes;
-            int res = mService.makeFile(mId, path, params);
+            int res = mService.makeFile(mId, path, params, content);
             if (res != 0) {
                 throw new IOException("makeFile() failed with errno " + -res);
             }
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index fce6616..68c10cc 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -11451,6 +11451,7 @@
     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 getSourcePackageName();
     method @Nullable public String getSplitName();
     method @NonNull public byte[] getValue();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -11559,6 +11560,7 @@
   }
 
   public final class Checksum implements android.os.Parcelable {
+    ctor public Checksum(int, @NonNull byte[]);
     method public int describeContents();
     method public int getKind();
     method @NonNull public byte[] getValue();
@@ -11566,9 +11568,9 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Checksum> CREATOR;
     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 WHOLE_MD5 = 2; // 0x2
+    field @Deprecated 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 @Deprecated 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
   }
@@ -11864,7 +11866,7 @@
 
   public static class PackageInstaller.Session implements java.io.Closeable {
     method public void abandon();
-    method public void addChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>) throws java.io.IOException;
+    method @Deprecated public void addChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>) throws java.io.IOException;
     method public void addChildSessionId(int);
     method public void close();
     method public void commit(@NonNull android.content.IntentSender);
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index c6c80ae..8640dbc 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -64,7 +64,6 @@
 import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -168,12 +167,11 @@
     }
 
     /**
-     * Serialize checksums to file in binary format.
+     * Serialize checksums to the stream in binary format.
      */
-    public static void writeChecksums(File file, ApkChecksum[] checksums)
+    public static void writeChecksums(OutputStream os, ApkChecksum[] checksums)
             throws IOException, CertificateException {
-        try (OutputStream os = new FileOutputStream(file);
-             DataOutputStream dos = new DataOutputStream(os)) {
+        try (DataOutputStream dos = new DataOutputStream(os)) {
             dos.writeInt(checksums.length);
             for (ApkChecksum checksum : checksums) {
                 final String splitName = checksum.getSplitName();
@@ -190,6 +188,14 @@
                 dos.writeInt(valueBytes.length);
                 dos.write(valueBytes);
 
+                final String packageName = checksum.getSourcePackageName();
+                if (packageName == null) {
+                    dos.writeInt(-1);
+                } else {
+                    dos.writeInt(packageName.length());
+                    dos.writeUTF(packageName);
+                }
+
                 final Certificate cert = checksum.getSourceCertificate();
                 final byte[] certBytes = (cert == null) ? null : cert.getEncoded();
                 if (certBytes == null) {
@@ -218,9 +224,19 @@
                 } else {
                     splitName = dis.readUTF();
                 }
+
                 final int kind = dis.readInt();
+
                 final byte[] valueBytes = new byte[dis.readInt()];
                 dis.read(valueBytes);
+
+                final String packageName;
+                if (dis.readInt() < 0) {
+                    packageName = null;
+                } else {
+                    packageName = dis.readUTF();
+                }
+
                 final byte[] certBytes;
                 final int certBytesLength = dis.readInt();
                 if (certBytesLength < 0) {
@@ -230,7 +246,7 @@
                     dis.read(certBytes);
                 }
                 checksums[i] = new ApkChecksum(splitName, new Checksum(kind, valueBytes),
-                        certBytes);
+                        packageName, certBytes);
             }
             return checksums;
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 48efa5c..2a189c0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -52,6 +52,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.admin.DevicePolicyEventLogger;
@@ -153,6 +154,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileFilter;
@@ -240,6 +242,7 @@
     private static final String ATTR_SIGNATURE = "signature";
     private static final String ATTR_CHECKSUM_KIND = "checksumKind";
     private static final String ATTR_CHECKSUM_VALUE = "checksumValue";
+    private static final String ATTR_CHECKSUM_PACKAGE = "checksumPackage";
     private static final String ATTR_CHECKSUM_CERTIFICATE = "checksumCertificate";
 
     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
@@ -394,10 +397,13 @@
 
     static class CertifiedChecksum {
         final @NonNull Checksum mChecksum;
+        final @NonNull String mPackageName;
         final @NonNull byte[] mCertificate;
 
-        CertifiedChecksum(@NonNull Checksum checksum, @NonNull byte[] certificate) {
+        CertifiedChecksum(@NonNull Checksum checksum, @NonNull String packageName,
+                @NonNull byte[] certificate) {
             mChecksum = checksum;
+            mPackageName = packageName;
             mCertificate = certificate;
         }
 
@@ -405,6 +411,10 @@
             return mChecksum;
         }
 
+        String getPackageName() {
+            return mPackageName;
+        }
+
         byte[] getCertificate() {
             return mCertificate;
         }
@@ -951,23 +961,26 @@
             return;
         }
 
-        final PackageManagerInternal pmi = LocalServices.getService(
-                PackageManagerInternal.class);
-        final AndroidPackage callingInstaller = pmi.getPackage(Binder.getCallingUid());
+        final String initiatingPackageName = mInstallSource.initiatingPackageName;
 
+        final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+        appOps.checkPackage(Binder.getCallingUid(), initiatingPackageName);
+
+        final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+        final AndroidPackage callingInstaller = pmi.getPackage(initiatingPackageName);
         if (callingInstaller == null) {
             throw new IllegalStateException("Can't obtain calling installer's package.");
         }
 
         // Obtaining array of certificates used for signing the installer package.
-        // According to V2/V3 signing schema, the first certificate corresponds to public key
-        // in the signing block.
-        Signature[] certs = callingInstaller.getSigningDetails().signatures;
+        final Signature[] certs = callingInstaller.getSigningDetails().signatures;
         if (certs == null || certs.length == 0 || certs[0] == null) {
             throw new IllegalStateException(
                     "Can't obtain calling installer package's certificates.");
         }
-        byte[] mainCertificateBytes = certs[0].toByteArray();
+        // According to V2/V3 signing schema, the first certificate corresponds to the public key
+        // in the signing block.
+        final byte[] mainCertificateBytes = certs[0].toByteArray();
 
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
@@ -979,7 +992,8 @@
                     fileChecksums = new ArrayList<>();
                     mChecksums.put(name, fileChecksums);
                 }
-                fileChecksums.add(new CertifiedChecksum(checksum, mainCertificateBytes));
+                fileChecksums.add(new CertifiedChecksum(checksum, initiatingPackageName,
+                        mainCertificateBytes));
             }
         }
     }
@@ -2633,7 +2647,7 @@
         for (int i = 0, size = checksums.size(); i < size; ++i) {
             CertifiedChecksum checksum = checksums.get(i);
             result[i] = new ApkChecksum(splitName, checksum.getChecksum(),
-                    checksum.getCertificate());
+                    checksum.getPackageName(), checksum.getCertificate());
         }
         return result;
     }
@@ -2651,11 +2665,17 @@
             return;
         }
 
-        final File targetDigestsFile = new File(stageDir,
-                ApkChecksums.buildDigestsPathForApk(targetFile.getName()));
-        try {
-            ApkChecksums.writeChecksums(targetDigestsFile,
-                    createApkChecksums(splitName, checksums));
+        final String targetDigestsPath = ApkChecksums.buildDigestsPathForApk(targetFile.getName());
+        final File targetDigestsFile = new File(stageDir, targetDigestsPath);
+        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+            ApkChecksums.writeChecksums(os, createApkChecksums(splitName, checksums));
+            final byte[] checksumsBytes = os.toByteArray();
+
+            if (!isIncrementalInstallation() || mIncrementalFileStorages == null) {
+                FileUtils.bytesToFile(targetDigestsFile.getAbsolutePath(), checksumsBytes);
+            } else {
+                mIncrementalFileStorages.makeFile(targetDigestsPath, checksumsBytes);
+            }
         } catch (CertificateException e) {
             throw new PackageManagerException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
                     "Failed to encode certificate for " + mPackageName, e);
@@ -3975,6 +3995,7 @@
                     writeIntAttribute(out, ATTR_CHECKSUM_KIND, checksum.getChecksum().getKind());
                     writeByteArrayAttribute(out, ATTR_CHECKSUM_VALUE,
                             checksum.getChecksum().getValue());
+                    writeStringAttribute(out, ATTR_CHECKSUM_PACKAGE, checksum.getPackageName());
                     writeByteArrayAttribute(out, ATTR_CHECKSUM_CERTIFICATE,
                             checksum.getCertificate());
                     out.endTag(null, TAG_SESSION_CHECKSUM);
@@ -4127,6 +4148,7 @@
                 final CertifiedChecksum certifiedChecksum = new CertifiedChecksum(
                         new Checksum(readIntAttribute(in, ATTR_CHECKSUM_KIND, 0),
                                 readByteArrayAttribute(in, ATTR_CHECKSUM_VALUE)),
+                        readStringAttribute(in, ATTR_CHECKSUM_PACKAGE),
                         readByteArrayAttribute(in, ATTR_CHECKSUM_CERTIFICATE));
 
                 List<CertifiedChecksum> certifiedChecksums = checksums.get(fileName);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index 2bbca79..1814a8e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -27,6 +27,8 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import libcore.io.IoUtils;
 
 import java.io.IOException;
@@ -112,7 +114,9 @@
         }
     }
 
-    static class Metadata {
+    /** @hide */
+    @VisibleForTesting
+    public static class Metadata {
         /**
          * Full files read from stdin.
          */
@@ -137,7 +141,9 @@
             return new Metadata(STDIN, fileId);
         }
 
-        static Metadata forLocalFile(String filePath) {
+        /** @hide */
+        @VisibleForTesting
+        public static Metadata forLocalFile(String filePath) {
             return new Metadata(LOCAL_FILE, filePath);
         }
 
@@ -163,7 +169,9 @@
             return new Metadata(mode, data);
         }
 
-        byte[] toByteArray() {
+        /** @hide */
+        @VisibleForTesting
+        public byte[] toByteArray() {
             byte[] dataBytes = this.mData.getBytes(StandardCharsets.UTF_8);
             byte[] result = new byte[1 + dataBytes.length];
             result[0] = this.mMode;
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 9e2bb45..631e185 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -225,6 +225,20 @@
     return res;
 }
 
+static inline unique_fd openLocalFile(JNIEnv* env, const JniIds& jni, jobject shellCommand,
+                                      const std::string& path) {
+    if (shellCommand) {
+        return unique_fd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
+                                                  jni.pmscdGetLocalFile, shellCommand,
+                                                  env->NewStringUTF(path.c_str()))};
+    }
+    auto fd = unique_fd(::open(path.c_str(), O_RDONLY | O_CLOEXEC));
+    if (!fd.ok()) {
+        PLOG(ERROR) << "Failed to open file: " << path << ", error code: " << fd.get();
+    }
+    return fd;
+}
+
 static inline InputDescs openLocalFile(JNIEnv* env, const JniIds& jni, jobject shellCommand,
                                        IncFsSize size, const std::string& filePath) {
     InputDescs result;
@@ -232,9 +246,7 @@
 
     const std::string idsigPath = filePath + ".idsig";
 
-    unique_fd idsigFd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
-                                               jni.pmscdGetLocalFile, shellCommand,
-                                               env->NewStringUTF(idsigPath.c_str()))};
+    unique_fd idsigFd = openLocalFile(env, jni, shellCommand, idsigPath);
     if (idsigFd.ok()) {
         auto treeSize = verityTreeSizeForFile(size);
         auto actualTreeSize = skipIdSigHeaders(idsigFd);
@@ -250,9 +262,7 @@
         });
     }
 
-    unique_fd fileFd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
-                                              jni.pmscdGetLocalFile, shellCommand,
-                                              env->NewStringUTF(filePath.c_str()))};
+    unique_fd fileFd = openLocalFile(env, jni, shellCommand, filePath);
     if (fileFd.ok()) {
         result.push_back(InputDesc{
                 .fd = std::move(fileFd),
@@ -272,6 +282,11 @@
                              std::string(metadata.data, metadata.size));
     }
 
+    if (!shellCommand) {
+        ALOGE("Missing shell command.");
+        return {};
+    }
+
     unique_fd fd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
                                           jni.pmscdGetStdIn, shellCommand)};
     if (!fd.ok()) {
@@ -396,10 +411,6 @@
         jobject shellCommand = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
                                                            jni.pmscdLookupShellCommand,
                                                            env->NewStringUTF(mArgs.c_str()));
-        if (!shellCommand) {
-            ALOGE("Missing shell command.");
-            return false;
-        }
 
         std::vector<char> buffer;
         buffer.reserve(BUFFER_SIZE);
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index bf3a896..2f8825b 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -200,16 +200,24 @@
     return {0, id, nfp};
 }
 
+static std::span<const uint8_t> toSpan(const ::std::optional<::std::vector<uint8_t>>& content) {
+    if (!content) {
+        return {};
+    }
+    return {content->data(), (int)content->size()};
+}
+
 binder::Status BinderIncrementalService::makeFile(
         int32_t storageId, const std::string& path,
-        const ::android::os::incremental::IncrementalNewFileParams& params, int32_t* _aidl_return) {
+        const ::android::os::incremental::IncrementalNewFileParams& params,
+        const ::std::optional<::std::vector<uint8_t>>& content, int32_t* _aidl_return) {
     auto [err, fileId, nfp] = toMakeFileParams(params);
     if (err) {
         *_aidl_return = err;
         return ok();
     }
 
-    *_aidl_return = mImpl.makeFile(storageId, path, 0777, fileId, nfp);
+    *_aidl_return = mImpl.makeFile(storageId, path, 0777, fileId, nfp, toSpan(content));
     return ok();
 }
 binder::Status BinderIncrementalService::makeFileFromRange(int32_t storageId,
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 1238498..0a89166 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -58,7 +58,9 @@
     binder::Status makeDirectories(int32_t storageId, const std::string& path,
                                    int32_t* _aidl_return) final;
     binder::Status makeFile(int32_t storageId, const std::string& path,
-                            const IncrementalNewFileParams& params, int32_t* _aidl_return) final;
+                            const IncrementalNewFileParams& params,
+                            const ::std::optional<::std::vector<uint8_t>>& content,
+                            int32_t* _aidl_return) final;
     binder::Status makeFileFromRange(int32_t storageId, const std::string& targetPath,
                                      const std::string& sourcePath, int64_t start, int64_t end,
                                      int32_t* _aidl_return) final;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 10a508b..44a07a1 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -859,7 +859,7 @@
 }
 
 int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
-                                 incfs::NewFileParams params) {
+                                 incfs::NewFileParams params, std::span<const uint8_t> data) {
     if (auto ifs = getIfs(storage)) {
         std::string normPath = normalizePathToStorage(*ifs, storage, path);
         if (normPath.empty()) {
@@ -867,11 +867,15 @@
                        << " failed to normalize: " << path;
             return -EINVAL;
         }
-        auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params);
-        if (err) {
+        if (auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); err) {
             LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err;
             return err;
         }
+        if (!data.empty()) {
+            if (auto err = setFileContent(ifs, id, path, data); err) {
+                return err;
+            }
+        }
         return 0;
     }
     return -EINVAL;
@@ -1584,67 +1588,38 @@
 
 void IncrementalService::extractZipFile(const IfsMountPtr& ifs, ZipArchiveHandle zipFile,
                                         ZipEntry& entry, const incfs::FileId& libFileId,
-                                        std::string_view targetLibPath,
+                                        std::string_view debugLibPath,
                                         Clock::time_point scheduledTs) {
     if (!ifs) {
-        LOG(INFO) << "Skipping zip file " << targetLibPath << " extraction for an expired mount";
+        LOG(INFO) << "Skipping zip file " << debugLibPath << " extraction for an expired mount";
         return;
     }
 
-    auto libName = path::basename(targetLibPath);
     auto startedTs = Clock::now();
 
     // Write extracted data to new file
     // NOTE: don't zero-initialize memory, it may take a while for nothing
     auto libData = std::unique_ptr<uint8_t[]>(new uint8_t[entry.uncompressed_length]);
     if (ExtractToMemory(zipFile, &entry, libData.get(), entry.uncompressed_length)) {
-        LOG(ERROR) << "Failed to extract native lib zip entry: " << libName;
+        LOG(ERROR) << "Failed to extract native lib zip entry: " << path::basename(debugLibPath);
         return;
     }
 
     auto extractFileTs = Clock::now();
 
-    const auto writeFd = mIncFs->openForSpecialOps(ifs->control, libFileId);
-    if (!writeFd.ok()) {
-        LOG(ERROR) << "Failed to open write fd for: " << targetLibPath
-                   << " errno: " << writeFd.get();
-        return;
-    }
-
-    auto openFileTs = Clock::now();
-    const int numBlocks =
-            (entry.uncompressed_length + constants().blockSize - 1) / constants().blockSize;
-    std::vector<IncFsDataBlock> instructions(numBlocks);
-    auto remainingData = std::span(libData.get(), entry.uncompressed_length);
-    for (int i = 0; i < numBlocks; i++) {
-        const auto blockSize = std::min<long>(constants().blockSize, remainingData.size());
-        instructions[i] = IncFsDataBlock{
-                .fileFd = writeFd.get(),
-                .pageIndex = static_cast<IncFsBlockIndex>(i),
-                .compression = INCFS_COMPRESSION_KIND_NONE,
-                .kind = INCFS_BLOCK_KIND_DATA,
-                .dataSize = static_cast<uint32_t>(blockSize),
-                .data = reinterpret_cast<const char*>(remainingData.data()),
-        };
-        remainingData = remainingData.subspan(blockSize);
-    }
-    auto prepareInstsTs = Clock::now();
-
-    size_t res = mIncFs->writeBlocks(instructions);
-    if (res != instructions.size()) {
-        LOG(ERROR) << "Failed to write data into: " << targetLibPath;
+    if (setFileContent(ifs, libFileId, debugLibPath,
+                       std::span(libData.get(), entry.uncompressed_length))) {
         return;
     }
 
     if (perfLoggingEnabled()) {
         auto endFileTs = Clock::now();
-        LOG(INFO) << "incfs: Extracted " << libName << "(" << entry.compressed_length << " -> "
-                  << entry.uncompressed_length << " bytes): " << elapsedMcs(startedTs, endFileTs)
+        LOG(INFO) << "incfs: Extracted " << path::basename(debugLibPath) << "("
+                  << entry.compressed_length << " -> " << entry.uncompressed_length
+                  << " bytes): " << elapsedMcs(startedTs, endFileTs)
                   << "mcs, scheduling delay: " << elapsedMcs(scheduledTs, startedTs)
                   << " extract: " << elapsedMcs(startedTs, extractFileTs)
-                  << " open: " << elapsedMcs(extractFileTs, openFileTs)
-                  << " prepare: " << elapsedMcs(openFileTs, prepareInstsTs)
-                  << " write: " << elapsedMcs(prepareInstsTs, endFileTs);
+                  << " open/prepare/write: " << elapsedMcs(extractFileTs, endFileTs);
     }
 }
 
@@ -1677,6 +1652,55 @@
     return mRunning;
 }
 
+int IncrementalService::setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId,
+                                       std::string_view debugFilePath,
+                                       std::span<const uint8_t> data) const {
+    auto startTs = Clock::now();
+
+    const auto writeFd = mIncFs->openForSpecialOps(ifs->control, fileId);
+    if (!writeFd.ok()) {
+        LOG(ERROR) << "Failed to open write fd for: " << debugFilePath
+                   << " errno: " << writeFd.get();
+        return writeFd.get();
+    }
+
+    const auto dataLength = data.size();
+
+    auto openFileTs = Clock::now();
+    const int numBlocks = (data.size() + constants().blockSize - 1) / constants().blockSize;
+    std::vector<IncFsDataBlock> instructions(numBlocks);
+    for (int i = 0; i < numBlocks; i++) {
+        const auto blockSize = std::min<long>(constants().blockSize, data.size());
+        instructions[i] = IncFsDataBlock{
+                .fileFd = writeFd.get(),
+                .pageIndex = static_cast<IncFsBlockIndex>(i),
+                .compression = INCFS_COMPRESSION_KIND_NONE,
+                .kind = INCFS_BLOCK_KIND_DATA,
+                .dataSize = static_cast<uint32_t>(blockSize),
+                .data = reinterpret_cast<const char*>(data.data()),
+        };
+        data = data.subspan(blockSize);
+    }
+    auto prepareInstsTs = Clock::now();
+
+    size_t res = mIncFs->writeBlocks(instructions);
+    if (res != instructions.size()) {
+        LOG(ERROR) << "Failed to write data into: " << debugFilePath;
+        return res;
+    }
+
+    if (perfLoggingEnabled()) {
+        auto endTs = Clock::now();
+        LOG(INFO) << "incfs: Set file content " << debugFilePath << "(" << dataLength
+                  << " bytes): " << elapsedMcs(startTs, endTs)
+                  << "mcs, open: " << elapsedMcs(startTs, openFileTs)
+                  << " prepare: " << elapsedMcs(openFileTs, prepareInstsTs)
+                  << " write: " << elapsedMcs(prepareInstsTs, endTs);
+    }
+
+    return 0;
+}
+
 int IncrementalService::isFileFullyLoaded(StorageId storage, const std::string& path) const {
     std::unique_lock l(mLock);
     const auto ifs = getIfsLocked(storage);
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index a49e0f3..d820417 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -127,7 +127,7 @@
     int setStorageParams(StorageId storage, bool enableReadLogs);
 
     int makeFile(StorageId storage, std::string_view path, int mode, FileId id,
-                 incfs::NewFileParams params);
+                 incfs::NewFileParams params, std::span<const uint8_t> data);
     int makeDir(StorageId storage, std::string_view path, int mode = 0755);
     int makeDirs(StorageId storage, std::string_view path, int mode = 0755);
 
@@ -349,13 +349,16 @@
     int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const;
     float getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
 
+    int setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId,
+                       std::string_view debugFilePath, std::span<const uint8_t> data) const;
+
     void registerAppOpsCallback(const std::string& packageName);
     bool unregisterAppOpsCallback(const std::string& packageName);
     void onAppOpChanged(const std::string& packageName);
 
     void runJobProcessing();
     void extractZipFile(const IfsMountPtr& ifs, ZipArchiveHandle zipFile, ZipEntry& entry,
-                        const incfs::FileId& libFileId, std::string_view targetLibPath,
+                        const incfs::FileId& libFileId, std::string_view debugLibPath,
                         Clock::time_point scheduledTs);
 
     void runCmdLooper();