725 lines
24 KiB
Java
725 lines
24 KiB
Java
/*
|
|
* 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.integrity;
|
|
|
|
import static com.android.internal.util.Preconditions.checkArgument;
|
|
|
|
import android.annotation.IntDef;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
|
|
import com.android.internal.annotations.VisibleForTesting;
|
|
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.security.MessageDigest;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
|
|
/**
|
|
* Represents a simple formula consisting of an app install metadata field and a value.
|
|
*
|
|
* <p>Instances of this class are immutable.
|
|
*
|
|
* @hide
|
|
*/
|
|
@VisibleForTesting
|
|
public abstract class AtomicFormula extends IntegrityFormula {
|
|
|
|
/** @hide */
|
|
@IntDef(
|
|
value = {
|
|
PACKAGE_NAME,
|
|
APP_CERTIFICATE,
|
|
INSTALLER_NAME,
|
|
INSTALLER_CERTIFICATE,
|
|
VERSION_CODE,
|
|
PRE_INSTALLED,
|
|
STAMP_TRUSTED,
|
|
STAMP_CERTIFICATE_HASH,
|
|
APP_CERTIFICATE_LINEAGE,
|
|
})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
public @interface Key {}
|
|
|
|
/** @hide */
|
|
@IntDef(value = {EQ, GT, GTE})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
public @interface Operator {}
|
|
|
|
/**
|
|
* Package name of the app.
|
|
*
|
|
* <p>Can only be used in {@link StringAtomicFormula}.
|
|
*/
|
|
public static final int PACKAGE_NAME = 0;
|
|
|
|
/**
|
|
* SHA-256 of the app certificate of the app.
|
|
*
|
|
* <p>Can only be used in {@link StringAtomicFormula}.
|
|
*/
|
|
public static final int APP_CERTIFICATE = 1;
|
|
|
|
/**
|
|
* Package name of the installer. Will be empty string if installed by the system (e.g., adb).
|
|
*
|
|
* <p>Can only be used in {@link StringAtomicFormula}.
|
|
*/
|
|
public static final int INSTALLER_NAME = 2;
|
|
|
|
/**
|
|
* SHA-256 of the cert of the installer. Will be empty string if installed by the system (e.g.,
|
|
* adb).
|
|
*
|
|
* <p>Can only be used in {@link StringAtomicFormula}.
|
|
*/
|
|
public static final int INSTALLER_CERTIFICATE = 3;
|
|
|
|
/**
|
|
* Version code of the app.
|
|
*
|
|
* <p>Can only be used in {@link LongAtomicFormula}.
|
|
*/
|
|
public static final int VERSION_CODE = 4;
|
|
|
|
/**
|
|
* If the app is pre-installed on the device.
|
|
*
|
|
* <p>Can only be used in {@link BooleanAtomicFormula}.
|
|
*/
|
|
public static final int PRE_INSTALLED = 5;
|
|
|
|
/**
|
|
* If the APK has an embedded trusted stamp.
|
|
*
|
|
* <p>Can only be used in {@link BooleanAtomicFormula}.
|
|
*/
|
|
public static final int STAMP_TRUSTED = 6;
|
|
|
|
/**
|
|
* SHA-256 of the certificate used to sign the stamp embedded in the APK.
|
|
*
|
|
* <p>Can only be used in {@link StringAtomicFormula}.
|
|
*/
|
|
public static final int STAMP_CERTIFICATE_HASH = 7;
|
|
|
|
/**
|
|
* SHA-256 of a certificate in the signing lineage of the app.
|
|
*
|
|
* <p>Can only be used in {@link StringAtomicFormula}.
|
|
*/
|
|
public static final int APP_CERTIFICATE_LINEAGE = 8;
|
|
|
|
public static final int EQ = 0;
|
|
public static final int GT = 1;
|
|
public static final int GTE = 2;
|
|
|
|
private final @Key int mKey;
|
|
|
|
public AtomicFormula(@Key int key) {
|
|
checkArgument(isValidKey(key), "Unknown key: %d", key);
|
|
mKey = key;
|
|
}
|
|
|
|
/** An {@link AtomicFormula} with an key and long value. */
|
|
public static final class LongAtomicFormula extends AtomicFormula implements Parcelable {
|
|
private final Long mValue;
|
|
private final @Operator Integer mOperator;
|
|
|
|
/**
|
|
* Constructs an empty {@link LongAtomicFormula}. This should only be used as a base.
|
|
*
|
|
* <p>This formula will always return false.
|
|
*
|
|
* @throws IllegalArgumentException if {@code key} cannot be used with long value
|
|
*/
|
|
public LongAtomicFormula(@Key int key) {
|
|
super(key);
|
|
checkArgument(
|
|
key == VERSION_CODE,
|
|
"Key %s cannot be used with LongAtomicFormula", keyToString(key));
|
|
mValue = null;
|
|
mOperator = null;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new {@link LongAtomicFormula}.
|
|
*
|
|
* <p>This formula will hold if and only if the corresponding information of an install
|
|
* specified by {@code key} is of the correct relationship to {@code value} as specified by
|
|
* {@code operator}.
|
|
*
|
|
* @throws IllegalArgumentException if {@code key} cannot be used with long value
|
|
*/
|
|
public LongAtomicFormula(@Key int key, @Operator int operator, long value) {
|
|
super(key);
|
|
checkArgument(
|
|
key == VERSION_CODE,
|
|
"Key %s cannot be used with LongAtomicFormula", keyToString(key));
|
|
checkArgument(
|
|
isValidOperator(operator), "Unknown operator: %d", operator);
|
|
mOperator = operator;
|
|
mValue = value;
|
|
}
|
|
|
|
LongAtomicFormula(Parcel in) {
|
|
super(in.readInt());
|
|
mValue = in.readLong();
|
|
mOperator = in.readInt();
|
|
}
|
|
|
|
@NonNull
|
|
public static final Creator<LongAtomicFormula> CREATOR =
|
|
new Creator<LongAtomicFormula>() {
|
|
@Override
|
|
public LongAtomicFormula createFromParcel(Parcel in) {
|
|
return new LongAtomicFormula(in);
|
|
}
|
|
|
|
@Override
|
|
public LongAtomicFormula[] newArray(int size) {
|
|
return new LongAtomicFormula[size];
|
|
}
|
|
};
|
|
|
|
@Override
|
|
public int getTag() {
|
|
return IntegrityFormula.LONG_ATOMIC_FORMULA_TAG;
|
|
}
|
|
|
|
@Override
|
|
public boolean matches(AppInstallMetadata appInstallMetadata) {
|
|
if (mValue == null || mOperator == null) {
|
|
return false;
|
|
}
|
|
|
|
long metadataValue = getLongMetadataValue(appInstallMetadata, getKey());
|
|
switch (mOperator) {
|
|
case EQ:
|
|
return metadataValue == mValue;
|
|
case GT:
|
|
return metadataValue > mValue;
|
|
case GTE:
|
|
return metadataValue >= mValue;
|
|
default:
|
|
throw new IllegalArgumentException(
|
|
String.format("Unexpected operator %d", mOperator));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isAppCertificateFormula() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isAppCertificateLineageFormula() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isInstallerFormula() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
if (mValue == null || mOperator == null) {
|
|
return String.format("(%s)", keyToString(getKey()));
|
|
}
|
|
return String.format(
|
|
"(%s %s %s)", keyToString(getKey()), operatorToString(mOperator), mValue);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(@Nullable Object o) {
|
|
if (this == o) {
|
|
return true;
|
|
}
|
|
if (o == null || getClass() != o.getClass()) {
|
|
return false;
|
|
}
|
|
LongAtomicFormula that = (LongAtomicFormula) o;
|
|
return getKey() == that.getKey()
|
|
&& Objects.equals(mValue, that.mValue)
|
|
&& Objects.equals(mOperator, that.mOperator);
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(getKey(), mOperator, mValue);
|
|
}
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
|
if (mValue == null || mOperator == null) {
|
|
throw new IllegalStateException("Cannot write an empty LongAtomicFormula.");
|
|
}
|
|
dest.writeInt(getKey());
|
|
dest.writeLong(mValue);
|
|
dest.writeInt(mOperator);
|
|
}
|
|
|
|
public Long getValue() {
|
|
return mValue;
|
|
}
|
|
|
|
public Integer getOperator() {
|
|
return mOperator;
|
|
}
|
|
|
|
private static boolean isValidOperator(int operator) {
|
|
return operator == EQ || operator == GT || operator == GTE;
|
|
}
|
|
|
|
private static long getLongMetadataValue(AppInstallMetadata appInstallMetadata, int key) {
|
|
switch (key) {
|
|
case AtomicFormula.VERSION_CODE:
|
|
return appInstallMetadata.getVersionCode();
|
|
default:
|
|
throw new IllegalStateException("Unexpected key in IntAtomicFormula" + key);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** An {@link AtomicFormula} with a key and string value. */
|
|
public static final class StringAtomicFormula extends AtomicFormula implements Parcelable {
|
|
private final String mValue;
|
|
// Indicates whether the value is the actual value or the hashed value.
|
|
private final Boolean mIsHashedValue;
|
|
|
|
/**
|
|
* Constructs an empty {@link StringAtomicFormula}. This should only be used as a base.
|
|
*
|
|
* <p>An empty formula will always match to false.
|
|
*
|
|
* @throws IllegalArgumentException if {@code key} cannot be used with string value
|
|
*/
|
|
public StringAtomicFormula(@Key int key) {
|
|
super(key);
|
|
checkArgument(
|
|
key == PACKAGE_NAME
|
|
|| key == APP_CERTIFICATE
|
|
|| key == INSTALLER_CERTIFICATE
|
|
|| key == INSTALLER_NAME
|
|
|| key == STAMP_CERTIFICATE_HASH
|
|
|| key == APP_CERTIFICATE_LINEAGE,
|
|
"Key %s cannot be used with StringAtomicFormula", keyToString(key));
|
|
mValue = null;
|
|
mIsHashedValue = null;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new {@link StringAtomicFormula}.
|
|
*
|
|
* <p>This formula will hold if and only if the corresponding information of an install
|
|
* specified by {@code key} equals {@code value}.
|
|
*
|
|
* @throws IllegalArgumentException if {@code key} cannot be used with string value
|
|
*/
|
|
public StringAtomicFormula(@Key int key, @NonNull String value, boolean isHashed) {
|
|
super(key);
|
|
checkArgument(
|
|
key == PACKAGE_NAME
|
|
|| key == APP_CERTIFICATE
|
|
|| key == INSTALLER_CERTIFICATE
|
|
|| key == INSTALLER_NAME
|
|
|| key == STAMP_CERTIFICATE_HASH
|
|
|| key == APP_CERTIFICATE_LINEAGE,
|
|
"Key %s cannot be used with StringAtomicFormula", keyToString(key));
|
|
mValue = value;
|
|
mIsHashedValue = isHashed;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new {@link StringAtomicFormula} together with handling the necessary hashing
|
|
* for the given key.
|
|
*
|
|
* <p>The value will be automatically hashed with SHA256 and the hex digest will be computed
|
|
* when the key is PACKAGE_NAME or INSTALLER_NAME and the value is more than 32 characters.
|
|
*
|
|
* <p>The APP_CERTIFICATES, INSTALLER_CERTIFICATES, STAMP_CERTIFICATE_HASH and
|
|
* APP_CERTIFICATE_LINEAGE are always delivered in hashed form. So the isHashedValue is set
|
|
* to true by default.
|
|
*
|
|
* @throws IllegalArgumentException if {@code key} cannot be used with string value.
|
|
*/
|
|
public StringAtomicFormula(@Key int key, @NonNull String value) {
|
|
super(key);
|
|
checkArgument(
|
|
key == PACKAGE_NAME
|
|
|| key == APP_CERTIFICATE
|
|
|| key == INSTALLER_CERTIFICATE
|
|
|| key == INSTALLER_NAME
|
|
|| key == STAMP_CERTIFICATE_HASH
|
|
|| key == APP_CERTIFICATE_LINEAGE,
|
|
"Key %s cannot be used with StringAtomicFormula", keyToString(key));
|
|
mValue = hashValue(key, value);
|
|
mIsHashedValue =
|
|
(key == APP_CERTIFICATE
|
|
|| key == INSTALLER_CERTIFICATE
|
|
|| key == STAMP_CERTIFICATE_HASH
|
|
|| key == APP_CERTIFICATE_LINEAGE)
|
|
|| !mValue.equals(value);
|
|
}
|
|
|
|
StringAtomicFormula(Parcel in) {
|
|
super(in.readInt());
|
|
mValue = in.readStringNoHelper();
|
|
mIsHashedValue = in.readByte() != 0;
|
|
}
|
|
|
|
@NonNull
|
|
public static final Creator<StringAtomicFormula> CREATOR =
|
|
new Creator<StringAtomicFormula>() {
|
|
@Override
|
|
public StringAtomicFormula createFromParcel(Parcel in) {
|
|
return new StringAtomicFormula(in);
|
|
}
|
|
|
|
@Override
|
|
public StringAtomicFormula[] newArray(int size) {
|
|
return new StringAtomicFormula[size];
|
|
}
|
|
};
|
|
|
|
@Override
|
|
public int getTag() {
|
|
return IntegrityFormula.STRING_ATOMIC_FORMULA_TAG;
|
|
}
|
|
|
|
@Override
|
|
public boolean matches(AppInstallMetadata appInstallMetadata) {
|
|
if (mValue == null || mIsHashedValue == null) {
|
|
return false;
|
|
}
|
|
return getMetadataValue(appInstallMetadata, getKey()).contains(mValue);
|
|
}
|
|
|
|
@Override
|
|
public boolean isAppCertificateFormula() {
|
|
return getKey() == APP_CERTIFICATE;
|
|
}
|
|
|
|
@Override
|
|
public boolean isAppCertificateLineageFormula() {
|
|
return getKey() == APP_CERTIFICATE_LINEAGE;
|
|
}
|
|
|
|
@Override
|
|
public boolean isInstallerFormula() {
|
|
return getKey() == INSTALLER_NAME || getKey() == INSTALLER_CERTIFICATE;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
if (mValue == null || mIsHashedValue == null) {
|
|
return String.format("(%s)", keyToString(getKey()));
|
|
}
|
|
return String.format("(%s %s %s)", keyToString(getKey()), operatorToString(EQ), mValue);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(@Nullable Object o) {
|
|
if (this == o) {
|
|
return true;
|
|
}
|
|
if (o == null || getClass() != o.getClass()) {
|
|
return false;
|
|
}
|
|
StringAtomicFormula that = (StringAtomicFormula) o;
|
|
return getKey() == that.getKey() && Objects.equals(mValue, that.mValue);
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(getKey(), mValue);
|
|
}
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
|
if (mValue == null || mIsHashedValue == null) {
|
|
throw new IllegalStateException("Cannot write an empty StringAtomicFormula.");
|
|
}
|
|
dest.writeInt(getKey());
|
|
dest.writeStringNoHelper(mValue);
|
|
dest.writeByte((byte) (mIsHashedValue ? 1 : 0));
|
|
}
|
|
|
|
public String getValue() {
|
|
return mValue;
|
|
}
|
|
|
|
public Boolean getIsHashedValue() {
|
|
return mIsHashedValue;
|
|
}
|
|
|
|
private static List<String> getMetadataValue(
|
|
AppInstallMetadata appInstallMetadata, int key) {
|
|
switch (key) {
|
|
case AtomicFormula.PACKAGE_NAME:
|
|
return Collections.singletonList(appInstallMetadata.getPackageName());
|
|
case AtomicFormula.APP_CERTIFICATE:
|
|
return appInstallMetadata.getAppCertificates();
|
|
case AtomicFormula.INSTALLER_CERTIFICATE:
|
|
return appInstallMetadata.getInstallerCertificates();
|
|
case AtomicFormula.INSTALLER_NAME:
|
|
return Collections.singletonList(appInstallMetadata.getInstallerName());
|
|
case AtomicFormula.STAMP_CERTIFICATE_HASH:
|
|
return Collections.singletonList(appInstallMetadata.getStampCertificateHash());
|
|
case AtomicFormula.APP_CERTIFICATE_LINEAGE:
|
|
return appInstallMetadata.getAppCertificateLineage();
|
|
default:
|
|
throw new IllegalStateException(
|
|
"Unexpected key in StringAtomicFormula: " + key);
|
|
}
|
|
}
|
|
|
|
private static String hashValue(@Key int key, String value) {
|
|
// Hash the string value if it is a PACKAGE_NAME or INSTALLER_NAME and the value is
|
|
// greater than 32 characters.
|
|
if (value.length() > 32) {
|
|
if (key == PACKAGE_NAME || key == INSTALLER_NAME) {
|
|
return hash(value);
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
private static String hash(String value) {
|
|
try {
|
|
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
|
|
byte[] hashBytes = messageDigest.digest(value.getBytes(StandardCharsets.UTF_8));
|
|
return IntegrityUtils.getHexDigest(hashBytes);
|
|
} catch (NoSuchAlgorithmException e) {
|
|
throw new RuntimeException("SHA-256 algorithm not found", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** An {@link AtomicFormula} with a key and boolean value. */
|
|
public static final class BooleanAtomicFormula extends AtomicFormula implements Parcelable {
|
|
private final Boolean mValue;
|
|
|
|
/**
|
|
* Constructs an empty {@link BooleanAtomicFormula}. This should only be used as a base.
|
|
*
|
|
* <p>An empty formula will always match to false.
|
|
*
|
|
* @throws IllegalArgumentException if {@code key} cannot be used with boolean value
|
|
*/
|
|
public BooleanAtomicFormula(@Key int key) {
|
|
super(key);
|
|
checkArgument(
|
|
key == PRE_INSTALLED || key == STAMP_TRUSTED,
|
|
String.format(
|
|
"Key %s cannot be used with BooleanAtomicFormula", keyToString(key)));
|
|
mValue = null;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new {@link BooleanAtomicFormula}.
|
|
*
|
|
* <p>This formula will hold if and only if the corresponding information of an install
|
|
* specified by {@code key} equals {@code value}.
|
|
*
|
|
* @throws IllegalArgumentException if {@code key} cannot be used with boolean value
|
|
*/
|
|
public BooleanAtomicFormula(@Key int key, boolean value) {
|
|
super(key);
|
|
checkArgument(
|
|
key == PRE_INSTALLED || key == STAMP_TRUSTED,
|
|
String.format(
|
|
"Key %s cannot be used with BooleanAtomicFormula", keyToString(key)));
|
|
mValue = value;
|
|
}
|
|
|
|
BooleanAtomicFormula(Parcel in) {
|
|
super(in.readInt());
|
|
mValue = in.readByte() != 0;
|
|
}
|
|
|
|
@NonNull
|
|
public static final Creator<BooleanAtomicFormula> CREATOR =
|
|
new Creator<BooleanAtomicFormula>() {
|
|
@Override
|
|
public BooleanAtomicFormula createFromParcel(Parcel in) {
|
|
return new BooleanAtomicFormula(in);
|
|
}
|
|
|
|
@Override
|
|
public BooleanAtomicFormula[] newArray(int size) {
|
|
return new BooleanAtomicFormula[size];
|
|
}
|
|
};
|
|
|
|
@Override
|
|
public int getTag() {
|
|
return IntegrityFormula.BOOLEAN_ATOMIC_FORMULA_TAG;
|
|
}
|
|
|
|
@Override
|
|
public boolean matches(AppInstallMetadata appInstallMetadata) {
|
|
if (mValue == null) {
|
|
return false;
|
|
}
|
|
return getBooleanMetadataValue(appInstallMetadata, getKey()) == mValue;
|
|
}
|
|
|
|
@Override
|
|
public boolean isAppCertificateFormula() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isAppCertificateLineageFormula() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isInstallerFormula() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
if (mValue == null) {
|
|
return String.format("(%s)", keyToString(getKey()));
|
|
}
|
|
return String.format("(%s %s %s)", keyToString(getKey()), operatorToString(EQ), mValue);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(@Nullable Object o) {
|
|
if (this == o) {
|
|
return true;
|
|
}
|
|
if (o == null || getClass() != o.getClass()) {
|
|
return false;
|
|
}
|
|
BooleanAtomicFormula that = (BooleanAtomicFormula) o;
|
|
return getKey() == that.getKey() && Objects.equals(mValue, that.mValue);
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(getKey(), mValue);
|
|
}
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
|
if (mValue == null) {
|
|
throw new IllegalStateException("Cannot write an empty BooleanAtomicFormula.");
|
|
}
|
|
dest.writeInt(getKey());
|
|
dest.writeByte((byte) (mValue ? 1 : 0));
|
|
}
|
|
|
|
public Boolean getValue() {
|
|
return mValue;
|
|
}
|
|
|
|
private static boolean getBooleanMetadataValue(
|
|
AppInstallMetadata appInstallMetadata, int key) {
|
|
switch (key) {
|
|
case AtomicFormula.PRE_INSTALLED:
|
|
return appInstallMetadata.isPreInstalled();
|
|
case AtomicFormula.STAMP_TRUSTED:
|
|
return appInstallMetadata.isStampTrusted();
|
|
default:
|
|
throw new IllegalStateException(
|
|
"Unexpected key in BooleanAtomicFormula: " + key);
|
|
}
|
|
}
|
|
}
|
|
|
|
public int getKey() {
|
|
return mKey;
|
|
}
|
|
|
|
static String keyToString(int key) {
|
|
switch (key) {
|
|
case PACKAGE_NAME:
|
|
return "PACKAGE_NAME";
|
|
case APP_CERTIFICATE:
|
|
return "APP_CERTIFICATE";
|
|
case VERSION_CODE:
|
|
return "VERSION_CODE";
|
|
case INSTALLER_NAME:
|
|
return "INSTALLER_NAME";
|
|
case INSTALLER_CERTIFICATE:
|
|
return "INSTALLER_CERTIFICATE";
|
|
case PRE_INSTALLED:
|
|
return "PRE_INSTALLED";
|
|
case STAMP_TRUSTED:
|
|
return "STAMP_TRUSTED";
|
|
case STAMP_CERTIFICATE_HASH:
|
|
return "STAMP_CERTIFICATE_HASH";
|
|
case APP_CERTIFICATE_LINEAGE:
|
|
return "APP_CERTIFICATE_LINEAGE";
|
|
default:
|
|
throw new IllegalArgumentException("Unknown key " + key);
|
|
}
|
|
}
|
|
|
|
static String operatorToString(int op) {
|
|
switch (op) {
|
|
case EQ:
|
|
return "EQ";
|
|
case GT:
|
|
return "GT";
|
|
case GTE:
|
|
return "GTE";
|
|
default:
|
|
throw new IllegalArgumentException("Unknown operator " + op);
|
|
}
|
|
}
|
|
|
|
private static boolean isValidKey(int key) {
|
|
return key == PACKAGE_NAME
|
|
|| key == APP_CERTIFICATE
|
|
|| key == VERSION_CODE
|
|
|| key == INSTALLER_NAME
|
|
|| key == INSTALLER_CERTIFICATE
|
|
|| key == PRE_INSTALLED
|
|
|| key == STAMP_TRUSTED
|
|
|| key == STAMP_CERTIFICATE_HASH
|
|
|| key == APP_CERTIFICATE_LINEAGE;
|
|
}
|
|
}
|