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

Commit 379f81b2 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Prepare rule / formula classes to be ready for external API."

parents 5fdaa0c9 097f65d6
Loading
Loading
Loading
Loading
+2 −16
Original line number Diff line number Diff line
@@ -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) {
+33 −54
Original line number Diff line number Diff line
@@ -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;

/**
@@ -43,64 +49,37 @@ final class RuleEvaluator {
     * @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}.
     * @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);
        boolean denied = false;
        Rule denyRule = null;
        for (Rule rule : matchedRules) {
            switch (rule.getEffect()) {
                case DENY:
                    if (!denied) {
                        denied = true;
                        denyRule = rule;
                    }

    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());
                    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;
                    Slog.e(TAG, "Matched an unknown effect rule: " + rule);
                    return IntegrityCheckResult.allow();
            }
        } 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;
        }
        }

        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;
    }
}
+26 −13
Original line number Diff line number Diff line
@@ -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);
+348 −164

File changed.

Preview size limit exceeded, changes collapsed.

+76 −1
Original line number Diff line number Diff line
@@ -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
 */
@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
     */
public abstract class Formula {
    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);
        }
    }
}
Loading