summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2019-11-11 14:41:48 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2019-11-11 14:41:48 +0000
commit379f81b2e98a38c445b8a259b8ce059fc4c40ba9 (patch)
tree5f509105d015f5f0fcdfb47ef33d5514791d1059
parent5fdaa0c9af03cf5199bc6d6bb630d4c18f5bce05 (diff)
parent097f65d6d33e76955ef9698aeedccd5a9c5352ce (diff)
Merge "Prepare rule / formula classes to be ready for external API."
-rw-r--r--services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java18
-rw-r--r--services/core/java/com/android/server/integrity/engine/RuleEvaluator.java87
-rw-r--r--services/core/java/com/android/server/integrity/model/AppInstallMetadata.java39
-rw-r--r--services/core/java/com/android/server/integrity/model/AtomicFormula.java512
-rw-r--r--services/core/java/com/android/server/integrity/model/Formula.java77
-rw-r--r--services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java19
-rw-r--r--services/core/java/com/android/server/integrity/model/OpenFormula.java157
-rw-r--r--services/core/java/com/android/server/integrity/model/Rule.java99
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java247
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/model/AtomicFormulaTest.java240
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java183
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java66
12 files changed, 1265 insertions, 479 deletions
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
index e90612e54105..f0bb192e3b61 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
@@ -16,8 +16,6 @@
package com.android.server.integrity.engine;
-import android.util.Slog;
-
import com.android.server.integrity.model.AppInstallMetadata;
import com.android.server.integrity.model.IntegrityCheckResult;
import com.android.server.integrity.model.Rule;
@@ -53,23 +51,11 @@ public final class RuleEvaluationEngine {
*
* @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules
* against.
- * @return A rule matching the metadata. If there are multiple matching rules, returns any. If
- * no rules are matching, returns {@link Rule#EMPTY}.
+ * @return result of the integrity check
*/
public IntegrityCheckResult evaluate(AppInstallMetadata appInstallMetadata) {
List<Rule> rules = loadRules(appInstallMetadata);
- Rule matchedRule = RuleEvaluator.evaluateRules(rules, appInstallMetadata);
- if (matchedRule == Rule.EMPTY) {
- return IntegrityCheckResult.allow();
- } else {
- switch (matchedRule.getEffect()) {
- case DENY:
- return IntegrityCheckResult.deny(matchedRule);
- default:
- Slog.e(TAG, "Matched a non-DENY rule: " + matchedRule);
- return IntegrityCheckResult.allow();
- }
- }
+ return RuleEvaluator.evaluateRules(rules, appInstallMetadata);
}
private List<Rule> loadRules(AppInstallMetadata appInstallMetadata) {
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
index 641650557f4c..7deae466ac3f 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
@@ -16,14 +16,20 @@
package com.android.server.integrity.engine;
+import static com.android.server.integrity.model.Rule.DENY;
+import static com.android.server.integrity.model.Rule.FORCE_ALLOW;
+
+import android.annotation.NonNull;
import android.util.Slog;
import com.android.server.integrity.model.AppInstallMetadata;
import com.android.server.integrity.model.AtomicFormula;
import com.android.server.integrity.model.Formula;
+import com.android.server.integrity.model.IntegrityCheckResult;
import com.android.server.integrity.model.OpenFormula;
import com.android.server.integrity.model.Rule;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -40,67 +46,40 @@ final class RuleEvaluator {
* <p>Rules must be in disjunctive normal form (DNF). A rule should contain AND'ed formulas
* only. All rules are OR'ed together by default.
*
- * @param rules The list of rules to evaluate.
+ * @param rules The list of rules to evaluate.
* @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules
- * against.
- * @return A rule matching the metadata. If there are multiple matching rules, returns any. If
- * no rules are matching, returns {@link Rule#EMPTY}.
+ * against.
+ * @return result of the integrity check
*/
- static Rule evaluateRules(List<Rule> rules, AppInstallMetadata appInstallMetadata) {
+ @NonNull
+ static IntegrityCheckResult evaluateRules(
+ List<Rule> rules, AppInstallMetadata appInstallMetadata) {
+ List<Rule> matchedRules = new ArrayList<>();
for (Rule rule : rules) {
- if (isConjunctionOfFormulas(rule.getFormula()) && isMatch(rule, appInstallMetadata)) {
- return rule;
+ if (isConjunctionOfFormulas(rule.getFormula())
+ && rule.getFormula().isSatisfied(appInstallMetadata)) {
+ matchedRules.add(rule);
}
}
- return Rule.EMPTY;
- }
-
- /**
- * Match a rule against app install metadata.
- */
- private static boolean isMatch(Rule rule, AppInstallMetadata appInstallMetadata) {
- return isMatch(rule.getFormula(), appInstallMetadata);
- }
- private static boolean isMatch(Formula formula, AppInstallMetadata appInstallMetadata) {
- if (formula instanceof AtomicFormula) {
- AtomicFormula atomicFormula = (AtomicFormula) formula;
- switch (atomicFormula.getKey()) {
- case PACKAGE_NAME:
- return atomicFormula.isMatch(appInstallMetadata.getPackageName());
- case APP_CERTIFICATE:
- return atomicFormula.isMatch(appInstallMetadata.getAppCertificate());
- case INSTALLER_NAME:
- return atomicFormula.isMatch(appInstallMetadata.getInstallerName());
- case INSTALLER_CERTIFICATE:
- return atomicFormula.isMatch(appInstallMetadata.getInstallerCertificate());
- case VERSION_CODE:
- return atomicFormula.isMatch(appInstallMetadata.getVersionCode());
- case PRE_INSTALLED:
- return atomicFormula.isMatch(appInstallMetadata.isPreInstalled());
+ boolean denied = false;
+ Rule denyRule = null;
+ for (Rule rule : matchedRules) {
+ switch (rule.getEffect()) {
+ case DENY:
+ if (!denied) {
+ denied = true;
+ denyRule = rule;
+ }
+ break;
+ case FORCE_ALLOW:
+ return IntegrityCheckResult.allow(rule);
default:
- Slog.i(TAG, String.format("Returned no match for unknown key %s",
- atomicFormula.getKey()));
- return false;
- }
- } else if (formula instanceof OpenFormula) {
- OpenFormula openFormula = (OpenFormula) formula;
- // A rule is in disjunctive normal form, so there are no OR connectors.
- switch (openFormula.getConnector()) {
- case NOT:
- // NOT connector has only 1 formula attached.
- return !isMatch(openFormula.getFormulas().get(0), appInstallMetadata);
- case AND:
- return openFormula.getFormulas().stream().allMatch(
- subFormula -> isMatch(subFormula, appInstallMetadata));
- default:
- Slog.i(TAG, String.format("Returned no match for unknown connector %s",
- openFormula.getConnector()));
- return false;
+ Slog.e(TAG, "Matched an unknown effect rule: " + rule);
+ return IntegrityCheckResult.allow();
}
}
-
- return false;
+ return denied ? IntegrityCheckResult.deny(denyRule) : IntegrityCheckResult.allow();
}
private static boolean isConjunctionOfFormulas(Formula formula) {
@@ -111,7 +90,7 @@ final class RuleEvaluator {
return true;
}
OpenFormula openFormula = (OpenFormula) formula;
- return openFormula.getConnector() == OpenFormula.Connector.AND
+ return openFormula.getConnector() == OpenFormula.AND
&& openFormula.getFormulas().stream().allMatch(RuleEvaluator::isAtomicFormula);
}
@@ -120,7 +99,7 @@ final class RuleEvaluator {
return true;
}
OpenFormula openFormula = (OpenFormula) formula;
- return openFormula.getConnector() == OpenFormula.Connector.NOT
+ return openFormula.getConnector() == OpenFormula.NOT
&& openFormula.getFormulas().get(0) instanceof AtomicFormula;
}
}
diff --git a/services/core/java/com/android/server/integrity/model/AppInstallMetadata.java b/services/core/java/com/android/server/integrity/model/AppInstallMetadata.java
index 660bd2e0d62e..dfc373bb97dc 100644
--- a/services/core/java/com/android/server/integrity/model/AppInstallMetadata.java
+++ b/services/core/java/com/android/server/integrity/model/AppInstallMetadata.java
@@ -19,7 +19,11 @@ package com.android.server.integrity.model;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.VisibleForTesting;
/**
* The app install metadata.
@@ -28,7 +32,11 @@ import android.annotation.Nullable;
* to the rule evaluation engine to evaluate the metadata against the rules.
*
* <p>Instances of this class are immutable.
+ *
+ * @hide
*/
+@SystemApi
+@VisibleForTesting
public final class AppInstallMetadata {
private final String mPackageName;
// Raw string encoding for the SHA-256 hash of the certificate of the app.
@@ -48,10 +56,12 @@ public final class AppInstallMetadata {
this.mIsPreInstalled = builder.mIsPreInstalled;
}
+ @NonNull
public String getPackageName() {
return mPackageName;
}
+ @NonNull
public String getAppCertificate() {
return mAppCertificate;
}
@@ -66,23 +76,17 @@ public final class AppInstallMetadata {
return mInstallerCertificate;
}
- /**
- * @see AppInstallMetadata.Builder#setVersionCode(int)
- */
+ /** @see AppInstallMetadata.Builder#setVersionCode(int) */
public int getVersionCode() {
return mVersionCode;
}
- /**
- * @see AppInstallMetadata.Builder#setIsPreInstalled(boolean)
- */
+ /** @see AppInstallMetadata.Builder#setIsPreInstalled(boolean) */
public boolean isPreInstalled() {
return mIsPreInstalled;
}
- /**
- * Builder class for constructing {@link AppInstallMetadata} objects.
- */
+ /** Builder class for constructing {@link AppInstallMetadata} objects. */
public static final class Builder {
private String mPackageName;
private String mAppCertificate;
@@ -96,7 +100,8 @@ public final class AppInstallMetadata {
*
* @see AppInstallMetadata#getPackageName()
*/
- public Builder setPackageName(String packageName) {
+ @NonNull
+ public Builder setPackageName(@NonNull String packageName) {
this.mPackageName = checkNotNull(packageName);
return this;
}
@@ -109,7 +114,8 @@ public final class AppInstallMetadata {
*
* @see AppInstallMetadata#getAppCertificate()
*/
- public Builder setAppCertificate(String appCertificate) {
+ @NonNull
+ public Builder setAppCertificate(@NonNull String appCertificate) {
this.mAppCertificate = checkNotNull(appCertificate);
return this;
}
@@ -119,7 +125,8 @@ public final class AppInstallMetadata {
*
* @see AppInstallMetadata#getInstallerName()
*/
- public Builder setInstallerName(String installerName) {
+ @NonNull
+ public Builder setInstallerName(@NonNull String installerName) {
this.mInstallerName = checkNotNull(installerName);
return this;
}
@@ -132,7 +139,8 @@ public final class AppInstallMetadata {
*
* @see AppInstallMetadata#getInstallerCertificate()
*/
- public Builder setInstallerCertificate(String installerCertificate) {
+ @NonNull
+ public Builder setInstallerCertificate(@NonNull String installerCertificate) {
this.mInstallerCertificate = checkNotNull(installerCertificate);
return this;
}
@@ -142,6 +150,7 @@ public final class AppInstallMetadata {
*
* @see AppInstallMetadata#getVersionCode()
*/
+ @NonNull
public Builder setVersionCode(int versionCode) {
this.mVersionCode = versionCode;
return this;
@@ -152,6 +161,7 @@ public final class AppInstallMetadata {
*
* @see AppInstallMetadata#isPreInstalled()
*/
+ @NonNull
public Builder setIsPreInstalled(boolean isPreInstalled) {
this.mIsPreInstalled = isPreInstalled;
return this;
@@ -159,7 +169,10 @@ public final class AppInstallMetadata {
/**
* Build {@link AppInstallMetadata}.
+ *
+ * @throws IllegalArgumentException if package name or app certificate is null
*/
+ @NonNull
public AppInstallMetadata build() {
checkArgument(mPackageName != null);
checkArgument(mAppCertificate != null);
diff --git a/services/core/java/com/android/server/integrity/model/AtomicFormula.java b/services/core/java/com/android/server/integrity/model/AtomicFormula.java
index b9b46e3e1aae..a75752841cbc 100644
--- a/services/core/java/com/android/server/integrity/model/AtomicFormula.java
+++ b/services/core/java/com/android/server/integrity/model/AtomicFormula.java
@@ -17,220 +17,404 @@
package com.android.server.integrity.model;
import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
-import android.annotation.Nullable;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
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
*/
-public final class AtomicFormula extends Formula {
+@SystemApi
+@VisibleForTesting
+public abstract class AtomicFormula implements Formula {
private static final String TAG = "AtomicFormula";
- public enum Key {
- PACKAGE_NAME,
- APP_CERTIFICATE,
- INSTALLER_NAME,
- INSTALLER_CERTIFICATE,
- VERSION_CODE,
- PRE_INSTALLED
- }
+ @IntDef(
+ value = {
+ PACKAGE_NAME,
+ APP_CERTIFICATE,
+ INSTALLER_NAME,
+ INSTALLER_CERTIFICATE,
+ VERSION_CODE,
+ PRE_INSTALLED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Key {}
- public enum Operator {
- EQ,
- LT,
- LE,
- GT,
- GE
- }
+ @IntDef(value = {EQ, LT, LE, GT, GE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Operator {}
- private final Key mKey;
- private final Operator mOperator;
-
- // The value of a key can take either 1 of 3 forms: String, Integer, or Boolean.
- // It cannot have multiple values.
- @Nullable
- private final String mStringValue;
- @Nullable
- private final Integer mIntValue;
- @Nullable
- private final Boolean mBoolValue;
-
- public AtomicFormula(Key key, Operator operator, String stringValue) {
- validateOperator(key, operator);
- checkArgument(
- key == Key.PACKAGE_NAME || key == Key.APP_CERTIFICATE || key == Key.INSTALLER_NAME
- || key == Key.INSTALLER_CERTIFICATE,
- String.format("Key %s cannot have string value", key));
- this.mKey = checkNotNull(key);
- this.mOperator = checkNotNull(operator);
- this.mStringValue = checkNotNull(stringValue);
- this.mIntValue = null;
- this.mBoolValue = null;
- }
+ public static final int PACKAGE_NAME = 0;
+ public static final int APP_CERTIFICATE = 1;
+ public static final int INSTALLER_NAME = 2;
+ public static final int INSTALLER_CERTIFICATE = 3;
+ public static final int VERSION_CODE = 4;
+ public static final int PRE_INSTALLED = 5;
- public AtomicFormula(Key key, Operator operator, Integer intValue) {
- validateOperator(key, operator);
- checkArgument(key == Key.VERSION_CODE,
- String.format("Key %s cannot have integer value", key));
- this.mKey = checkNotNull(key);
- this.mOperator = checkNotNull(operator);
- this.mStringValue = null;
- this.mIntValue = checkNotNull(intValue);
- this.mBoolValue = null;
- }
+ public static final int EQ = 0;
+ public static final int LT = 1;
+ public static final int LE = 2;
+ public static final int GT = 3;
+ public static final int GE = 4;
- public AtomicFormula(Key key, Operator operator, Boolean boolValue) {
- validateOperator(key, operator);
- checkArgument(key == Key.PRE_INSTALLED,
- String.format("Key %s cannot have boolean value", key));
- this.mKey = checkNotNull(key);
- this.mOperator = checkNotNull(operator);
- this.mStringValue = null;
- this.mIntValue = null;
- this.mBoolValue = checkNotNull(boolValue);
- }
+ private final @Key int mKey;
- public Key getKey() {
- return mKey;
+ public AtomicFormula(@Key int key) {
+ mKey = key;
}
- public Operator getOperator() {
- return mOperator;
- }
+ /** An {@link AtomicFormula} with an key and int value. */
+ public static final class IntAtomicFormula extends AtomicFormula implements Parcelable {
+ private final int mValue;
+ private final @Operator int mOperator;
- public String getStringValue() {
- return mStringValue;
- }
+ /**
+ * Constructs a new {@link IntAtomicFormula}.
+ *
+ * <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} is not {@link #VERSION_CODE}
+ */
+ public IntAtomicFormula(@Key int key, @Operator int operator, int value) {
+ super(key);
+ checkArgument(
+ key == VERSION_CODE,
+ String.format("Key %s cannot be used with IntAtomicFormula", keyToString(key)));
+ mOperator = operator;
+ mValue = value;
+ }
- public Integer getIntValue() {
- return mIntValue;
- }
+ IntAtomicFormula(Parcel in) {
+ super(in.readInt());
+ mValue = in.readInt();
+ mOperator = in.readInt();
+ }
- public Boolean getBoolValue() {
- return mBoolValue;
- }
+ @NonNull
+ public static final Creator<IntAtomicFormula> CREATOR =
+ new Creator<IntAtomicFormula>() {
+ @Override
+ public IntAtomicFormula createFromParcel(Parcel in) {
+ return new IntAtomicFormula(in);
+ }
+
+ @Override
+ public IntAtomicFormula[] newArray(int size) {
+ return new IntAtomicFormula[size];
+ }
+ };
- /**
- * Get string representation of the value of the key in the formula.
- *
- * @return string representation of the value of the key.
- */
- public String getValue() {
- if (mStringValue != null) {
- return mStringValue;
+ @Override
+ public boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) {
+ int metadataValue = getMetadataValueByKey(appInstallMetadata);
+ switch (mOperator) {
+ case EQ:
+ return metadataValue == mValue;
+ case LE:
+ return metadataValue <= mValue;
+ case LT:
+ return metadataValue < mValue;
+ case GE:
+ return metadataValue >= mValue;
+ case GT:
+ return metadataValue > mValue;
+ default:
+ Slog.i(TAG, String.format("Unexpected operator %d", mOperator));
+ return false;
+ }
}
- if (mIntValue != null) {
- return mIntValue.toString();
+
+ @Override
+ public String toString() {
+ return String.format(
+ "(%s %s %s)", keyToString(getKey()), operatorToString(mOperator), mValue);
}
- return mBoolValue.toString();
- }
- /**
- * Check if the formula is true when substituting its {@link Key} with the string value.
- *
- * @param value String value to substitute the key with.
- * @return {@code true} if the formula is true, and {@code false} otherwise.
- */
- public boolean isMatch(String value) {
- switch (mOperator) {
- case EQ:
- return mStringValue.equals(value);
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ IntAtomicFormula that = (IntAtomicFormula) o;
+ return getKey() == that.getKey() && mValue == that.mValue;
}
- Slog.i(TAG, String.format("Found operator %s for value %s", mOperator, mStringValue));
- return false;
- }
- /**
- * Check if the formula is true when substituting its {@link Key} with the integer value.
- *
- * @param value Integer value to substitute the key with.
- * @return {@code true} if the formula is true, and {@code false} otherwise.
- */
- public boolean isMatch(int value) {
- switch (mOperator) {
- case EQ:
- return mIntValue == value;
- case LE:
- return mIntValue <= value;
- case LT:
- return mIntValue < value;
- case GE:
- return mIntValue >= value;
- case GT:
- return mIntValue > value;
+ @Override
+ public int hashCode() {
+ return Objects.hash(getKey(), mOperator, mValue);
}
- Slog.i(TAG, String.format("Found operator %s for value %s", mOperator, mIntValue));
- return false;
- }
- /**
- * Check if the formula is true when substituting its {@link Key} with the boolean value.
- *
- * @param value Boolean value to substitute the key with.
- * @return {@code true} if the formula is true, and {@code false} otherwise.
- */
- public boolean isMatch(boolean value) {
- switch (mOperator) {
- case EQ:
- return mBoolValue == value;
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(getKey());
+ dest.writeInt(mValue);
+ dest.writeInt(mOperator);
+ }
+
+ private int getMetadataValueByKey(AppInstallMetadata appInstallMetadata) {
+ switch (getKey()) {
+ case VERSION_CODE:
+ return appInstallMetadata.getVersionCode();
+ default:
+ throw new IllegalStateException(
+ "Unexpected key in IntAtomicFormula" + getKey());
+ }
}
- Slog.i(TAG, String.format("Found operator %s for value %s", mOperator, mBoolValue));
- return false;
}
- @Override
- public String toString() {
- return String.format("%s %s %s", mKey, mOperator, getValue());
+ /** An {@link AtomicFormula} with a key and string value. */
+ public static final class StringAtomicFormula extends AtomicFormula implements Parcelable {
+ private final String mValue;
+
+ /**
+ * 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} is not one of {@link #PACKAGE_NAME},
+ * {@link #APP_CERTIFICATE}, {@link #INSTALLER_NAME} and {@link #INSTALLER_CERTIFICATE}
+ */
+ public StringAtomicFormula(@Key int key, @NonNull String value) {
+ super(key);
+ checkArgument(
+ key == PACKAGE_NAME
+ || key == APP_CERTIFICATE
+ || key == INSTALLER_CERTIFICATE
+ || key == INSTALLER_NAME,
+ String.format(
+ "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
+ mValue = value;
+ }
+
+ StringAtomicFormula(Parcel in) {
+ super(in.readInt());
+ mValue = in.readStringNoHelper();
+ }
+
+ @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 boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) {
+ String metadataValue = getMetadataValueByKey(appInstallMetadata);
+ return metadataValue.equals(mValue);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("(%s %s %s)", keyToString(getKey()), operatorToString(EQ), mValue);
+ }
+
+ @Override
+ public boolean equals(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) {
+ dest.writeInt(getKey());
+ dest.writeStringNoHelper(mValue);
+ }
+
+ private String getMetadataValueByKey(AppInstallMetadata appInstallMetadata) {
+ switch (getKey()) {
+ case PACKAGE_NAME:
+ return appInstallMetadata.getPackageName();
+ case APP_CERTIFICATE:
+ return appInstallMetadata.getAppCertificate();
+ case INSTALLER_CERTIFICATE:
+ return appInstallMetadata.getInstallerCertificate();
+ case INSTALLER_NAME:
+ return appInstallMetadata.getInstallerName();
+ default:
+ throw new IllegalStateException(
+ "Unexpected key in StringAtomicFormula: " + getKey());
+ }
+ }
}
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
+ /** An {@link AtomicFormula} with a key and boolean value. */
+ public static final class BooleanAtomicFormula extends AtomicFormula implements Parcelable {
+ private final boolean mValue;
+
+ /**
+ * 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} is not {@link #PRE_INSTALLED}
+ */
+ public BooleanAtomicFormula(@Key int key, boolean value) {
+ super(key);
+ checkArgument(
+ key == PRE_INSTALLED,
+ 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 boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) {
+ boolean metadataValue = getMetadataValueByKey(appInstallMetadata);
+ return metadataValue == mValue;
}
- if (o == null || getClass() != o.getClass()) {
- return false;
+
+ @Override
+ public String toString() {
+ return String.format("(%s %s %s)", keyToString(getKey()), operatorToString(EQ), mValue);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ BooleanAtomicFormula that = (BooleanAtomicFormula) o;
+ return getKey() == that.getKey() && 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) {
+ dest.writeInt(getKey());
+ dest.writeByte((byte) (mValue ? 1 : 0));
+ }
+
+ private boolean getMetadataValueByKey(AppInstallMetadata appInstallMetadata) {
+ switch (getKey()) {
+ case PRE_INSTALLED:
+ return appInstallMetadata.isPreInstalled();
+ default:
+ throw new IllegalStateException(
+ "Unexpected key in BooleanAtomicFormula: " + getKey());
+ }
}
- AtomicFormula that = (AtomicFormula) o;
- return mKey == that.mKey
- && mOperator == that.mOperator
- && Objects.equals(mStringValue, that.mStringValue)
- && Objects.equals(mIntValue, that.mIntValue)
- && Objects.equals(mBoolValue, that.mBoolValue);
}
- @Override
- public int hashCode() {
- return Objects.hash(mKey, mOperator, mStringValue, mIntValue, mBoolValue);
+ public int getKey() {
+ return mKey;
}
- private void validateOperator(Key key, Operator operator) {
- boolean validOperator;
+ 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:
- validOperator = (operator == Operator.EQ);
- break;
- case VERSION_CODE:
- validOperator = true;
- break;
+ return "PRE_INSTALLED";
default:
- Slog.i(TAG, String.format("Found operator %s for key %s", operator, key));
- validOperator = false;
+ throw new IllegalArgumentException("Unknown key " + key);
}
- if (!validOperator) {
- throw new IllegalArgumentException(
- String.format("Invalid operator %s used for key %s", operator, key));
+ }
+
+ String operatorToString(int op) {
+ switch (op) {
+ case EQ:
+ return "EQ";
+ case LT:
+ return "LT";
+ case LE:
+ return "LE";
+ case GT:
+ return "GT";
+ case GE:
+ return "GE";
+ default:
+ throw new IllegalArgumentException("Unknown operator " + op);
}
}
}
diff --git a/services/core/java/com/android/server/integrity/model/Formula.java b/services/core/java/com/android/server/integrity/model/Formula.java
index 9db445378dce..852ece536f31 100644
--- a/services/core/java/com/android/server/integrity/model/Formula.java
+++ b/services/core/java/com/android/server/integrity/model/Formula.java
@@ -16,9 +16,84 @@
package com.android.server.integrity.model;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.integrity.model.AtomicFormula.BooleanAtomicFormula;
+import com.android.server.integrity.model.AtomicFormula.IntAtomicFormula;
+import com.android.server.integrity.model.AtomicFormula.StringAtomicFormula;
+
/**
* Represents a rule logic/content.
+ *
+ * @hide
*/
-public abstract class Formula {
+@SystemApi
+@VisibleForTesting
+public interface Formula {
+
+ int OPEN_FORMULA_TAG = 0;
+ int STRING_ATOMIC_FORMULA_TAG = 1;
+ int INT_ATOMIC_FORMULA_TAG = 2;
+ int BOOLEAN_ATOMIC_FORMULA_TAG = 3;
+
+ /**
+ * Returns if this formula can be satisfied by substituting the corresponding information of
+ * {@code appInstallMetadata} into the formula.
+ */
+ boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata);
+
+ /**
+ * Write a {@link Formula} to {@link android.os.Parcel}.
+ *
+ * <p>This helper method is needed because non-final class/interface are not allowed to be
+ * {@link Parcelable}.
+ *
+ * @throws IllegalArgumentException if {@link Formula} is not a recognized subclass
+ */
+ static void writeToParcel(@NonNull Formula formula, @NonNull Parcel dest, int flags) {
+ if (formula instanceof OpenFormula) {
+ dest.writeInt(OPEN_FORMULA_TAG);
+ ((OpenFormula) formula).writeToParcel(dest, flags);
+ } else if (formula instanceof StringAtomicFormula) {
+ dest.writeInt(STRING_ATOMIC_FORMULA_TAG);
+ ((StringAtomicFormula) formula).writeToParcel(dest, flags);
+ } else if (formula instanceof IntAtomicFormula) {
+ dest.writeInt(INT_ATOMIC_FORMULA_TAG);
+ ((IntAtomicFormula) formula).writeToParcel(dest, flags);
+ } else if (formula instanceof BooleanAtomicFormula) {
+ dest.writeInt(BOOLEAN_ATOMIC_FORMULA_TAG);
+ ((BooleanAtomicFormula) formula).writeToParcel(dest, flags);
+ } else {
+ throw new IllegalArgumentException("Unrecognized class " + formula.getClass());
+ }
+ }
+ /**
+ * Read a {@link Formula} from a {@link android.os.Parcel}.
+ *
+ * <p>We need this (hacky) helper method because non-final class/interface cannot be {@link
+ * Parcelable} (api lint error).
+ *
+ * @throws IllegalArgumentException if the parcel cannot be parsed
+ */
+ @NonNull
+ static Formula readFromParcel(@NonNull Parcel in) {
+ int tag = in.readInt();
+ switch (tag) {
+ case OPEN_FORMULA_TAG:
+ return OpenFormula.CREATOR.createFromParcel(in);
+ case STRING_ATOMIC_FORMULA_TAG:
+ return StringAtomicFormula.CREATOR.createFromParcel(in);
+ case INT_ATOMIC_FORMULA_TAG:
+ return IntAtomicFormula.CREATOR.createFromParcel(in);
+ case BOOLEAN_ATOMIC_FORMULA_TAG:
+ return BooleanAtomicFormula.CREATOR.createFromParcel(in);
+ default:
+ throw new IllegalArgumentException("Unknown formula tag " + tag);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
index 7aeb0c1b188e..ef0751d329d0 100644
--- a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
+++ b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
@@ -16,6 +16,8 @@
package com.android.server.integrity.model;
+import android.annotation.Nullable;
+
/**
* A class encapsulating the result from the evaluation engine after evaluating rules against app
* install metadata.
@@ -31,9 +33,9 @@ public final class IntegrityCheckResult {
}
private final Effect mEffect;
- private final Rule mRule;
+ @Nullable private final Rule mRule;
- private IntegrityCheckResult(Effect effect, Rule rule) {
+ private IntegrityCheckResult(Effect effect, @Nullable Rule rule) {
this.mEffect = effect;
this.mRule = rule;
}
@@ -49,10 +51,19 @@ public final class IntegrityCheckResult {
/**
* Create an ALLOW evaluation outcome.
*
- * @return An evaluation outcome with ALLOW effect and empty rule.
+ * @return An evaluation outcome with ALLOW effect and no rule.
*/
public static IntegrityCheckResult allow() {
- return new IntegrityCheckResult(Effect.ALLOW, Rule.EMPTY);
+ return new IntegrityCheckResult(Effect.ALLOW, null);
+ }
+
+ /**
+ * Create an ALLOW evaluation outcome.
+ *
+ * @return An evaluation outcome with ALLOW effect and rule causing that effect.
+ */
+ public static IntegrityCheckResult allow(Rule rule) {
+ return new IntegrityCheckResult(Effect.ALLOW, rule);
}
/**
diff --git a/services/core/java/com/android/server/integrity/model/OpenFormula.java b/services/core/java/com/android/server/integrity/model/OpenFormula.java
index 21da629fba2e..f29706adeebb 100644
--- a/services/core/java/com/android/server/integrity/model/OpenFormula.java
+++ b/services/core/java/com/android/server/integrity/model/OpenFormula.java
@@ -17,8 +17,19 @@
package com.android.server.integrity.model;
import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -27,40 +38,108 @@ import java.util.Objects;
* Represents a complex formula consisting of other simple and complex formulas.
*
* <p>Instances of this class are immutable.
+ *
+ * @hide
*/
-public final class OpenFormula extends Formula {
+@SystemApi
+@VisibleForTesting
+public final class OpenFormula implements Formula, Parcelable {
+ private static final String TAG = "OpenFormula";
- public enum Connector {
- AND,
- OR,
- NOT
- }
+ @IntDef(
+ value = {
+ AND, OR, NOT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Connector {}
+
+ /** Boolean AND operator. */
+ public static final int AND = 0;
- private final Connector mConnector;
+ /** Boolean OR operator. */
+ public static final int OR = 1;
+
+ /** Boolean NOT operator. */
+ public static final int NOT = 2;
+
+ private final @Connector int mConnector;
private final List<Formula> mFormulas;
- public OpenFormula(Connector connector, List<Formula> formulas) {
+ @NonNull
+ public static final Creator<OpenFormula> CREATOR =
+ new Creator<OpenFormula>() {
+ @Override
+ public OpenFormula createFromParcel(Parcel in) {
+ return new OpenFormula(in);
+ }
+
+ @Override
+ public OpenFormula[] newArray(int size) {
+ return new OpenFormula[size];
+ }
+ };
+
+ /**
+ * Create a new formula from operator and operands.
+ *
+ * @throws IllegalArgumentException if the number of operands is not matching the requirements
+ * for that operator (at least 2 for {@link #AND} and {@link #OR}, 1 for {@link #NOT}).
+ */
+ public OpenFormula(@Connector int connector, @NonNull List<Formula> formulas) {
validateFormulas(connector, formulas);
- this.mConnector = checkNotNull(connector);
- this.mFormulas = Collections.unmodifiableList(checkNotNull(formulas));
+ this.mConnector = connector;
+ this.mFormulas = Collections.unmodifiableList(formulas);
}
- public Connector getConnector() {
+ OpenFormula(Parcel in) {
+ mConnector = in.readInt();
+ int length = in.readInt();
+ checkArgument(length >= 0, "Must have non-negative length. Got " + length);
+ mFormulas = new ArrayList<>(length);
+ for (int i = 0; i < length; i++) {
+ mFormulas.add(Formula.readFromParcel(in));
+ }
+ }
+
+ public @Connector int getConnector() {
return mConnector;
}
+ @NonNull
public List<Formula> getFormulas() {
return mFormulas;
}
@Override
+ public boolean isSatisfied(@NonNull AppInstallMetadata appInstallMetadata) {
+ switch (mConnector) {
+ case NOT:
+ return !mFormulas.get(0).isSatisfied(appInstallMetadata);
+ case AND:
+ return mFormulas.stream()
+ .allMatch(formula -> formula.isSatisfied(appInstallMetadata));
+ case OR:
+ return mFormulas.stream()
+ .anyMatch(formula -> formula.isSatisfied(appInstallMetadata));
+ default:
+ Slog.i(TAG, "Unknown connector " + mConnector);
+ return false;
+ }
+ }
+
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder();
- for (int i = 0; i < mFormulas.size(); i++) {
- if (i > 0) {
- sb.append(String.format(" %s ", mConnector));
+ if (mFormulas.size() == 1) {
+ sb.append(String.format("%s ", connectorToString(mConnector)));
+ sb.append(mFormulas.get(0).toString());
+ } else {
+ for (int i = 0; i < mFormulas.size(); i++) {
+ if (i > 0) {
+ sb.append(String.format(" %s ", connectorToString(mConnector)));
+ }
+ sb.append(mFormulas.get(i).toString());
}
- sb.append(mFormulas.get(i).toString());
}
return sb.toString();
}
@@ -74,8 +153,7 @@ public final class OpenFormula extends Formula {
return false;
}
OpenFormula that = (OpenFormula) o;
- return mConnector == that.mConnector
- && mFormulas.equals(that.mFormulas);
+ return mConnector == that.mConnector && mFormulas.equals(that.mFormulas);
}
@Override
@@ -83,17 +161,50 @@ public final class OpenFormula extends Formula {
return Objects.hash(mConnector, mFormulas);
}
- private void validateFormulas(Connector connector, List<Formula> formulas) {
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mConnector);
+ dest.writeInt(mFormulas.size());
+ for (Formula formula : mFormulas) {
+ Formula.writeToParcel(formula, dest, flags);
+ }
+ }
+
+ private void validateFormulas(@Connector int connector, List<Formula> formulas) {
switch (connector) {
case AND:
case OR:
- checkArgument(formulas.size() >= 2,
- String.format("Connector %s must have at least 2 formulas", connector));
+ checkArgument(
+ formulas.size() >= 2,
+ String.format(
+ "Connector %s must have at least 2 formulas",
+ connectorToString(connector)));
break;
case NOT:
- checkArgument(formulas.size() == 1,
- String.format("Connector %s must have 1 formula only", connector));
+ checkArgument(
+ formulas.size() == 1,
+ String.format(
+ "Connector %s must have 1 formula only",
+ connectorToString(connector)));
break;
}
}
+
+ private String connectorToString(int connector) {
+ switch (connector) {
+ case AND:
+ return "AND";
+ case OR:
+ return "OR";
+ case NOT:
+ return "NOT";
+ default:
+ throw new IllegalArgumentException("Unknown connector " + connector);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/integrity/model/Rule.java b/services/core/java/com/android/server/integrity/model/Rule.java
index 63b9b911ff4f..14dcb26d1025 100644
--- a/services/core/java/com/android/server/integrity/model/Rule.java
+++ b/services/core/java/com/android/server/integrity/model/Rule.java
@@ -18,55 +18,96 @@ package com.android.server.integrity.model;
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+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.util.Objects;
/**
* Represent rules to be used in the rule evaluation engine to match against app installs.
*
* <p>Instances of this class are immutable.
+ *
+ * @hide
*/
-public final class Rule {
+@SystemApi
+@VisibleForTesting
+public final class Rule implements Parcelable {
- public enum Effect {
- DENY
- }
+ @IntDef(
+ value = {
+ DENY,
+ FORCE_ALLOW,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Effect {}
+
+ /** If this rule matches the install, the install should be denied. */
+ public static final int DENY = 0;
- // Holds an empty rule instance.
- public static final Rule EMPTY = new Rule();
+ /**
+ * If this rule matches the install, the install will be allowed regardless of other matched
+ * rules.
+ */
+ public static final int FORCE_ALLOW = 1;
private final Formula mFormula;
- private final Effect mEffect;
+ private final @Effect int mEffect;
- private Rule() {
- this.mFormula = null;
- this.mEffect = null;
+ public Rule(@NonNull Formula formula, @Effect int effect) {
+ this.mFormula = checkNotNull(formula);
+ this.mEffect = effect;
}
- public Rule(Formula formula, Effect effect) {
- this.mFormula = checkNotNull(formula);
- this.mEffect = checkNotNull(effect);
+ Rule(Parcel in) {
+ mFormula = Formula.readFromParcel(in);
+ mEffect = in.readInt();
}
- /**
- * Indicates whether the rule is empty or not.
- *
- * @return {@code true} if the rule is empty, and {@code false} otherwise.
- */
- public boolean isEmpty() {
- return mFormula == null && mEffect == null;
+ @NonNull
+ public static final Creator<Rule> CREATOR =
+ new Creator<Rule>() {
+ @Override
+ public Rule createFromParcel(Parcel in) {
+ return new Rule(in);
+ }
+
+ @Override
+ public Rule[] newArray(int size) {
+ return new Rule[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ Formula.writeToParcel(mFormula, dest, flags);
+ dest.writeInt(mEffect);
}
+ @NonNull
public Formula getFormula() {
return mFormula;
}
- public Effect getEffect() {
+ public @Effect int getEffect() {
return mEffect;
}
@Override
public String toString() {
- return String.format("Rule: %s, %s", mFormula, mEffect);
+ return String.format("Rule: %s, %s", mFormula, effectToString(mEffect));
}
@Override
@@ -78,12 +119,22 @@ public final class Rule {
return false;
}
Rule that = (Rule) o;
- return Objects.equals(mFormula, that.mFormula)
- && Objects.equals(mEffect, that.mEffect);
+ return Objects.equals(mFormula, that.mFormula) && mEffect == that.mEffect;
}
@Override
public int hashCode() {
return Objects.hash(mFormula, mEffect);
}
+
+ private String effectToString(int effect) {
+ switch (effect) {
+ case DENY:
+ return "DENY";
+ case FORCE_ALLOW:
+ return "FORCE_ALLOW";
+ default:
+ throw new IllegalArgumentException("Unknown effect " + effect);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
index baf1ed00c7b5..e52aca36bb05 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
@@ -16,11 +16,15 @@
package com.android.server.integrity.engine;
+import static com.android.server.integrity.model.IntegrityCheckResult.Effect.ALLOW;
+import static com.android.server.integrity.model.IntegrityCheckResult.Effect.DENY;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import com.android.server.integrity.model.AppInstallMetadata;
import com.android.server.integrity.model.AtomicFormula;
+import com.android.server.integrity.model.AtomicFormula.StringAtomicFormula;
+import com.android.server.integrity.model.IntegrityCheckResult;
import com.android.server.integrity.model.OpenFormula;
import com.android.server.integrity.model.Rule;
@@ -43,141 +47,176 @@ public class RuleEvaluatorTest {
new AppInstallMetadata.Builder()
.setPackageName(PACKAGE_NAME_1)
.setAppCertificate(APP_CERTIFICATE)
+ .setVersionCode(2)
.build();
@Test
- public void testMatchRules_emptyRules() {
+ public void testEvaluateRules_noRules_allow() {
List<Rule> rules = new ArrayList<>();
- Rule matchedRule = RuleEvaluator.evaluateRules(rules, APP_INSTALL_METADATA);
+ IntegrityCheckResult result = RuleEvaluator.evaluateRules(rules, APP_INSTALL_METADATA);
- assertEquals(Rule.EMPTY, matchedRule);
+ assertEquals(ALLOW, result.getEffect());
}
@Test
- public void testMatchRules_emptyMatch() {
- Rule rule1 = new Rule(
- new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
- PACKAGE_NAME_2), Rule.Effect.DENY);
+ public void testEvaluateRules_noMatchedRules_allow() {
+ Rule rule1 =
+ new Rule(
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_2),
+ Rule.DENY);
- Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule1),
- APP_INSTALL_METADATA);
+ IntegrityCheckResult result =
+ RuleEvaluator.evaluateRules(Collections.singletonList(rule1), APP_INSTALL_METADATA);
- assertEquals(Rule.EMPTY, matchedRule);
+ assertEquals(ALLOW, result.getEffect());
}
-
@Test
- public void testMatchRules_oneMatch() {
- Rule rule1 = new Rule(
- new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
- PACKAGE_NAME_1), Rule.Effect.DENY);
- Rule rule2 = new Rule(
- new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
- PACKAGE_NAME_2), Rule.Effect.DENY);
-
- Rule matchedRule = RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2),
- APP_INSTALL_METADATA);
-
- assertEquals(rule1, matchedRule);
+ public void testEvaluateRules_oneMatch_deny() {
+ Rule rule1 =
+ new Rule(
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_1),
+ Rule.DENY);
+ Rule rule2 =
+ new Rule(
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_2),
+ Rule.DENY);
+
+ IntegrityCheckResult result =
+ RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);
+
+ assertEquals(DENY, result.getEffect());
+ assertEquals(rule1, result.getRule());
}
@Test
- public void testMatchRules_multipleMatches() {
- Rule rule1 = new Rule(
- new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
- PACKAGE_NAME_1), Rule.Effect.DENY);
- OpenFormula openFormula2 = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
- new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
- PACKAGE_NAME_1),
- new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
- AtomicFormula.Operator.EQ,
- APP_CERTIFICATE)));
- Rule rule2 = new Rule(
- openFormula2, Rule.Effect.DENY);
-
- Rule matchedRule = RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2),
- APP_INSTALL_METADATA);
-
- assertNotEquals(Rule.EMPTY, matchedRule);
+ public void testEvaluateRules_multipleMatches_deny() {
+ Rule rule1 =
+ new Rule(
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_1),
+ Rule.DENY);
+ OpenFormula openFormula2 =
+ new OpenFormula(
+ OpenFormula.AND,
+ Arrays.asList(
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_1),
+ new StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE, APP_CERTIFICATE)));
+ Rule rule2 = new Rule(openFormula2, Rule.DENY);
+
+ IntegrityCheckResult result =
+ RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);
+
+ assertEquals(DENY, result.getEffect());
+ assertEquals(rule1, result.getRule());
}
@Test
- public void testMatchRules_ruleWithNot() {
- OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.NOT,
- Collections.singletonList(
- new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
- PACKAGE_NAME_2)));
- Rule rule = new Rule(openFormula, Rule.Effect.DENY);
-
- Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
- APP_INSTALL_METADATA);
-
- assertEquals(rule, matchedRule);
+ public void testEvaluateRules_ruleWithNot_deny() {
+ OpenFormula openFormula =
+ new OpenFormula(
+ OpenFormula.NOT,
+ Collections.singletonList(
+ new StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_2)));
+ Rule rule = new Rule(openFormula, Rule.DENY);
+
+ IntegrityCheckResult result =
+ RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
+
+ assertEquals(DENY, result.getEffect());
+ assertEquals(rule, result.getRule());
}
@Test
- public void testMatchRules_ruleWithIntegerOperators() {
- Rule rule1 = new Rule(
- new AtomicFormula(AtomicFormula.Key.VERSION_CODE, AtomicFormula.Operator.GT,
- 1), Rule.Effect.DENY);
-
- Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule1),
- APP_INSTALL_METADATA);
+ public void testEvaluateRules_ruleWithIntegerOperators_deny() {
+ Rule rule =
+ new Rule(
+ new AtomicFormula.IntAtomicFormula(
+ AtomicFormula.VERSION_CODE, AtomicFormula.GT, 1),
+ Rule.DENY);
+
+ IntegrityCheckResult result =
+ RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
+
+ assertEquals(DENY, result.getEffect());
+ assertEquals(rule, result.getRule());
+ }
- assertEquals(rule1, matchedRule);
+ @Test
+ public void testEvaluateRules_validForm_deny() {
+ OpenFormula openFormula =
+ new OpenFormula(
+ OpenFormula.AND,
+ Arrays.asList(
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_1),
+ new StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE, APP_CERTIFICATE)));
+ Rule rule = new Rule(openFormula, Rule.DENY);
+
+ IntegrityCheckResult result =
+ RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
+
+ assertEquals(DENY, result.getEffect());
+ assertEquals(rule, result.getRule());
}
@Test
- public void testMatchRules_validForm() {
- OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
- new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
- PACKAGE_NAME_1),
- new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
- AtomicFormula.Operator.EQ,
- APP_CERTIFICATE)));
- Rule rule = new Rule(
- openFormula, Rule.Effect.DENY);
-
- Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
- APP_INSTALL_METADATA);
-
- assertEquals(rule, matchedRule);
+ public void testEvaluateRules_ruleNotInDNF_ignoreAndAllow() {
+ OpenFormula openFormula =
+ new OpenFormula(
+ OpenFormula.OR,
+ Arrays.asList(
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_1),
+ new StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE, APP_CERTIFICATE)));
+ Rule rule = new Rule(openFormula, Rule.DENY);
+
+ IntegrityCheckResult result =
+ RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
+
+ assertEquals(ALLOW, result.getEffect());
}
@Test
- public void testMatchRules_ruleNotInDNF() {
- OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.OR, Arrays.asList(
- new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
- PACKAGE_NAME_1),
- new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
- AtomicFormula.Operator.EQ,
- APP_CERTIFICATE)));
- Rule rule = new Rule(
- openFormula, Rule.Effect.DENY);
-
- Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
- APP_INSTALL_METADATA);
-
- assertEquals(Rule.EMPTY, matchedRule);
+ public void testEvaluateRules_openFormulaWithNot_allow() {
+ OpenFormula openSubFormula =
+ new OpenFormula(
+ OpenFormula.AND,
+ Arrays.asList(
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_2),
+ new StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE, APP_CERTIFICATE)));
+ OpenFormula openFormula =
+ new OpenFormula(OpenFormula.NOT, Collections.singletonList(openSubFormula));
+ Rule rule = new Rule(openFormula, Rule.DENY);
+
+ IntegrityCheckResult result =
+ RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
+
+ assertEquals(ALLOW, result.getEffect());
}
@Test
- public void testMatchRules_openFormulaWithNot() {
- OpenFormula openSubFormula = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
- new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
- PACKAGE_NAME_2),
- new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
- AtomicFormula.Operator.EQ,
- APP_CERTIFICATE)));
- OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.NOT,
- Collections.singletonList(openSubFormula));
- Rule rule = new Rule(
- openFormula, Rule.Effect.DENY);
-
- Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
- APP_INSTALL_METADATA);
-
- assertEquals(Rule.EMPTY, matchedRule);
+ public void testEvaluateRules_forceAllow() {
+ Rule rule1 =
+ new Rule(
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_1),
+ Rule.FORCE_ALLOW);
+ OpenFormula openFormula2 =
+ new OpenFormula(
+ OpenFormula.AND,
+ Arrays.asList(
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME_1),
+ new StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE, APP_CERTIFICATE)));
+ Rule rule2 = new Rule(openFormula2, Rule.DENY);
+
+ IntegrityCheckResult result =
+ RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);
+
+ assertEquals(ALLOW, result.getEffect());
+ assertEquals(rule1, result.getRule());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/AtomicFormulaTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/AtomicFormulaTest.java
index 1cb2fb3517d3..c8c5ecaed43a 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/model/AtomicFormulaTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/AtomicFormulaTest.java
@@ -19,6 +19,14 @@ package com.android.server.integrity.model;
import static com.android.server.testutils.TestUtils.assertExpectException;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+
+import com.android.server.integrity.model.AtomicFormula.BooleanAtomicFormula;
+import com.android.server.integrity.model.AtomicFormula.IntAtomicFormula;
+import com.android.server.integrity.model.AtomicFormula.StringAtomicFormula;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,32 +37,26 @@ public class AtomicFormulaTest {
@Test
public void testValidAtomicFormula_stringValue() {
- AtomicFormula atomicFormula = new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME,
- AtomicFormula.Operator.EQ, "com.test.app");
+ StringAtomicFormula stringAtomicFormula =
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.test.app");
- assertEquals(AtomicFormula.Key.PACKAGE_NAME, atomicFormula.getKey());
- assertEquals(AtomicFormula.Operator.EQ, atomicFormula.getOperator());
- assertEquals("com.test.app", atomicFormula.getStringValue());
+ assertEquals(AtomicFormula.PACKAGE_NAME, stringAtomicFormula.getKey());
}
@Test
public void testValidAtomicFormula_intValue() {
- AtomicFormula atomicFormula = new AtomicFormula(AtomicFormula.Key.VERSION_CODE,
- AtomicFormula.Operator.LE, 1);
+ IntAtomicFormula intAtomicFormula =
+ new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, 1);
- assertEquals(AtomicFormula.Key.VERSION_CODE, atomicFormula.getKey());
- assertEquals(AtomicFormula.Operator.LE, atomicFormula.getOperator());
- assertEquals(1, atomicFormula.getIntValue().intValue());
+ assertEquals(AtomicFormula.VERSION_CODE, intAtomicFormula.getKey());
}
@Test
public void testValidAtomicFormula_boolValue() {
- AtomicFormula atomicFormula = new AtomicFormula(AtomicFormula.Key.PRE_INSTALLED,
- AtomicFormula.Operator.EQ, true);
+ BooleanAtomicFormula atomicFormula =
+ new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
- assertEquals(AtomicFormula.Key.PRE_INSTALLED, atomicFormula.getKey());
- assertEquals(AtomicFormula.Operator.EQ, atomicFormula.getOperator());
- assertEquals(true, atomicFormula.getBoolValue());
+ assertEquals(AtomicFormula.PRE_INSTALLED, atomicFormula.getKey());
}
@Test
@@ -62,9 +64,9 @@ public class AtomicFormulaTest {
assertExpectException(
IllegalArgumentException.class,
/* expectedExceptionMessageRegex */
- String.format("Key %s cannot have string value", AtomicFormula.Key.VERSION_CODE),
- () -> new AtomicFormula(AtomicFormula.Key.VERSION_CODE, AtomicFormula.Operator.EQ,
- "test-value"));
+ String.format(
+ "Key VERSION_CODE cannot be used with StringAtomicFormula"),
+ () -> new StringAtomicFormula(AtomicFormula.VERSION_CODE, "test-value"));
}
@Test
@@ -72,9 +74,9 @@ public class AtomicFormulaTest {
assertExpectException(
IllegalArgumentException.class,
/* expectedExceptionMessageRegex */
- String.format("Key %s cannot have integer value", AtomicFormula.Key.PACKAGE_NAME),
- () -> new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
- 1));
+ String.format(
+ "Key PACKAGE_NAME cannot be used with IntAtomicFormula"),
+ () -> new IntAtomicFormula(AtomicFormula.PACKAGE_NAME, AtomicFormula.EQ, 1));
}
@Test
@@ -82,19 +84,193 @@ public class AtomicFormulaTest {
assertExpectException(
IllegalArgumentException.class,
/* expectedExceptionMessageRegex */
- String.format("Key %s cannot have boolean value", AtomicFormula.Key.PACKAGE_NAME),
- () -> new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
- true));
+ String.format(
+ "Key PACKAGE_NAME cannot be used with BooleanAtomicFormula"),
+ () -> new BooleanAtomicFormula(AtomicFormula.PACKAGE_NAME, true));
}
@Test
- public void testValidateOperator_invalidKeyOperatorPair() {
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex */
- String.format("Invalid operator %s used for key %s",
- AtomicFormula.Operator.LE, AtomicFormula.Key.PACKAGE_NAME),
- () -> new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.LE,
- "test-value"));
+ public void testIsSatisfiable_string_true() {
+ StringAtomicFormula stringAtomicFormula =
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.test.app");
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setPackageName("com.test.app").build();
+
+ assertTrue(stringAtomicFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_string_false() {
+ StringAtomicFormula stringAtomicFormula =
+ new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.test.app");
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setPackageName("com.foo.bar").build();
+
+ assertFalse(stringAtomicFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_int_eq_true() {
+ IntAtomicFormula intAtomicFormula =
+ new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 0);
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setVersionCode(0).build();
+
+ assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_int_eq_false() {
+ IntAtomicFormula intAtomicFormula =
+ new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 0);
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setVersionCode(1).build();
+
+ assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_int_gt_true() {
+ IntAtomicFormula intAtomicFormula =
+ new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, 0);
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setVersionCode(1).build();
+
+ assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_int_gt_false() {
+ IntAtomicFormula intAtomicFormula =
+ new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, 1);
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setVersionCode(0).build();
+
+ assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_int_ge_true() {
+ IntAtomicFormula intAtomicFormula =
+ new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GE, 0);
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setVersionCode(1).build();
+
+ assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_int_ge_false() {
+ IntAtomicFormula intAtomicFormula =
+ new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GE, 1);
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setVersionCode(0).build();
+
+ assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_int_lt_true() {
+ IntAtomicFormula intAtomicFormula =
+ new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LT, 1);
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setVersionCode(0).build();
+
+ assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_int_lt_false() {
+ IntAtomicFormula intAtomicFormula =
+ new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LT, 1);
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setVersionCode(2).build();
+
+ assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_int_le_true() {
+ IntAtomicFormula intAtomicFormula =
+ new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, 1);
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setVersionCode(0).build();
+
+ assertTrue(intAtomicFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_int_le_false() {
+ IntAtomicFormula intAtomicFormula =
+ new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, 1);
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setVersionCode(2).build();
+
+ assertFalse(intAtomicFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_bool_true() {
+ BooleanAtomicFormula boolFormula =
+ new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setIsPreInstalled(true).build();
+
+ assertTrue(boolFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_bool_false() {
+ BooleanAtomicFormula boolFormula =
+ new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setIsPreInstalled(false).build();
+
+ assertFalse(boolFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testParcelUnparcel_string() {
+ StringAtomicFormula formula = new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "abc");
+ Parcel p = Parcel.obtain();
+ formula.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ StringAtomicFormula newFormula = StringAtomicFormula.CREATOR.createFromParcel(p);
+
+ assertEquals(formula, newFormula);
+ }
+
+ @Test
+ public void testParcelUnparcel_int() {
+ IntAtomicFormula formula =
+ new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LT, 1);
+ Parcel p = Parcel.obtain();
+ formula.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ IntAtomicFormula newFormula = IntAtomicFormula.CREATOR.createFromParcel(p);
+
+ assertEquals(formula, newFormula);
+ }
+
+ @Test
+ public void testParcelUnparcel_bool() {
+ BooleanAtomicFormula formula = new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
+ Parcel p = Parcel.obtain();
+ formula.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ BooleanAtomicFormula newFormula = BooleanAtomicFormula.CREATOR.createFromParcel(p);
+
+ assertEquals(formula, newFormula);
+ }
+
+ /** Returns a builder with all fields filled with some dummy data. */
+ private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
+ return new AppInstallMetadata.Builder()
+ .setPackageName("abc")
+ .setAppCertificate("abc")
+ .setInstallerCertificate("abc")
+ .setInstallerName("abc")
+ .setVersionCode(-1)
+ .setIsPreInstalled(true);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java
index 2133a7d3550b..ecabb5276790 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java
@@ -19,6 +19,10 @@ package com.android.server.integrity.model;
import static com.android.server.testutils.TestUtils.assertExpectException;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -30,17 +34,17 @@ import java.util.Collections;
@RunWith(JUnit4.class)
public class OpenFormulaTest {
- private static final AtomicFormula ATOMIC_FORMULA_1 = new AtomicFormula(
- AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ, "test1");
- private static final AtomicFormula ATOMIC_FORMULA_2 = new AtomicFormula(
- AtomicFormula.Key.VERSION_CODE, AtomicFormula.Operator.EQ, 1);
+ private static final AtomicFormula ATOMIC_FORMULA_1 =
+ new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "test1");
+ private static final AtomicFormula ATOMIC_FORMULA_2 =
+ new AtomicFormula.IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1);
@Test
public void testValidOpenFormula() {
- OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND,
- Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
+ OpenFormula openFormula =
+ new OpenFormula(OpenFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
- assertEquals(OpenFormula.Connector.AND, openFormula.getConnector());
+ assertEquals(OpenFormula.AND, openFormula.getConnector());
assertEquals(Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2), openFormula.getFormulas());
}
@@ -49,10 +53,10 @@ public class OpenFormulaTest {
assertExpectException(
IllegalArgumentException.class,
/* expectedExceptionMessageRegex */
- String.format("Connector %s must have at least 2 formulas",
- OpenFormula.Connector.AND),
- () -> new OpenFormula(OpenFormula.Connector.AND,
- Collections.singletonList(ATOMIC_FORMULA_1)));
+ String.format("Connector AND must have at least 2 formulas"),
+ () ->
+ new OpenFormula(
+ OpenFormula.AND, Collections.singletonList(ATOMIC_FORMULA_1)));
}
@Test
@@ -60,8 +64,159 @@ public class OpenFormulaTest {
assertExpectException(
IllegalArgumentException.class,
/* expectedExceptionMessageRegex */
- String.format("Connector %s must have 1 formula only", OpenFormula.Connector.NOT),
- () -> new OpenFormula(OpenFormula.Connector.NOT,
- Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)));
+ String.format("Connector NOT must have 1 formula only"),
+ () ->
+ new OpenFormula(
+ OpenFormula.NOT,
+ Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)));
+ }
+
+ @Test
+ public void testIsSatisfiable_notFalse_true() {
+ OpenFormula openFormula = new OpenFormula(OpenFormula.NOT, Arrays.asList(ATOMIC_FORMULA_1));
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setPackageName("test2").build();
+ // validate assumptions about the metadata
+ assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
+
+ assertTrue(openFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_notTrue_false() {
+ OpenFormula openFormula = new OpenFormula(OpenFormula.NOT, Arrays.asList(ATOMIC_FORMULA_1));
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setPackageName("test1").build();
+ // validate assumptions about the metadata
+ assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
+
+ assertFalse(openFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_trueAndTrue_true() {
+ OpenFormula openFormula =
+ new OpenFormula(OpenFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(1).build();
+ // validate assumptions about the metadata
+ assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
+ assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
+
+ assertTrue(openFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_trueAndFalse_false() {
+ OpenFormula openFormula =
+ new OpenFormula(OpenFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(2).build();
+ // validate assumptions about the metadata
+ assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
+ assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
+
+ assertFalse(openFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_falseAndTrue_false() {
+ OpenFormula openFormula =
+ new OpenFormula(OpenFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(1).build();
+ // validate assumptions about the metadata
+ assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
+ assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
+
+ assertFalse(openFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_falseAndFalse_false() {
+ OpenFormula openFormula =
+ new OpenFormula(OpenFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(2).build();
+ // validate assumptions about the metadata
+ assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
+ assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
+
+ assertFalse(openFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_trueOrTrue_true() {
+ OpenFormula openFormula =
+ new OpenFormula(OpenFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(1).build();
+ // validate assumptions about the metadata
+ assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
+ assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
+
+ assertTrue(openFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_trueOrFalse_true() {
+ OpenFormula openFormula =
+ new OpenFormula(OpenFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setPackageName("test1").setVersionCode(2).build();
+ // validate assumptions about the metadata
+ assertTrue(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
+ assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
+
+ assertTrue(openFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_falseOrTrue_true() {
+ OpenFormula openFormula =
+ new OpenFormula(OpenFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(1).build();
+ // validate assumptions about the metadata
+ assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
+ assertTrue(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
+
+ assertTrue(openFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testIsSatisfiable_falseOrFalse_false() {
+ OpenFormula openFormula =
+ new OpenFormula(OpenFormula.OR, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
+ AppInstallMetadata appInstallMetadata =
+ getAppInstallMetadataBuilder().setPackageName("test2").setVersionCode(2).build();
+ // validate assumptions about the metadata
+ assertFalse(ATOMIC_FORMULA_1.isSatisfied(appInstallMetadata));
+ assertFalse(ATOMIC_FORMULA_2.isSatisfied(appInstallMetadata));
+
+ assertFalse(openFormula.isSatisfied(appInstallMetadata));
+ }
+
+ @Test
+ public void testParcelUnparcel() {
+ OpenFormula formula =
+ new OpenFormula(OpenFormula.AND, Arrays.asList(ATOMIC_FORMULA_2, ATOMIC_FORMULA_1));
+ Parcel p = Parcel.obtain();
+ formula.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ OpenFormula newFormula = OpenFormula.CREATOR.createFromParcel(p);
+
+ assertEquals(formula, newFormula);
+ }
+
+ /** Returns a builder with all fields filled with some dummy data. */
+ private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
+ return new AppInstallMetadata.Builder()
+ .setPackageName("abc")
+ .setAppCertificate("abc")
+ .setInstallerCertificate("abc")
+ .setInstallerName("abc")
+ .setVersionCode(-1)
+ .setIsPreInstalled(true);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java
index 048ee707d8fe..e0c36fdfc546 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java
@@ -20,7 +20,8 @@ import static com.android.server.testutils.TestUtils.assertExpectException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNull;
+
+import android.os.Parcel;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,23 +32,13 @@ import java.util.Arrays;
@RunWith(JUnit4.class)
public class RuleTest {
- private static final Rule.Effect DENY_EFFECT = Rule.Effect.DENY;
+ private static final @Rule.Effect int DENY_EFFECT = Rule.DENY;
private static final String PACKAGE_NAME = "com.test.app";
private static final String APP_CERTIFICATE = "test_cert";
private static final Formula PACKAGE_NAME_ATOMIC_FORMULA =
- new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
- PACKAGE_NAME);
+ new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, PACKAGE_NAME);
private static final Formula APP_CERTIFICATE_ATOMIC_FORMULA =
- new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE, AtomicFormula.Operator.EQ,
- APP_CERTIFICATE);
-
- @Test
- public void testEmptyRule() {
- Rule emptyRule = Rule.EMPTY;
-
- assertNull(emptyRule.getFormula());
- assertNull(emptyRule.getEffect());
- }
+ new AtomicFormula.StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, APP_CERTIFICATE);
@Test
public void testValidRule() {
@@ -58,14 +49,6 @@ public class RuleTest {
}
@Test
- public void testInvalidRule_invalidEffect() {
- assertExpectException(
- NullPointerException.class,
- /* expectedExceptionMessageRegex */ null,
- () -> new Rule(PACKAGE_NAME_ATOMIC_FORMULA, null));
- }
-
- @Test
public void testInvalidRule_invalidFormula() {
assertExpectException(
NullPointerException.class,
@@ -75,14 +58,17 @@ public class RuleTest {
@Test
public void testToString() {
- OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND,
- Arrays.asList(PACKAGE_NAME_ATOMIC_FORMULA, APP_CERTIFICATE_ATOMIC_FORMULA));
- Rule rule = new Rule(openFormula, Rule.Effect.DENY);
-
- String toString = rule.toString();
-
- assertEquals(String.format("Rule: PACKAGE_NAME EQ %s AND APP_CERTIFICATE EQ %s, DENY",
- PACKAGE_NAME, APP_CERTIFICATE), toString);
+ OpenFormula openFormula =
+ new OpenFormula(
+ OpenFormula.AND,
+ Arrays.asList(PACKAGE_NAME_ATOMIC_FORMULA, APP_CERTIFICATE_ATOMIC_FORMULA));
+ Rule rule = new Rule(openFormula, Rule.DENY);
+
+ assertEquals(
+ String.format(
+ "Rule: (PACKAGE_NAME EQ %s) AND (APP_CERTIFICATE EQ %s), DENY",
+ PACKAGE_NAME, APP_CERTIFICATE),
+ rule.toString());
}
@Test
@@ -100,4 +86,24 @@ public class RuleTest {
assertNotEquals(rule1, rule2);
}
+
+ @Test
+ public void testParcelUnparcel() {
+ Rule rule =
+ new Rule(
+ new OpenFormula(
+ OpenFormula.AND,
+ Arrays.asList(
+ APP_CERTIFICATE_ATOMIC_FORMULA,
+ new OpenFormula(
+ OpenFormula.NOT,
+ Arrays.asList(PACKAGE_NAME_ATOMIC_FORMULA)))),
+ Rule.DENY);
+ Parcel p = Parcel.obtain();
+ rule.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ Rule newRule = Rule.CREATOR.createFromParcel(p);
+
+ assertEquals(newRule, rule);
+ }
}