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();