diff options
17 files changed, 256 insertions, 104 deletions
diff --git a/api/current.txt b/api/current.txt index 0f4cffc9427b..405f6b7f0551 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11451,6 +11451,7 @@ package android.content.pm { 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 @@ package android.content.pm { } 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 @@ package android.content.pm { 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 @@ package android.content.pm { 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 e087c44d1ed1..bf678419cbef 100644 --- a/core/java/android/content/pm/ApkChecksum.java +++ b/core/java/android/content/pm/ApkChecksum.java @@ -50,7 +50,11 @@ public final class ApkChecksum implements Parcelable { */ 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 final class ApkChecksum implements Parcelable { */ 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 @@ public final class ApkChecksum implements Parcelable { * * @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 @@ public final class ApkChecksum implements Parcelable { * 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 @@ public final class ApkChecksum implements Parcelable { 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 @@ public final class ApkChecksum implements Parcelable { 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 @@ public final class ApkChecksum implements Parcelable { 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 @@ public final class ApkChecksum implements Parcelable { }; @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]\n@com.android.internal.util.DataClass(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]\n@com.android.internal.util.DataClass(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 123aaddda7ce..10ca5cac5276 100644 --- a/core/java/android/content/pm/Checksum.java +++ b/core/java/android/content/pm/Checksum.java @@ -32,14 +32,19 @@ import java.util.List; * * @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 @@ public final class Checksum implements Parcelable { * 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 @@ public final class Checksum implements Parcelable { * 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 @@ public final class Checksum implements Parcelable { * <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 @@ public final class Checksum implements Parcelable { * <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 @@ public final class Checksum implements Parcelable { + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! @@ -133,7 +153,6 @@ public final class Checksum implements Parcelable { * Checksum kind. * @param value * Checksum value. - * @hide */ @DataClass.Generated.Member public Checksum( @@ -214,10 +233,10 @@ public final class Checksum implements Parcelable { }; @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]\n@com.android.internal.util.DataClass(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]\n@com.android.internal.util.DataClass(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 75ee9b7943fe..da56abf73516 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1227,7 +1227,10 @@ public class PackageInstaller { * 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 bbb9ab4a5b10..f0b2329385af 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -7873,6 +7873,9 @@ public abstract class PackageManager { * - 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 ad9a16231f78..52475e9cd89d 100644 --- a/core/java/android/os/incremental/IIncrementalService.aidl +++ b/core/java/android/os/incremental/IIncrementalService.aidl @@ -69,7 +69,7 @@ interface IIncrementalService { /** * 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 dcbbd712914a..284c2df2ee7b 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -42,6 +42,7 @@ import android.text.TextUtils; 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 @@ public final class IncrementalFileStorages { 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 @@ public final class IncrementalFileStorages { } /** + * 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 f83541293330..a1c3cc697e02 100644 --- a/core/java/android/os/incremental/IncrementalStorage.java +++ b/core/java/android/os/incremental/IncrementalStorage.java @@ -170,9 +170,10 @@ public final class IncrementalStorage { * @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 @@ public final class IncrementalStorage { 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 fce66164dae6..68c10ccb6d5c 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -11451,6 +11451,7 @@ package android.content.pm { 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 @@ package android.content.pm { } 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 @@ package android.content.pm { 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 @@ package android.content.pm { 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 c6c80aef4432..8640dbcab33c 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.DataInputStream; 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 @@ public class ApkChecksums { } /** - * 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 @@ public class ApkChecksums { 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 @@ public class ApkChecksums { } 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 @@ public class ApkChecksums { 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 48efa5c31f2b..2a189c0ebb0c 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 static com.android.server.pm.PackageInstallerService.prepareStageDir; 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.XmlPullParser; 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return mChecksum; } + String getPackageName() { + return mPackageName; + } + byte[] getCertificate() { return mCertificate; } @@ -951,23 +961,26 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { fileChecksums = new ArrayList<>(); mChecksums.put(name, fileChecksums); } - fileChecksums.add(new CertifiedChecksum(checksum, mainCertificateBytes)); + fileChecksums.add(new CertifiedChecksum(checksum, initiatingPackageName, + mainCertificateBytes)); } } } @@ -2633,7 +2647,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 2bbca79741bd..1814a8e6322e 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.service.dataloader.DataLoaderService; 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 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { } } - static class Metadata { + /** @hide */ + @VisibleForTesting + public static class Metadata { /** * Full files read from stdin. */ @@ -137,7 +141,9 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { 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 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { 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 9e2bb45ab341..631e185fffce 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 @@ std::optional<T> read(IncFsSpan& data) { 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 @@ static inline InputDescs openLocalFile(JNIEnv* env, const JniIds& jni, jobject s 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 @@ static inline InputDescs openLocalFile(JNIEnv* env, const JniIds& jni, jobject s }); } - 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 @@ static inline InputDescs openInputs(JNIEnv* env, const JniIds& jni, jobject shel 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 @@ private: 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 bf3a8964465b..2f8825b064ce 100644 --- a/services/incremental/BinderIncrementalService.cpp +++ b/services/incremental/BinderIncrementalService.cpp @@ -200,16 +200,24 @@ static std::tuple<int, incfs::FileId, incfs::NewFileParams> toMakeFileParams( 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 123849876095..0a89166f4868 100644 --- a/services/incremental/BinderIncrementalService.h +++ b/services/incremental/BinderIncrementalService.h @@ -58,7 +58,9 @@ public: 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 10a508b06086..44a07a17824f 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -859,7 +859,7 @@ std::string IncrementalService::normalizePathToStorage(const IncFsMount& ifs, St } 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 @@ int IncrementalService::makeFile(StorageId storage, std::string_view path, int m << " 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 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ 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 @@ bool IncrementalService::waitForNativeBinariesExtraction(StorageId storage) { 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 a49e0f36c9bc..d820417e73ed 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -127,7 +127,7 @@ public: 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 @@ private: 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(); |