summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt43
-rw-r--r--core/java/android/app/ApplicationPackageManager.java11
-rw-r--r--core/java/android/content/pm/ApkChecksum.aidl (renamed from core/java/android/content/pm/FileChecksum.aidl)2
-rw-r--r--core/java/android/content/pm/ApkChecksum.java (renamed from core/java/android/content/pm/FileChecksum.java)123
-rw-r--r--core/java/android/content/pm/Checksum.aidl20
-rw-r--r--core/java/android/content/pm/Checksum.java228
-rw-r--r--core/java/android/content/pm/IPackageInstallerSession.aidl3
-rw-r--r--core/java/android/content/pm/PackageInstaller.java25
-rw-r--r--core/java/android/content/pm/PackageManager.java72
-rw-r--r--non-updatable-api/current.txt43
-rw-r--r--services/core/java/com/android/server/pm/ApkChecksums.java250
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java268
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java1
15 files changed, 829 insertions, 271 deletions
diff --git a/api/current.txt b/api/current.txt
index 6cc6956b8aba..9b9e258f23a2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11447,6 +11447,16 @@ package android.content.pm {
field public final float widthFraction;
}
+ public final class ApkChecksum implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getKind();
+ method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
+ method @Nullable public String getSplitName();
+ method @NonNull public byte[] getValue();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ApkChecksum> CREATOR;
+ }
+
public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
ctor public ApplicationInfo();
ctor public ApplicationInfo(android.content.pm.ApplicationInfo);
@@ -11548,6 +11558,21 @@ package android.content.pm {
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ChangedPackages> CREATOR;
}
+ public final class Checksum implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getKind();
+ method @NonNull public byte[] getValue();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.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 public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
+ field public static final int WHOLE_SHA1 = 4; // 0x4
+ field public static final int WHOLE_SHA256 = 8; // 0x8
+ field public static final int WHOLE_SHA512 = 16; // 0x10
+ }
+
public class ComponentInfo extends android.content.pm.PackageItemInfo {
ctor public ComponentInfo();
ctor public ComponentInfo(android.content.pm.ComponentInfo);
@@ -11619,16 +11644,6 @@ package android.content.pm {
field public int version;
}
- public final class FileChecksum implements android.os.Parcelable {
- method public int describeContents();
- method public int getKind();
- method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
- method @Nullable public String getSplitName();
- method @NonNull public byte[] getValue();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.FileChecksum> CREATOR;
- }
-
public final class InstallSourceInfo implements android.os.Parcelable {
method public int describeContents();
method @Nullable public String getInitiatingPackageName();
@@ -11849,6 +11864,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 public void addChildSessionId(int);
method public void close();
method public void commit(@NonNull android.content.IntentSender);
@@ -12250,8 +12266,6 @@ package android.content.pm {
field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
- field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
- field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
field public static final int PERMISSION_DENIED = -1; // 0xffffffff
field public static final int PERMISSION_GRANTED = 0; // 0x0
field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
@@ -12266,11 +12280,6 @@ package android.content.pm {
field public static final int VERIFICATION_ALLOW = 1; // 0x1
field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
- field public static final int WHOLE_MD5 = 2; // 0x2
- field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
- field public static final int WHOLE_SHA1 = 4; // 0x4
- field public static final int WHOLE_SHA256 = 8; // 0x8
- field public static final int WHOLE_SHA512 = 16; // 0x10
}
public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 2780036d8102..066a00732965 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -16,6 +16,14 @@
package android.app;
+import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA256;
+import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA512;
+import static android.content.pm.Checksum.WHOLE_MD5;
+import static android.content.pm.Checksum.WHOLE_MERKLE_ROOT_4K_SHA256;
+import static android.content.pm.Checksum.WHOLE_SHA1;
+import static android.content.pm.Checksum.WHOLE_SHA256;
+import static android.content.pm.Checksum.WHOLE_SHA512;
+
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -32,6 +40,7 @@ import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ChangedPackages;
+import android.content.pm.Checksum;
import android.content.pm.ComponentInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
@@ -973,7 +982,7 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public void getChecksums(@NonNull String packageName, boolean includeSplits,
- @FileChecksumKind int required, @Nullable List<Certificate> trustedInstallers,
+ @Checksum.Kind int required, @Nullable List<Certificate> trustedInstallers,
@NonNull IntentSender statusReceiver)
throws CertificateEncodingException, IOException, NameNotFoundException {
Objects.requireNonNull(packageName);
diff --git a/core/java/android/content/pm/FileChecksum.aidl b/core/java/android/content/pm/ApkChecksum.aidl
index 109f211033c1..46781fe26677 100644
--- a/core/java/android/content/pm/FileChecksum.aidl
+++ b/core/java/android/content/pm/ApkChecksum.aidl
@@ -16,5 +16,5 @@
package android.content.pm;
-parcelable FileChecksum;
+parcelable ApkChecksum;
diff --git a/core/java/android/content/pm/FileChecksum.java b/core/java/android/content/pm/ApkChecksum.java
index 55430c2b877b..e087c44d1ed1 100644
--- a/core/java/android/content/pm/FileChecksum.java
+++ b/core/java/android/content/pm/ApkChecksum.java
@@ -34,51 +34,71 @@ import java.security.cert.X509Certificate;
import java.util.List;
/**
- * A typed checksum.
+ * A typed checksum of an APK.
*
* @see PackageManager#getChecksums(String, boolean, int, List, IntentSender)
*/
@DataClass(genHiddenConstructor = true)
-public final class FileChecksum implements Parcelable {
+@DataClass.Suppress({"getChecksum"})
+public final class ApkChecksum implements Parcelable {
/**
* Checksum for which split. Null indicates base.apk.
*/
private final @Nullable String mSplitName;
/**
- * Checksum kind.
+ * Checksum.
*/
- private final @PackageManager.FileChecksumKind int mKind;
- /**
- * Checksum value.
- */
- private final @NonNull byte[] mValue;
+ private final @NonNull Checksum mChecksum;
/**
* For Installer-provided checksums, certificate of the Installer/AppStore.
*/
private final @Nullable byte[] mSourceCertificate;
/**
- * Constructor, internal use only
+ * Constructor, internal use only.
*
* @hide
*/
- public FileChecksum(@Nullable String splitName, @PackageManager.FileChecksumKind int kind,
+ public ApkChecksum(@Nullable String splitName, @Checksum.Kind int kind,
@NonNull byte[] value) {
- this(splitName, kind, value, (byte[]) null);
+ this(splitName, new Checksum(kind, value), (byte[]) null);
}
/**
- * Constructor, internal use only
+ * Constructor, internal use only.
*
* @hide
*/
- public FileChecksum(@Nullable String splitName, @PackageManager.FileChecksumKind int kind,
+ public ApkChecksum(@Nullable String splitName, @Checksum.Kind int kind,
@NonNull byte[] value, @Nullable Certificate sourceCertificate)
throws CertificateEncodingException {
- this(splitName, kind, value,
+ this(splitName, new Checksum(kind, value),
(sourceCertificate != null) ? sourceCertificate.getEncoded() : null);
}
+
+ /**
+ * Checksum kind.
+ */
+ public @Checksum.Kind int getKind() {
+ return mChecksum.getKind();
+ }
+
+ /**
+ * Checksum value.
+ */
+ public @NonNull byte[] getValue() {
+ return mChecksum.getValue();
+ }
+
+ /**
+ * Returns raw bytes representing encoded certificate of the source of this checksum.
+ * @hide
+ */
+ public @Nullable byte[] getSourceCertificateBytes() {
+ return mSourceCertificate;
+ }
+
/**
* Certificate of the source of this checksum.
* @throws CertificateException in case when certificate can't be re-created from serialized
@@ -102,7 +122,7 @@ public final class FileChecksum implements Parcelable {
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/FileChecksum.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ApkChecksum.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -110,31 +130,25 @@ public final class FileChecksum implements Parcelable {
/**
- * Creates a new FileChecksum.
+ * Creates a new ApkChecksum.
*
* @param splitName
* Checksum for which split. Null indicates base.apk.
- * @param kind
- * Checksum kind.
- * @param value
- * Checksum value.
+ * @param checksum
+ * Checksum.
* @param sourceCertificate
* For Installer-provided checksums, certificate of the Installer/AppStore.
* @hide
*/
@DataClass.Generated.Member
- public FileChecksum(
+ public ApkChecksum(
@Nullable String splitName,
- @PackageManager.FileChecksumKind int kind,
- @NonNull byte[] value,
+ @NonNull Checksum checksum,
@Nullable byte[] sourceCertificate) {
this.mSplitName = splitName;
- this.mKind = kind;
+ this.mChecksum = checksum;
com.android.internal.util.AnnotationValidations.validate(
- PackageManager.FileChecksumKind.class, null, mKind);
- this.mValue = value;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mValue);
+ NonNull.class, null, mChecksum);
this.mSourceCertificate = sourceCertificate;
// onConstructed(); // You can define this method to get a callback
@@ -148,22 +162,6 @@ public final class FileChecksum implements Parcelable {
return mSplitName;
}
- /**
- * Checksum kind.
- */
- @DataClass.Generated.Member
- public @PackageManager.FileChecksumKind int getKind() {
- return mKind;
- }
-
- /**
- * Checksum value.
- */
- @DataClass.Generated.Member
- public @NonNull byte[] getValue() {
- return mValue;
- }
-
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -172,11 +170,10 @@ public final class FileChecksum implements Parcelable {
byte flg = 0;
if (mSplitName != null) flg |= 0x1;
- if (mSourceCertificate != null) flg |= 0x8;
+ if (mSourceCertificate != null) flg |= 0x4;
dest.writeByte(flg);
if (mSplitName != null) dest.writeString(mSplitName);
- dest.writeInt(mKind);
- dest.writeByteArray(mValue);
+ dest.writeTypedObject(mChecksum, flags);
if (mSourceCertificate != null) dest.writeByteArray(mSourceCertificate);
}
@@ -187,47 +184,43 @@ public final class FileChecksum implements Parcelable {
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- /* package-private */ FileChecksum(@NonNull Parcel in) {
+ /* package-private */ ApkChecksum(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte();
String splitName = (flg & 0x1) == 0 ? null : in.readString();
- int kind = in.readInt();
- byte[] value = in.createByteArray();
- byte[] sourceCertificate = (flg & 0x8) == 0 ? null : in.createByteArray();
+ Checksum checksum = (Checksum) in.readTypedObject(Checksum.CREATOR);
+ byte[] sourceCertificate = (flg & 0x4) == 0 ? null : in.createByteArray();
this.mSplitName = splitName;
- this.mKind = kind;
- com.android.internal.util.AnnotationValidations.validate(
- PackageManager.FileChecksumKind.class, null, mKind);
- this.mValue = value;
+ this.mChecksum = checksum;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mValue);
+ NonNull.class, null, mChecksum);
this.mSourceCertificate = sourceCertificate;
// onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<FileChecksum> CREATOR
- = new Parcelable.Creator<FileChecksum>() {
+ public static final @NonNull Parcelable.Creator<ApkChecksum> CREATOR
+ = new Parcelable.Creator<ApkChecksum>() {
@Override
- public FileChecksum[] newArray(int size) {
- return new FileChecksum[size];
+ public ApkChecksum[] newArray(int size) {
+ return new ApkChecksum[size];
}
@Override
- public FileChecksum createFromParcel(@NonNull Parcel in) {
- return new FileChecksum(in);
+ public ApkChecksum createFromParcel(@NonNull Parcel in) {
+ return new ApkChecksum(in);
}
};
@DataClass.Generated(
- time = 1598322801861L,
+ time = 1599845645160L,
codegenVersion = "1.0.15",
- sourceFile = "frameworks/base/core/java/android/content/pm/FileChecksum.java",
- inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.content.pm.PackageManager.FileChecksumKind int mKind\nprivate final @android.annotation.NonNull byte[] mValue\nprivate final @android.annotation.Nullable byte[] mSourceCertificate\npublic @android.annotation.Nullable java.security.cert.Certificate getSourceCertificate()\nclass FileChecksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+ 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)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/Checksum.aidl b/core/java/android/content/pm/Checksum.aidl
new file mode 100644
index 000000000000..f0ca206fad58
--- /dev/null
+++ b/core/java/android/content/pm/Checksum.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+parcelable Checksum;
+
diff --git a/core/java/android/content/pm/Checksum.java b/core/java/android/content/pm/Checksum.java
new file mode 100644
index 000000000000..123aaddda7ce
--- /dev/null
+++ b/core/java/android/content/pm/Checksum.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * A typed checksum.
+ *
+ * @see PackageInstaller.Session#addChecksums(String, List)
+ */
+@DataClass(genHiddenConstructor = true, 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>.
+ *
+ * @see PackageManager#getChecksums
+ */
+ public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001;
+
+ /**
+ * MD5 hash computed over all file bytes.
+ *
+ * @see PackageManager#getChecksums
+ */
+ public static final int WHOLE_MD5 = 0x00000002;
+
+ /**
+ * SHA1 hash computed over all file bytes.
+ *
+ * @see PackageManager#getChecksums
+ */
+ public static final int WHOLE_SHA1 = 0x00000004;
+
+ /**
+ * SHA256 hash computed over all file bytes.
+ *
+ * @see PackageManager#getChecksums
+ */
+ public static final int WHOLE_SHA256 = 0x00000008;
+
+ /**
+ * SHA512 hash computed over all file bytes.
+ *
+ * @see PackageManager#getChecksums
+ */
+ public static final int WHOLE_SHA512 = 0x00000010;
+
+ /**
+ * Root SHA256 hash of a 1M Merkle tree computed over protected content.
+ * Excludes signing block.
+ * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
+ *
+ * @see PackageManager#getChecksums
+ */
+ public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020;
+
+ /**
+ * Root SHA512 hash of a 1M Merkle tree computed over protected content.
+ * Excludes signing block.
+ * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
+ *
+ * @see PackageManager#getChecksums
+ */
+ public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = {"WHOLE_", "PARTIAL_"}, value = {
+ WHOLE_MERKLE_ROOT_4K_SHA256,
+ WHOLE_MD5,
+ WHOLE_SHA1,
+ WHOLE_SHA256,
+ WHOLE_SHA512,
+ PARTIAL_MERKLE_ROOT_1M_SHA256,
+ PARTIAL_MERKLE_ROOT_1M_SHA512,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Kind {}
+
+ /**
+ * Checksum kind.
+ */
+ private final @Checksum.Kind int mKind;
+ /**
+ * Checksum value.
+ */
+ private final @NonNull byte[] mValue;
+
+
+
+ // Code below generated by codegen v1.0.15.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/Checksum.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new Checksum.
+ *
+ * @param kind
+ * Checksum kind.
+ * @param value
+ * Checksum value.
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public Checksum(
+ @Checksum.Kind int kind,
+ @NonNull byte[] value) {
+ this.mKind = kind;
+ com.android.internal.util.AnnotationValidations.validate(
+ Checksum.Kind.class, null, mKind);
+ this.mValue = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mValue);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * Checksum kind.
+ */
+ @DataClass.Generated.Member
+ public @Checksum.Kind int getKind() {
+ return mKind;
+ }
+
+ /**
+ * Checksum value.
+ */
+ @DataClass.Generated.Member
+ public @NonNull byte[] getValue() {
+ return mValue;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mKind);
+ dest.writeByteArray(mValue);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ Checksum(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int kind = in.readInt();
+ byte[] value = in.createByteArray();
+
+ this.mKind = kind;
+ com.android.internal.util.AnnotationValidations.validate(
+ Checksum.Kind.class, null, mKind);
+ this.mValue = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mValue);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<Checksum> CREATOR
+ = new Parcelable.Creator<Checksum>() {
+ @Override
+ public Checksum[] newArray(int size) {
+ return new Checksum[size];
+ }
+
+ @Override
+ public Checksum createFromParcel(@NonNull Parcel in) {
+ return new Checksum(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1599845646883L,
+ 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)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index fc20263fe00a..6ccbc36e26f6 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.content.pm.Checksum;
import android.content.pm.DataLoaderParamsParcel;
import android.content.pm.IPackageInstallObserver2;
import android.content.IntentSender;
@@ -33,6 +34,8 @@ interface IPackageInstallerSession {
void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd);
+ void addChecksums(String name, in Checksum[] checksums);
+
void removeSplit(String splitName);
void close();
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index e6ea04433114..9eb95a3f6707 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1219,6 +1219,31 @@ public class PackageInstaller {
}
/**
+ * Adds installer-provided checksums for the APK file in session.
+ *
+ * @param name previously written as part of this session.
+ * @param checksums installer intends to make available via
+ * {@link PackageManager#getChecksums(String, boolean, int, List,
+ * IntentSender)}.
+ * @throws SecurityException if called after the session has been
+ * committed or abandoned.
+ */
+ public void addChecksums(@NonNull String name, @NonNull List<Checksum> checksums)
+ throws IOException {
+ Objects.requireNonNull(name);
+ Objects.requireNonNull(checksums);
+
+ try {
+ mSession.addChecksums(name, checksums.toArray(new Checksum[checksums.size()]));
+ } catch (RuntimeException e) {
+ ExceptionUtils.maybeUnwrapIOException(e);
+ throw e;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Attempt to commit everything staged in this session. This may require
* user intervention, and so it may not happen immediately. The final
* result of the commit will be reported through the given callback.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d2395ec9f69f..e68df3383642 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -7844,74 +7844,6 @@ public abstract class PackageManager {
}
/**
- * Root SHA256 hash of a 4K Merkle tree computed over all file bytes.
- * <a href="https://source.android.com/security/apksigning/v4">See APK Signature Scheme V4</a>.
- * <a href="https://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git/tree/Documentation/filesystems/fsverity.rst">See fs-verity</a>.
- *
- * @see #getChecksums
- */
- public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001;
-
- /**
- * MD5 hash computed over all file bytes.
- *
- * @see #getChecksums
- */
- public static final int WHOLE_MD5 = 0x00000002;
-
- /**
- * SHA1 hash computed over all file bytes.
- *
- * @see #getChecksums
- */
- public static final int WHOLE_SHA1 = 0x00000004;
-
- /**
- * SHA256 hash computed over all file bytes.
- *
- * @see #getChecksums
- */
- public static final int WHOLE_SHA256 = 0x00000008;
-
- /**
- * SHA512 hash computed over all file bytes.
- *
- * @see #getChecksums
- */
- public static final int WHOLE_SHA512 = 0x00000010;
-
- /**
- * Root SHA256 hash of a 1M Merkle tree computed over protected content.
- * Excludes signing block.
- * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
- *
- * @see #getChecksums
- */
- public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020;
-
- /**
- * Root SHA512 hash of a 1M Merkle tree computed over protected content.
- * Excludes signing block.
- * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
- *
- * @see #getChecksums
- */
- public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040;
-
- /** @hide */
- @IntDef(flag = true, prefix = {"WHOLE_", "PARTIAL_"}, value = {
- WHOLE_MERKLE_ROOT_4K_SHA256,
- WHOLE_MD5,
- WHOLE_SHA1,
- WHOLE_SHA256,
- WHOLE_SHA512,
- PARTIAL_MERKLE_ROOT_1M_SHA256,
- PARTIAL_MERKLE_ROOT_1M_SHA512,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface FileChecksumKind {}
-
- /**
* Trust any Installer to provide checksums for the package.
* @see #getChecksums
*/
@@ -7940,12 +7872,12 @@ public abstract class PackageManager {
* {@link #TRUST_ALL} will return checksums from any Installer,
* {@link #TRUST_NONE} disables optimized Installer-enforced checksums.
* @param statusReceiver called once when the results are available as
- * {@link #EXTRA_CHECKSUMS} of type FileChecksum[].
+ * {@link #EXTRA_CHECKSUMS} of type ApkChecksum[].
* @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
* @throws NameNotFoundException if a package with the given name cannot be found on the system.
*/
public void getChecksums(@NonNull String packageName, boolean includeSplits,
- @FileChecksumKind int required, @Nullable List<Certificate> trustedInstallers,
+ @Checksum.Kind int required, @Nullable List<Certificate> trustedInstallers,
@NonNull IntentSender statusReceiver)
throws CertificateEncodingException, IOException, NameNotFoundException {
throw new UnsupportedOperationException("getChecksums not implemented in subclass");
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index c0509445a54a..d62e132b5745 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -11447,6 +11447,16 @@ package android.content.pm {
field public final float widthFraction;
}
+ public final class ApkChecksum implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getKind();
+ method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
+ method @Nullable public String getSplitName();
+ method @NonNull public byte[] getValue();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ApkChecksum> CREATOR;
+ }
+
public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
ctor public ApplicationInfo();
ctor public ApplicationInfo(android.content.pm.ApplicationInfo);
@@ -11548,6 +11558,21 @@ package android.content.pm {
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ChangedPackages> CREATOR;
}
+ public final class Checksum implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getKind();
+ method @NonNull public byte[] getValue();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.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 public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
+ field public static final int WHOLE_SHA1 = 4; // 0x4
+ field public static final int WHOLE_SHA256 = 8; // 0x8
+ field public static final int WHOLE_SHA512 = 16; // 0x10
+ }
+
public class ComponentInfo extends android.content.pm.PackageItemInfo {
ctor public ComponentInfo();
ctor public ComponentInfo(android.content.pm.ComponentInfo);
@@ -11619,16 +11644,6 @@ package android.content.pm {
field public int version;
}
- public final class FileChecksum implements android.os.Parcelable {
- method public int describeContents();
- method public int getKind();
- method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
- method @Nullable public String getSplitName();
- method @NonNull public byte[] getValue();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.FileChecksum> CREATOR;
- }
-
public final class InstallSourceInfo implements android.os.Parcelable {
method public int describeContents();
method @Nullable public String getInitiatingPackageName();
@@ -11849,6 +11864,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 public void addChildSessionId(int);
method public void close();
method public void commit(@NonNull android.content.IntentSender);
@@ -12250,8 +12266,6 @@ package android.content.pm {
field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
- field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
- field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
field public static final int PERMISSION_DENIED = -1; // 0xffffffff
field public static final int PERMISSION_GRANTED = 0; // 0x0
field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
@@ -12266,11 +12280,6 @@ package android.content.pm {
field public static final int VERIFICATION_ALLOW = 1; // 0x1
field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
- field public static final int WHOLE_MD5 = 2; // 0x2
- field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
- field public static final int WHOLE_SHA1 = 4; // 0x4
- field public static final int WHOLE_SHA256 = 8; // 0x8
- field public static final int WHOLE_SHA512 = 16; // 0x10
}
public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 0338ed802436..c6c80aef4432 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -16,14 +16,15 @@
package com.android.server.pm;
+import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA256;
+import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA512;
+import static android.content.pm.Checksum.WHOLE_MD5;
+import static android.content.pm.Checksum.WHOLE_MERKLE_ROOT_4K_SHA256;
+import static android.content.pm.Checksum.WHOLE_SHA1;
+import static android.content.pm.Checksum.WHOLE_SHA256;
+import static android.content.pm.Checksum.WHOLE_SHA512;
import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
-import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA256;
-import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA512;
-import static android.content.pm.PackageManager.WHOLE_MD5;
-import static android.content.pm.PackageManager.WHOLE_MERKLE_ROOT_4K_SHA256;
-import static android.content.pm.PackageManager.WHOLE_SHA1;
-import static android.content.pm.PackageManager.WHOLE_SHA256;
-import static android.content.pm.PackageManager.WHOLE_SHA512;
+import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
@@ -33,14 +34,16 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.pm.FileChecksum;
-import android.content.pm.PackageManager;
+import android.content.pm.ApkChecksum;
+import android.content.pm.Checksum;
import android.content.pm.PackageParser;
+import android.content.pm.Signature;
import android.os.Handler;
import android.os.SystemClock;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
import android.util.apk.ApkSignatureSchemeV2Verifier;
@@ -57,18 +60,26 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.security.VerityUtils;
import java.io.BufferedInputStream;
+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;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Provides checksums for APK.
@@ -76,6 +87,8 @@ import java.util.Map;
public class ApkChecksums {
static final String TAG = "ApkChecksums";
+ private static final String DIGESTS_FILE_EXTENSION = ".digests";
+
// MessageDigest algorithms.
static final String ALGO_MD5 = "MD5";
static final String ALGO_SHA1 = "SHA1";
@@ -131,6 +144,100 @@ public class ApkChecksums {
}
/**
+ * Return the digests path associated with the given code path
+ * (replaces '.apk' extension with '.digests')
+ *
+ * @throws IllegalArgumentException if the code path is not an .apk.
+ */
+ public static String buildDigestsPathForApk(String codePath) {
+ if (!PackageParser.isApkPath(codePath)) {
+ throw new IllegalStateException("Code path is not an apk " + codePath);
+ }
+ return codePath.substring(0, codePath.length() - APK_FILE_EXTENSION.length())
+ + DIGESTS_FILE_EXTENSION;
+ }
+
+ /**
+ * Search for the digests file associated with the given target file.
+ * If it exists, the method returns the digests file; otherwise it returns null.
+ */
+ public static File findDigestsForFile(File targetFile) {
+ String digestsPath = buildDigestsPathForApk(targetFile.getAbsolutePath());
+ File digestsFile = new File(digestsPath);
+ return digestsFile.exists() ? digestsFile : null;
+ }
+
+ /**
+ * Serialize checksums to file in binary format.
+ */
+ public static void writeChecksums(File file, ApkChecksum[] checksums)
+ throws IOException, CertificateException {
+ try (OutputStream os = new FileOutputStream(file);
+ DataOutputStream dos = new DataOutputStream(os)) {
+ dos.writeInt(checksums.length);
+ for (ApkChecksum checksum : checksums) {
+ final String splitName = checksum.getSplitName();
+ if (splitName == null) {
+ dos.writeInt(-1);
+ } else {
+ dos.writeInt(splitName.length());
+ dos.writeUTF(splitName);
+ }
+
+ dos.writeInt(checksum.getKind());
+
+ final byte[] valueBytes = checksum.getValue();
+ dos.writeInt(valueBytes.length);
+ dos.write(valueBytes);
+
+ final Certificate cert = checksum.getSourceCertificate();
+ final byte[] certBytes = (cert == null) ? null : cert.getEncoded();
+ if (certBytes == null) {
+ dos.writeInt(-1);
+ } else {
+ dos.writeInt(certBytes.length);
+ dos.write(certBytes);
+ }
+ }
+ }
+ }
+
+ /**
+ * Deserialize array of checksums previously stored in
+ * {@link #writeChecksums(File, ApkChecksum[])}.
+ */
+ private static ApkChecksum[] readChecksums(File file) throws IOException {
+ try (InputStream is = new FileInputStream(file);
+ DataInputStream dis = new DataInputStream(is)) {
+ final int size = dis.readInt();
+ ApkChecksum[] checksums = new ApkChecksum[size];
+ for (int i = 0; i < size; ++i) {
+ final String splitName;
+ if (dis.readInt() < 0) {
+ splitName = null;
+ } else {
+ splitName = dis.readUTF();
+ }
+ final int kind = dis.readInt();
+ final byte[] valueBytes = new byte[dis.readInt()];
+ dis.read(valueBytes);
+ final byte[] certBytes;
+ final int certBytesLength = dis.readInt();
+ if (certBytesLength < 0) {
+ certBytes = null;
+ } else {
+ certBytes = new byte[certBytesLength];
+ dis.read(certBytes);
+ }
+ checksums[i] = new ApkChecksum(splitName, new Checksum(kind, valueBytes),
+ certBytes);
+ }
+ return checksums;
+ }
+ }
+
+
+ /**
* Fetch or calculate checksums for the collection of files.
*
* @param filesToChecksum split name, null for base and File to fetch checksums for
@@ -142,20 +249,20 @@ public class ApkChecksums {
* @param statusReceiver to receive the resulting checksums
*/
public static void getChecksums(List<Pair<String, File>> filesToChecksum,
- @PackageManager.FileChecksumKind int optional,
- @PackageManager.FileChecksumKind int required,
+ @Checksum.Kind int optional,
+ @Checksum.Kind int required,
@Nullable Certificate[] trustedInstallers,
@NonNull IntentSender statusReceiver,
@NonNull Injector injector) {
- List<Map<Integer, FileChecksum>> result = new ArrayList<>(filesToChecksum.size());
+ List<Map<Integer, ApkChecksum>> result = new ArrayList<>(filesToChecksum.size());
for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
final String split = filesToChecksum.get(i).first;
final File file = filesToChecksum.get(i).second;
- Map<Integer, FileChecksum> checksums = new ArrayMap<>();
+ Map<Integer, ApkChecksum> checksums = new ArrayMap<>();
result.add(checksums);
try {
- getAvailableFileChecksums(split, file, optional | required, trustedInstallers,
+ getAvailableApkChecksums(split, file, optional | required, trustedInstallers,
checksums);
} catch (Throwable e) {
Slog.e(TAG, "Preferred checksum calculation error", e);
@@ -168,18 +275,18 @@ public class ApkChecksums {
}
private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum,
- List<Map<Integer, FileChecksum>> result,
- @PackageManager.FileChecksumKind int required,
+ List<Map<Integer, ApkChecksum>> result,
+ @Checksum.Kind int required,
@NonNull IntentSender statusReceiver,
@NonNull Injector injector,
long startTime) {
final boolean timeout =
SystemClock.uptimeMillis() - startTime >= PROCESS_REQUIRED_CHECKSUMS_TIMEOUT_MILLIS;
- List<FileChecksum> allChecksums = new ArrayList<>();
+ List<ApkChecksum> allChecksums = new ArrayList<>();
for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
final String split = filesToChecksum.get(i).first;
final File file = filesToChecksum.get(i).second;
- Map<Integer, FileChecksum> checksums = result.get(i);
+ Map<Integer, ApkChecksum> checksums = result.get(i);
try {
if (!timeout || required != 0) {
@@ -192,7 +299,7 @@ public class ApkChecksums {
return;
}
- getRequiredFileChecksums(split, file, required, checksums);
+ getRequiredApkChecksums(split, file, required, checksums);
}
allChecksums.addAll(checksums.values());
} catch (Throwable e) {
@@ -202,7 +309,7 @@ public class ApkChecksums {
final Intent intent = new Intent();
intent.putExtra(EXTRA_CHECKSUMS,
- allChecksums.toArray(new FileChecksum[allChecksums.size()]));
+ allChecksums.toArray(new ApkChecksum[allChecksums.size()]));
try {
statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null);
@@ -222,16 +329,16 @@ public class ApkChecksums {
* [] - trust nobody.
* @param checksums resulting checksums
*/
- private static void getAvailableFileChecksums(String split, File file,
- @PackageManager.FileChecksumKind int kinds,
+ private static void getAvailableApkChecksums(String split, File file,
+ @Checksum.Kind int kinds,
@Nullable Certificate[] trustedInstallers,
- Map<Integer, FileChecksum> checksums) {
+ Map<Integer, ApkChecksum> checksums) {
final String filePath = file.getAbsolutePath();
// Always available: FSI or IncFs.
if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) {
// Hashes in fs-verity and IncFS are always verified.
- FileChecksum checksum = extractHashFromFS(split, filePath);
+ ApkChecksum checksum = extractHashFromFS(split, filePath);
if (checksum != null) {
checksums.put(checksum.getKind(), checksum);
}
@@ -240,22 +347,40 @@ public class ApkChecksums {
// System enforced: v2/v3.
if (isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums) || isRequired(
PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) {
- Map<Integer, FileChecksum> v2v3checksums = extractHashFromV2V3Signature(
+ Map<Integer, ApkChecksum> v2v3checksums = extractHashFromV2V3Signature(
split, filePath, kinds);
if (v2v3checksums != null) {
checksums.putAll(v2v3checksums);
}
}
- // TODO(b/160605420): Installer provided.
+ if (trustedInstallers == null || trustedInstallers.length > 0) {
+ final File digestsFile = new File(buildDigestsPathForApk(filePath));
+ if (digestsFile.exists()) {
+ try {
+ final ApkChecksum[] digests = readChecksums(digestsFile);
+ final Set<Signature> trusted = convertToSet(trustedInstallers);
+ for (ApkChecksum digest : digests) {
+ if (isRequired(digest.getKind(), kinds, checksums) && isTrusted(digest,
+ trusted)) {
+ checksums.put(digest.getKind(), digest);
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Error reading .digests", e);
+ } catch (CertificateEncodingException e) {
+ Slog.e(TAG, "Error encoding trustedInstallers", e);
+ }
+ }
+ }
}
/**
* Whether the file is available for checksumming or we need to wait.
*/
private static boolean needToWait(File file,
- @PackageManager.FileChecksumKind int kinds,
- Map<Integer, FileChecksum> checksums,
+ @Checksum.Kind int kinds,
+ Map<Integer, ApkChecksum> checksums,
@NonNull Injector injector) throws IOException {
if (!isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)
&& !isRequired(WHOLE_MD5, kinds, checksums)
@@ -274,12 +399,13 @@ public class ApkChecksums {
IncrementalManager manager = injector.getIncrementalManager();
if (manager == null) {
- throw new IllegalStateException("IncrementalManager is missing.");
+ Slog.e(TAG, "IncrementalManager is missing.");
+ return false;
}
IncrementalStorage storage = manager.openStorage(filePath);
if (storage == null) {
- throw new IllegalStateException(
- "IncrementalStorage is missing for a path on IncFs: " + filePath);
+ Slog.e(TAG, "IncrementalStorage is missing for a path on IncFs: " + filePath);
+ return false;
}
return !storage.isFileFullyLoaded(filePath);
@@ -293,9 +419,9 @@ public class ApkChecksums {
* @param kinds mask to forcefully calculate if not available
* @param checksums resulting checksums
*/
- private static void getRequiredFileChecksums(String split, File file,
- @PackageManager.FileChecksumKind int kinds,
- Map<Integer, FileChecksum> checksums) {
+ private static void getRequiredApkChecksums(String split, File file,
+ @Checksum.Kind int kinds,
+ Map<Integer, ApkChecksum> checksums) {
final String filePath = file.getAbsolutePath();
// Manually calculating required checksums if not readily available.
@@ -310,7 +436,7 @@ public class ApkChecksums {
}
});
checksums.put(WHOLE_MERKLE_ROOT_4K_SHA256,
- new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, generatedRootHash));
+ new ApkChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, generatedRootHash));
} catch (IOException | NoSuchAlgorithmException | DigestException e) {
Slog.e(TAG, "Error calculating WHOLE_MERKLE_ROOT_4K_SHA256", e);
}
@@ -324,8 +450,8 @@ public class ApkChecksums {
calculatePartialChecksumsIfRequested(checksums, split, file, kinds);
}
- private static boolean isRequired(@PackageManager.FileChecksumKind int kind,
- @PackageManager.FileChecksumKind int kinds, Map<Integer, FileChecksum> checksums) {
+ private static boolean isRequired(@Checksum.Kind int kind,
+ @Checksum.Kind int kinds, Map<Integer, ApkChecksum> checksums) {
if ((kinds & kind) == 0) {
return false;
}
@@ -335,12 +461,36 @@ public class ApkChecksums {
return true;
}
- private static FileChecksum extractHashFromFS(String split, String filePath) {
+ /**
+ * Signature class provides a fast way to compare certificates using their hashes.
+ * The hash is exactly the same as in X509/Certificate.
+ */
+ private static Set<Signature> convertToSet(@Nullable Certificate[] array) throws
+ CertificateEncodingException {
+ if (array == null) {
+ return null;
+ }
+ final Set<Signature> set = new ArraySet<>(array.length);
+ for (Certificate item : array) {
+ set.add(new Signature(item.getEncoded()));
+ }
+ return set;
+ }
+
+ private static boolean isTrusted(ApkChecksum checksum, Set<Signature> trusted) {
+ if (trusted == null) {
+ return true;
+ }
+ final Signature signature = new Signature(checksum.getSourceCertificateBytes());
+ return trusted.contains(signature);
+ }
+
+ private static ApkChecksum extractHashFromFS(String split, String filePath) {
// verity first
{
byte[] hash = VerityUtils.getFsverityRootHash(filePath);
if (hash != null) {
- return new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash);
+ return new ApkChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash);
}
}
// v4 next
@@ -350,7 +500,7 @@ public class ApkChecksums {
byte[] hash = signer.contentDigests.getOrDefault(CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
null);
if (hash != null) {
- return new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash);
+ return new ApkChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash);
}
} catch (SignatureNotFoundException e) {
// Nothing
@@ -360,7 +510,7 @@ public class ApkChecksums {
return null;
}
- private static Map<Integer, FileChecksum> extractHashFromV2V3Signature(
+ private static Map<Integer, ApkChecksum> extractHashFromV2V3Signature(
String split, String filePath, int kinds) {
Map<Integer, byte[]> contentDigests = null;
try {
@@ -377,19 +527,19 @@ public class ApkChecksums {
return null;
}
- Map<Integer, FileChecksum> checksums = new ArrayMap<>();
+ Map<Integer, ApkChecksum> checksums = new ArrayMap<>();
if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0) {
byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA256, null);
if (hash != null) {
checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA256,
- new FileChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA256, hash));
+ new ApkChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA256, hash));
}
}
if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0) {
byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA512, null);
if (hash != null) {
checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA512,
- new FileChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA512, hash));
+ new ApkChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA512, hash));
}
}
return checksums;
@@ -411,17 +561,17 @@ public class ApkChecksums {
}
}
- private static void calculateChecksumIfRequested(Map<Integer, FileChecksum> checksums,
+ private static void calculateChecksumIfRequested(Map<Integer, ApkChecksum> checksums,
String split, File file, int required, int kind) {
if ((required & kind) != 0 && !checksums.containsKey(kind)) {
- final byte[] checksum = getFileChecksum(file, kind);
+ final byte[] checksum = getApkChecksum(file, kind);
if (checksum != null) {
- checksums.put(kind, new FileChecksum(split, kind, checksum));
+ checksums.put(kind, new ApkChecksum(split, kind, checksum));
}
}
}
- private static byte[] getFileChecksum(File file, int kind) {
+ private static byte[] getApkChecksum(File file, int kind) {
try (FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis)) {
byte[] dataBytes = new byte[512 * 1024];
@@ -466,7 +616,7 @@ public class ApkChecksums {
}
}
- private static void calculatePartialChecksumsIfRequested(Map<Integer, FileChecksum> checksums,
+ private static void calculatePartialChecksumsIfRequested(Map<Integer, ApkChecksum> checksums,
String split, File file, int required) {
boolean needSignatureSha256 =
(required & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0 && !checksums.containsKey(
@@ -500,7 +650,7 @@ public class ApkChecksums {
for (int i = 0, size = digestAlgos.length; i < size; ++i) {
int checksumKind = getChecksumKindForContentDigestAlgo(digestAlgos[i]);
if (checksumKind != -1) {
- checksums.put(checksumKind, new FileChecksum(split, checksumKind, digests[i]));
+ checksums.put(checksumKind, new ApkChecksum(split, checksumKind, digests[i]));
}
}
} catch (IOException | DigestException e) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 155af82289d4..f52db5fb5f2f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -732,9 +732,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
installerAttributionTag);
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid,
- installSource, params, createdMillis,
- stageDir, stageCid, null, false, false, false, false, null, SessionInfo.INVALID_ID,
- false, false, false, SessionInfo.STAGED_SESSION_NO_ERROR, "");
+ installSource, params, createdMillis, stageDir, stageCid, null, null, false, false,
+ false, false, null, SessionInfo.INVALID_ID, false, false, false,
+ SessionInfo.STAGED_SESSION_NO_ERROR, "");
synchronized (mSessions) {
mSessions.put(sessionId, session);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 17cd8f58c3a3..87dedfe9c84a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -27,6 +27,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
import static android.content.pm.PackageParser.APEX_FILE_EXTENSION;
import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
import static android.system.OsConstants.O_CREAT;
@@ -61,7 +62,9 @@ import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.pm.ApkChecksum;
import android.content.pm.ApplicationInfo;
+import android.content.pm.Checksum;
import android.content.pm.DataLoaderManager;
import android.content.pm.DataLoaderParams;
import android.content.pm.DataLoaderParamsParcel;
@@ -84,6 +87,7 @@ import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ApkLite;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.Signature;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.result.ParseResult;
@@ -118,6 +122,7 @@ import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.ExceptionUtils;
import android.util.MathUtils;
@@ -153,6 +158,7 @@ import java.io.FileDescriptor;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -176,6 +182,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
static final String TAG_SESSION = "session";
static final String TAG_CHILD_SESSION = "childSession";
static final String TAG_SESSION_FILE = "sessionFile";
+ static final String TAG_SESSION_CHECKSUM = "sessionChecksum";
private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION =
"whitelisted-restricted-permission";
@@ -230,10 +237,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final String ATTR_LENGTH_BYTES = "lengthBytes";
private static final String ATTR_METADATA = "metadata";
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_CERTIFICATE = "checksumCertificate";
private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT;
private static final InstallationFile[] EMPTY_INSTALLATION_FILE_ARRAY = {};
+ private static final ApkChecksum[] EMPTY_FILE_CHECKSUM_ARRAY = {};
private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";
@@ -380,6 +391,27 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private ArraySet<FileEntry> mFiles = new ArraySet<>();
+ static class CertifiedChecksum {
+ final @NonNull Checksum mChecksum;
+ final @NonNull byte[] mCertificate;
+
+ CertifiedChecksum(@NonNull Checksum checksum, @NonNull byte[] certificate) {
+ mChecksum = checksum;
+ mCertificate = certificate;
+ }
+
+ Checksum getChecksum() {
+ return mChecksum;
+ }
+
+ byte[] getCertificate() {
+ return mCertificate;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private ArrayMap<String, List<CertifiedChecksum>> mChecksums = new ArrayMap<>();
+
@GuardedBy("mLock")
private boolean mStagedSessionApplied;
@GuardedBy("mLock")
@@ -581,8 +613,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager,
int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
SessionParams params, long createdMillis,
- File stageDir, String stageCid, InstallationFile[] files, boolean prepared,
- boolean committed, boolean destroyed, boolean sealed,
+ File stageDir, String stageCid, InstallationFile[] files,
+ ArrayMap<String, List<CertifiedChecksum>> checksums,
+ boolean prepared, boolean committed, boolean destroyed, boolean sealed,
@Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
String stagedSessionErrorMessage) {
@@ -615,6 +648,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
this.mParentSessionId = parentSessionId;
if (files != null) {
+ mFiles.ensureCapacity(files.length);
for (int i = 0, size = files.length; i < size; ++i) {
InstallationFile file = files[i];
if (!mFiles.add(new FileEntry(i, file))) {
@@ -624,6 +658,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ if (checksums != null) {
+ mChecksums.putAll(checksums);
+ }
+
if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) {
throw new IllegalArgumentException(
"Exactly one of stageDir or stageCid stage must be set");
@@ -907,6 +945,45 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
@Override
+ public void addChecksums(String name, @NonNull Checksum[] checksums) {
+ if (checksums.length == 0) {
+ return;
+ }
+
+ final PackageManagerInternal pmi = LocalServices.getService(
+ PackageManagerInternal.class);
+ final AndroidPackage callingInstaller = pmi.getPackage(Binder.getCallingUid());
+
+ 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;
+ 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();
+
+ synchronized (mLock) {
+ assertCallerIsOwnerOrRootLocked();
+ assertPreparedAndNotCommittedOrDestroyedLocked("addChecksums");
+
+ for (Checksum checksum : checksums) {
+ List<CertifiedChecksum> fileChecksums = mChecksums.get(name);
+ if (fileChecksums == null) {
+ fileChecksums = new ArrayList<>();
+ mChecksums.put(name, fileChecksums);
+ }
+ fileChecksums.add(new CertifiedChecksum(checksum, mainCertificateBytes));
+ }
+ }
+ }
+
+ @Override
public void removeSplit(String splitName) {
if (isDataLoaderInstallation()) {
throw new IllegalStateException(
@@ -2155,7 +2232,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
final File targetFile = new File(stageDir, targetName);
- resolveAndStageFileLocked(addedFile, targetFile);
+ resolveAndStageFileLocked(addedFile, targetFile, null);
mResolvedBaseFile = targetFile;
// Populate package name of the apex session
@@ -2174,6 +2251,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ private static String splitNameToFileName(String splitName) {
+ if (splitName == null) {
+ return "base";
+ }
+ return "split_" + splitName;
+ }
+
/**
* Validate install by confirming that all application packages are have
* consistent package name, version code, and signing certificates.
@@ -2259,37 +2343,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
assertApkConsistentLocked(String.valueOf(addedFile), apk);
// Take this opportunity to enforce uniform naming
- final String targetName;
- if (apk.splitName == null) {
- targetName = "base" + APK_FILE_EXTENSION;
- } else {
- targetName = "split_" + apk.splitName + APK_FILE_EXTENSION;
- }
+ final String fileName = splitNameToFileName(apk.splitName);
+ final String targetName = fileName + APK_FILE_EXTENSION;
if (!FileUtils.isValidExtFilename(targetName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Invalid filename: " + targetName);
}
final File targetFile = new File(stageDir, targetName);
- resolveAndStageFileLocked(addedFile, targetFile);
+ resolveAndStageFileLocked(addedFile, targetFile, apk.splitName);
// Base is coming from session
if (apk.splitName == null) {
mResolvedBaseFile = targetFile;
baseApk = apk;
}
-
- // Validate and add Dex Metadata (.dm).
- final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile);
- if (dexMetadataFile != null) {
- if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Invalid filename: " + dexMetadataFile);
- }
- final File targetDexMetadataFile = new File(stageDir,
- DexMetadataHelper.buildDexMetadataPathForApk(targetName));
- resolveAndStageFileLocked(dexMetadataFile, targetDexMetadataFile);
- }
}
if (removeSplitList.size() > 0) {
@@ -2338,15 +2406,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ if (!mChecksums.isEmpty()) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_SESSION_INVALID,
+ "Invalid checksum name(s): " + String.join(",", mChecksums.keySet()));
+ }
+
if (params.mode == SessionParams.MODE_FULL_INSTALL) {
// Full installs must include a base package
if (!stagedSplits.contains(null)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Full install must include a base package");
}
-
} else {
- ApplicationInfo appInfo = pkgInfo.applicationInfo;
+ final ApplicationInfo appInfo = pkgInfo.applicationInfo;
ParseResult<PackageLite> pkgLiteResult = ApkLiteParseUtils.parsePackageLite(
input.reset(), new File(appInfo.getCodePath()), 0);
if (pkgLiteResult.isError()) {
@@ -2365,33 +2438,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
assertApkConsistentLocked("Existing base", existingBase);
- // Inherit base if not overridden
+ // Inherit base if not overridden.
if (mResolvedBaseFile == null) {
mResolvedBaseFile = new File(appInfo.getBaseCodePath());
- resolveInheritedFileLocked(mResolvedBaseFile);
- // Inherit the dex metadata if present.
- final File baseDexMetadataFile =
- DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile);
- if (baseDexMetadataFile != null) {
- resolveInheritedFileLocked(baseDexMetadataFile);
- }
+ inheritFileLocked(mResolvedBaseFile);
baseApk = existingBase;
}
- // Inherit splits if not overridden
+ // Inherit splits if not overridden.
if (!ArrayUtils.isEmpty(existing.splitNames)) {
for (int i = 0; i < existing.splitNames.length; i++) {
final String splitName = existing.splitNames[i];
final File splitFile = new File(existing.splitCodePaths[i]);
final boolean splitRemoved = removeSplitList.contains(splitName);
if (!stagedSplits.contains(splitName) && !splitRemoved) {
- resolveInheritedFileLocked(splitFile);
- // Inherit the dex metadata if present.
- final File splitDexMetadataFile =
- DexMetadataHelper.findDexMetadataForFile(splitFile);
- if (splitDexMetadataFile != null) {
- resolveInheritedFileLocked(splitDexMetadataFile);
- }
+ inheritFileLocked(splitFile);
}
}
}
@@ -2492,11 +2553,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
@GuardedBy("mLock")
- private void resolveAndStageFileLocked(File origFile, File targetFile)
+ private void stageFileLocked(File origFile, File targetFile)
throws PackageManagerException {
mResolvedStagedFiles.add(targetFile);
maybeRenameFile(origFile, targetFile);
+ }
+ @GuardedBy("mLock")
+ private void maybeStageFsveritySignatureLocked(File origFile, File targetFile)
+ throws PackageManagerException {
final File originalSignature = new File(
VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
// Make sure .fsv_sig exists when it should, then resolve and stage it.
@@ -2521,12 +2586,83 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final File stagedSignature = new File(
VerityUtils.getFsveritySignatureFilePath(targetFile.getPath()));
- maybeRenameFile(originalSignature, stagedSignature);
- mResolvedStagedFiles.add(stagedSignature);
+
+ stageFileLocked(originalSignature, stagedSignature);
}
@GuardedBy("mLock")
- private void resolveInheritedFileLocked(File origFile) {
+ private void maybeStageDexMetadataLocked(File origFile, File targetFile)
+ throws PackageManagerException {
+ final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(origFile);
+ if (dexMetadataFile == null) {
+ return;
+ }
+
+ if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Invalid filename: " + dexMetadataFile);
+ }
+ final File targetDexMetadataFile = new File(stageDir,
+ DexMetadataHelper.buildDexMetadataPathForApk(targetFile.getName()));
+
+ stageFileLocked(dexMetadataFile, targetDexMetadataFile);
+ }
+
+ private static ApkChecksum[] createApkChecksums(String splitName,
+ List<CertifiedChecksum> checksums) {
+ ApkChecksum[] result = new ApkChecksum[checksums.size()];
+ for (int i = 0, size = checksums.size(); i < size; ++i) {
+ CertifiedChecksum checksum = checksums.get(i);
+ result[i] = new ApkChecksum(splitName, checksum.getChecksum(),
+ checksum.getCertificate());
+ }
+ return result;
+ }
+
+ @GuardedBy("mLock")
+ private void maybeStageDigestsLocked(File origFile, File targetFile, String splitName)
+ throws PackageManagerException {
+ final List<CertifiedChecksum> checksums = mChecksums.get(origFile.getName());
+ if (checksums == null) {
+ return;
+ }
+ mChecksums.remove(origFile.getName());
+
+ if (checksums.isEmpty()) {
+ return;
+ }
+
+ final File targetDigestsFile = new File(stageDir,
+ ApkChecksums.buildDigestsPathForApk(targetFile.getName()));
+ try {
+ ApkChecksums.writeChecksums(targetDigestsFile,
+ createApkChecksums(splitName, checksums));
+ } catch (CertificateException e) {
+ throw new PackageManagerException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
+ "Failed to encode certificate for " + mPackageName, e);
+ } catch (IOException e) {
+ throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
+ "Failed to store digests for " + mPackageName, e);
+ }
+
+ stageFileLocked(targetDigestsFile, targetDigestsFile);
+ }
+
+ @GuardedBy("mLock")
+ private void resolveAndStageFileLocked(File origFile, File targetFile, String splitName)
+ throws PackageManagerException {
+ stageFileLocked(origFile, targetFile);
+
+ // Stage fsverity signature if present.
+ maybeStageFsveritySignatureLocked(origFile, targetFile);
+ // Stage dex metadata (.dm) if present.
+ maybeStageDexMetadataLocked(origFile, targetFile);
+ // Stage checksums (.digests) if present.
+ maybeStageDigestsLocked(origFile, targetFile, splitName);
+ }
+
+ @GuardedBy("mLock")
+ private void inheritFileLocked(File origFile) {
mResolvedInheritedFiles.add(origFile);
// Inherit the fsverity signature file if present.
@@ -2535,6 +2671,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (fsveritySignatureFile.exists()) {
mResolvedInheritedFiles.add(fsveritySignatureFile);
}
+ // Inherit the dex metadata if present.
+ final File dexMetadataFile =
+ DexMetadataHelper.findDexMetadataForFile(origFile);
+ if (dexMetadataFile != null) {
+ mResolvedInheritedFiles.add(dexMetadataFile);
+ }
+ // Inherit the digests if present.
+ final File digestsFile = ApkChecksums.findDigestsForFile(origFile);
+ if (digestsFile != null) {
+ mResolvedInheritedFiles.add(digestsFile);
+ }
}
@GuardedBy("mLock")
@@ -3791,6 +3938,22 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
writeByteArrayAttribute(out, ATTR_SIGNATURE, file.getSignature());
out.endTag(null, TAG_SESSION_FILE);
}
+
+ for (int i = 0, isize = mChecksums.size(); i < isize; ++i) {
+ String fileName = mChecksums.keyAt(i);
+ List<CertifiedChecksum> checksums = mChecksums.valueAt(i);
+ for (int j = 0, jsize = checksums.size(); j < jsize; ++j) {
+ CertifiedChecksum checksum = checksums.get(j);
+ out.startTag(null, TAG_SESSION_CHECKSUM);
+ writeStringAttribute(out, ATTR_NAME, fileName);
+ writeIntAttribute(out, ATTR_CHECKSUM_KIND, checksum.getChecksum().getKind());
+ writeByteArrayAttribute(out, ATTR_CHECKSUM_VALUE,
+ checksum.getChecksum().getValue());
+ writeByteArrayAttribute(out, ATTR_CHECKSUM_CERTIFICATE,
+ checksum.getCertificate());
+ out.endTag(null, TAG_SESSION_CHECKSUM);
+ }
+ }
}
out.endTag(null, TAG_SESSION);
@@ -3904,6 +4067,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
int autoRevokePermissionsMode = MODE_DEFAULT;
List<Integer> childSessionIds = new ArrayList<>();
List<InstallationFile> files = new ArrayList<>();
+ ArrayMap<String, List<CertifiedChecksum>> checksums = new ArrayMap<>();
int outerDepth = in.getDepth();
int type;
while ((type = in.next()) != XmlPullParser.END_DOCUMENT
@@ -3932,6 +4096,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
readByteArrayAttribute(in, ATTR_METADATA),
readByteArrayAttribute(in, ATTR_SIGNATURE)));
}
+ if (TAG_SESSION_CHECKSUM.equals(in.getName())) {
+ final String fileName = readStringAttribute(in, ATTR_NAME);
+ final CertifiedChecksum certifiedChecksum = new CertifiedChecksum(
+ new Checksum(readIntAttribute(in, ATTR_CHECKSUM_KIND, 0),
+ readByteArrayAttribute(in, ATTR_CHECKSUM_VALUE)),
+ readByteArrayAttribute(in, ATTR_CHECKSUM_CERTIFICATE));
+
+ List<CertifiedChecksum> certifiedChecksums = checksums.get(fileName);
+ if (certifiedChecksums == null) {
+ certifiedChecksums = new ArrayList<>();
+ checksums.put(fileName, certifiedChecksums);
+ }
+ certifiedChecksums.add(certifiedChecksum);
+ }
}
if (grantedRuntimePermissions.size() > 0) {
@@ -3964,7 +4142,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
installOriginatingPackageName, installerPackageName, installerAttributionTag);
return new PackageInstallerSession(callback, context, pm, sessionProvider,
installerThread, stagingManager, sessionId, userId, installerUid,
- installSource, params, createdMillis, stageDir, stageCid, fileArray,
+ installSource, params, createdMillis, stageDir, stageCid, fileArray, checksums,
prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId,
isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 69e8e65166ac..999bbc9ecf4e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -164,6 +164,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.ChangedPackages;
+import android.content.pm.Checksum;
import android.content.pm.ComponentInfo;
import android.content.pm.DataLoaderType;
import android.content.pm.FallbackCategoryProvider;
@@ -2459,8 +2460,8 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void getChecksums(@NonNull String packageName, boolean includeSplits,
- @PackageManager.FileChecksumKind int optional,
- @PackageManager.FileChecksumKind int required, @Nullable List trustedInstallers,
+ @Checksum.Kind int optional,
+ @Checksum.Kind int required, @Nullable List trustedInstallers,
@NonNull IntentSender statusReceiver, int userId) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(statusReceiver);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index fe429c8823f8..4a6e11bca613 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -176,6 +176,7 @@ public class PackageInstallerSessionTest {
/* stageDir */ mTmpDir,
/* stageCid */ null,
/* files */ null,
+ /* checksums */ null,
/* prepared */ true,
/* committed */ true,
/* destroyed */ staged ? true : false,