Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29be9648 authored by Khaled Abdelmohsen's avatar Khaled Abdelmohsen
Browse files

Match rules against app install metadata

Match a list of rules against metadata of the app to be installed. For
atomic formulas, values from the metadata must match values of the
formula. For open formulas, sub-formulas must match respecting the
connector used between them.

Bug: 141971373
Test: atest FrameworksServicesTests:RuleMatcherTest
Change-Id: I133a268b84dc3ab727970acb743c8a3a532d4383
parent aba57d6a
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -66,7 +66,7 @@ public final class RuleEvaluationEngine {
                case DENY:
                    return IntegrityCheckResult.deny(matchedRule);
                default:
                    Slog.i(TAG, "Matched a non-DENY rule: " + matchedRule);
                    Slog.e(TAG, "Matched a non-DENY rule: " + matchedRule);
                    return IntegrityCheckResult.allow();
            }
        }
+54 −1
Original line number Diff line number Diff line
@@ -16,7 +16,12 @@

package com.android.server.integrity.engine;

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.OpenFormula;
import com.android.server.integrity.model.Rule;

import java.util.List;
@@ -27,6 +32,8 @@ import java.util.List;
 */
final class RuleEvaluator {

    private static final String TAG = "RuleEvaluator";

    /**
     * Match the list of rules against an app install metadata.
     *
@@ -45,8 +52,54 @@ final class RuleEvaluator {
        return Rule.EMPTY;
    }

    /**
     * Match a rule against app install metadata.
     */
    private static boolean isMatch(Rule rule, AppInstallMetadata appInstallMetadata) {
        // TODO: Add matching logic
        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());
                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:
                    boolean result = true;
                    for (Formula subFormula : openFormula.getFormulas()) {
                        result &= isMatch(subFormula, appInstallMetadata);
                    }
                    return result;
                default:
                    Slog.i(TAG, String.format("Returned no match for unknown connector %s",
                            openFormula.getConnector()));
                    return false;
            }
        }

        return false;
    }
}
+59 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;

import android.annotation.Nullable;
import android.util.Slog;

/**
 * Represents a simple formula consisting of an app install metadata field and a value.
@@ -28,7 +29,9 @@ import android.annotation.Nullable;
 */
public final class AtomicFormula extends Formula {

    enum Key {
    private static final String TAG = "AtomicFormula";

    public enum Key {
        PACKAGE_NAME,
        APP_CERTIFICATE,
        INSTALLER_NAME,
@@ -37,7 +40,7 @@ public final class AtomicFormula extends Formula {
        PRE_INSTALLED
    }

    enum Operator {
    public enum Operator {
        EQ,
        LT,
        LE,
@@ -132,6 +135,59 @@ public final class AtomicFormula extends Formula {
        return String.format("%s %s %s", mKey, mOperator, getValue());
    }

    /**
     * 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);
        }
        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;
        }
        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;
        }
        Slog.i(TAG, String.format("Found operator %s for value %s", mOperator, mBoolValue));
        return false;
    }

    private void validateOperator(Key key, Operator operator) {
        boolean validOperator;
        switch (key) {
@@ -146,6 +202,7 @@ public final class AtomicFormula extends Formula {
                validOperator = true;
                break;
            default:
                Slog.i(TAG, String.format("Found operator %s for key %s", operator, key));
                validOperator = false;
        }
        if (!validOperator) {
+1 −1
Original line number Diff line number Diff line
@@ -19,6 +19,6 @@ package com.android.server.integrity.model;
/**
 * Represents a rule logic/content.
 */
abstract class Formula {
public abstract class Formula {

}
+1 −1
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ import java.util.List;
 */
public final class OpenFormula extends Formula {

    enum Connector {
    public enum Connector {
        AND,
        OR,
        NOT
Loading