Merge "Rollforward: Digest API: allow installer to provide digests for an APK."
This commit is contained in:
committed by
Android (Google) Code Review
commit
b406e6b4fd
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
|
||||
package android.content.pm;
|
||||
|
||||
parcelable FileChecksum;
|
||||
parcelable ApkChecksum;
|
||||
|
||||
@@ -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;
|
||||
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
|
||||
}
|
||||
|
||||
@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() {}
|
||||
|
||||
20
core/java/android/content/pm/Checksum.aidl
Normal file
20
core/java/android/content/pm/Checksum.aidl
Normal file
@@ -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;
|
||||
|
||||
228
core/java/android/content/pm/Checksum.java
Normal file
228
core/java/android/content/pm/Checksum.java
Normal file
@@ -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
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -1218,6 +1218,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
|
||||
|
||||
@@ -7843,74 +7843,6 @@ public abstract class PackageManager {
|
||||
"hasSigningCertificate not implemented in subclass");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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";
|
||||
@@ -130,6 +143,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.
|
||||
*
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
@@ -906,6 +944,45 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
return filterFiles(stageDir, names, sRemovedFilter);
|
||||
}
|
||||
|
||||
@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()) {
|
||||
@@ -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,14 +2586,84 @@ 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) {
|
||||
mResolvedInheritedFiles.add(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);
|
||||
maybeStageFsveritySignatureLocked(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 maybeInheritFsveritySignatureLocked(File origFile) {
|
||||
// Inherit the fsverity signature file if present.
|
||||
final File fsveritySignatureFile = new File(
|
||||
VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
|
||||
@@ -2537,6 +2672,26 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void inheritFileLocked(File origFile) {
|
||||
mResolvedInheritedFiles.add(origFile);
|
||||
|
||||
maybeInheritFsveritySignatureLocked(origFile);
|
||||
|
||||
// Inherit the dex metadata if present.
|
||||
final File dexMetadataFile =
|
||||
DexMetadataHelper.findDexMetadataForFile(origFile);
|
||||
if (dexMetadataFile != null) {
|
||||
mResolvedInheritedFiles.add(dexMetadataFile);
|
||||
maybeInheritFsveritySignatureLocked(dexMetadataFile);
|
||||
}
|
||||
// Inherit the digests if present.
|
||||
final File digestsFile = ApkChecksums.findDigestsForFile(origFile);
|
||||
if (digestsFile != null) {
|
||||
mResolvedInheritedFiles.add(digestsFile);
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void assertApkConsistentLocked(String tag, ApkLite apk)
|
||||
throws PackageManagerException {
|
||||
@@ -3791,6 +3946,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 +4075,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 +4104,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 +4150,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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -176,6 +176,7 @@ public class PackageInstallerSessionTest {
|
||||
/* stageDir */ mTmpDir,
|
||||
/* stageCid */ null,
|
||||
/* files */ null,
|
||||
/* checksums */ null,
|
||||
/* prepared */ true,
|
||||
/* committed */ true,
|
||||
/* destroyed */ staged ? true : false,
|
||||
|
||||
Reference in New Issue
Block a user