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

Commit 09e44f5f authored by Omer Nebil Yaveroglu's avatar Omer Nebil Yaveroglu
Browse files

Refactor the IntegrityCheckResult to hold a list of rules so that we can

identify the DENY / FORCE ALLOW type in our logs. The change also
refactors evaluateRules of RuleEvaluator method so that it is simpler to
understand.

Bug: 147095027
Test: atest frameworks/base/services/tests/servicetests/src/com/android/server/integrity/model
Change-Id: Ifc9d026ae19940adb9f4aa5028acbd2e495595d1
parent 4f3fb47c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -260,7 +260,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
                    "Integrity check result: "
                            + result.getEffect()
                            + " due to "
                            + result.getRule());
                            + result.getMatchedRules());

            StatsLog.write(
                    StatsLog.INTEGRITY_CHECK_RESULT_REPORTED,
+27 −22
Original line number Diff line number Diff line
@@ -25,8 +25,8 @@ import android.content.integrity.Rule;

import com.android.server.integrity.model.IntegrityCheckResult;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * A helper class for evaluating rules against app install metadata to find if there are matching
@@ -48,29 +48,34 @@ final class RuleEvaluator {
    @NonNull
    static IntegrityCheckResult evaluateRules(
            List<Rule> rules, AppInstallMetadata appInstallMetadata) {
        List<Rule> matchedRules = new ArrayList<>();
        for (Rule rule : rules) {
            if (rule.getFormula().matches(appInstallMetadata)) {
                matchedRules.add(rule);
            }
        }

        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:
                    throw new IllegalArgumentException("Matched an unknown effect rule: " + rule);
        // Identify the rules that match the {@code appInstallMetadata}.
        List<Rule> matchedRules =
                rules.stream()
                        .filter(rule -> rule.getFormula().matches(appInstallMetadata))
                        .collect(Collectors.toList());

        // Identify the matched power allow rules and terminate early if we have any.
        List<Rule> matchedPowerAllowRules =
                matchedRules.stream()
                        .filter(rule -> rule.getEffect() == FORCE_ALLOW)
                        .collect(Collectors.toList());

        if (!matchedPowerAllowRules.isEmpty()) {
            return IntegrityCheckResult.allow(matchedPowerAllowRules);
        }

        // Identify the matched deny rules.
        List<Rule> matchedDenyRules =
                matchedRules.stream()
                        .filter(rule -> rule.getEffect() == DENY)
                        .collect(Collectors.toList());

        if (!matchedDenyRules.isEmpty()) {
            return IntegrityCheckResult.deny(matchedDenyRules);
        }
        return denied ? IntegrityCheckResult.deny(denyRule) : IntegrityCheckResult.allow();

        // When no rules are denied, return default allow result.
        return IntegrityCheckResult.allow();
    }
}
+19 −14
Original line number Diff line number Diff line
@@ -20,6 +20,9 @@ import android.annotation.Nullable;
import android.content.integrity.Rule;
import android.util.StatsLog;

import java.util.Collections;
import java.util.List;

/**
 * A class encapsulating the result from the evaluation engine after evaluating rules against app
 * install metadata.
@@ -35,19 +38,19 @@ public final class IntegrityCheckResult {
    }

    private final Effect mEffect;
    @Nullable private final Rule mRule;
    private final List<Rule> mRuleList;

    private IntegrityCheckResult(Effect effect, @Nullable Rule rule) {
    private IntegrityCheckResult(Effect effect, @Nullable List<Rule> ruleList) {
        this.mEffect = effect;
        this.mRule = rule;
        this.mRuleList = ruleList;
    }

    public Effect getEffect() {
        return mEffect;
    }

    public Rule getRule() {
        return mRule;
    public List<Rule> getMatchedRules() {
        return mRuleList;
    }

    /**
@@ -56,7 +59,7 @@ public final class IntegrityCheckResult {
     * @return An evaluation outcome with ALLOW effect and no rule.
     */
    public static IntegrityCheckResult allow() {
        return new IntegrityCheckResult(Effect.ALLOW, null);
        return new IntegrityCheckResult(Effect.ALLOW, Collections.emptyList());
    }

    /**
@@ -64,30 +67,32 @@ public final class IntegrityCheckResult {
     *
     * @return An evaluation outcome with ALLOW effect and rule causing that effect.
     */
    public static IntegrityCheckResult allow(Rule rule) {
        return new IntegrityCheckResult(Effect.ALLOW, rule);
    public static IntegrityCheckResult allow(List<Rule> ruleList) {
        return new IntegrityCheckResult(Effect.ALLOW, ruleList);
    }

    /**
     * Create a DENY evaluation outcome.
     *
     * @param rule Rule causing the DENY effect.
     * @param ruleList All valid rules that cause the DENY effect.
     * @return An evaluation outcome with DENY effect and rule causing that effect.
     */
    public static IntegrityCheckResult deny(Rule rule) {
        return new IntegrityCheckResult(Effect.DENY, rule);
    public static IntegrityCheckResult deny(List<Rule> ruleList) {
        return new IntegrityCheckResult(Effect.DENY, ruleList);
    }

    /**
     * Returns the in value of the integrity check result for logging purposes.
     */
    public int getLoggingResponse() {
        if (getEffect() == IntegrityCheckResult.Effect.DENY) {
        if (getEffect() == Effect.DENY) {
            return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__REJECTED;
        } else if (getRule() != null) {
        } else if (getEffect() == Effect.ALLOW && getMatchedRules().isEmpty()) {
            return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__ALLOWED;
        } else if (getEffect() == Effect.ALLOW && !getMatchedRules().isEmpty()) {
            return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__FORCE_ALLOWED;
        } else {
            return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__ALLOWED;
            throw new IllegalStateException("IntegrityCheckResult is not valid.");
        }
    }

+5 −4
Original line number Diff line number Diff line
@@ -328,10 +328,11 @@ public class AppIntegrityManagerServiceImplTest {
        when(mRuleEvaluationEngine.evaluate(any(), any()))
                .thenReturn(
                        IntegrityCheckResult.deny(
                                Arrays.asList(
                                        new Rule(
                                                new AtomicFormula.BooleanAtomicFormula(
                                                        AtomicFormula.PRE_INSTALLED, false),
                                        Rule.DENY)));
                                                Rule.DENY))));
        Intent intent = makeVerificationIntent();

        broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
+55 −23
Original line number Diff line number Diff line
@@ -63,7 +63,7 @@ public class RuleEvaluatorTest {

    @Test
    public void testEvaluateRules_noMatchedRules_allow() {
        Rule rule1 =
        Rule rule =
                new Rule(
                        new StringAtomicFormula(
                                AtomicFormula.PACKAGE_NAME,
@@ -72,7 +72,7 @@ public class RuleEvaluatorTest {
                        Rule.DENY);

        IntegrityCheckResult result =
                RuleEvaluator.evaluateRules(Collections.singletonList(rule1), APP_INSTALL_METADATA);
                RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);

        assertThat(result.getEffect()).isEqualTo(ALLOW);
    }
@@ -98,7 +98,7 @@ public class RuleEvaluatorTest {
                RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);

        assertThat(result.getEffect()).isEqualTo(DENY);
        assertThat(result.getRule()).isEqualTo(rule1);
        assertThat(result.getMatchedRules()).containsExactly(rule1);
    }

    @Test
@@ -110,7 +110,7 @@ public class RuleEvaluatorTest {
                                PACKAGE_NAME_1,
                                /* isHashedValue= */ false),
                        Rule.DENY);
        CompoundFormula compoundFormula2 =
        Rule rule2 = new Rule(
                new CompoundFormula(
                        CompoundFormula.AND,
                        Arrays.asList(
@@ -121,33 +121,33 @@ public class RuleEvaluatorTest {
                                new StringAtomicFormula(
                                        AtomicFormula.APP_CERTIFICATE,
                                        APP_CERTIFICATE,
                                        /* isHashedValue= */ false)));
        Rule rule2 = new Rule(compoundFormula2, Rule.DENY);
                                        /* isHashedValue= */ false))),
                Rule.DENY);

        IntegrityCheckResult result =
                RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);

        assertThat(result.getEffect()).isEqualTo(DENY);
        assertThat(result.getRule()).isEqualTo(rule1);
        assertThat(result.getMatchedRules()).containsExactly(rule1, rule2);
    }

    @Test
    public void testEvaluateRules_ruleWithNot_deny() {
        CompoundFormula compoundFormula =
        Rule rule = new Rule(
                new CompoundFormula(
                        CompoundFormula.NOT,
                        Collections.singletonList(
                                new StringAtomicFormula(
                                        AtomicFormula.PACKAGE_NAME,
                                        PACKAGE_NAME_2,
                                        /* isHashedValue= */ false)));
        Rule rule = new Rule(compoundFormula, Rule.DENY);
                                        /* isHashedValue= */ false))),
                Rule.DENY);

        IntegrityCheckResult result =
                RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);

        assertThat(result.getEffect()).isEqualTo(DENY);
        assertThat(result.getRule()).isEqualTo(rule);
        assertThat(result.getMatchedRules()).containsExactly(rule);
    }

    @Test
@@ -162,12 +162,12 @@ public class RuleEvaluatorTest {
                RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);

        assertThat(result.getEffect()).isEqualTo(DENY);
        assertThat(result.getRule()).isEqualTo(rule);
        assertThat(result.getMatchedRules()).containsExactly(rule);
    }

    @Test
    public void testEvaluateRules_validForm_deny() {
        CompoundFormula compoundFormula =
        Rule rule = new Rule(
                new CompoundFormula(
                        CompoundFormula.AND,
                        Arrays.asList(
@@ -178,19 +178,19 @@ public class RuleEvaluatorTest {
                                new StringAtomicFormula(
                                        AtomicFormula.APP_CERTIFICATE,
                                        APP_CERTIFICATE,
                                        /* isHashedValue= */ false)));
        Rule rule = new Rule(compoundFormula, Rule.DENY);
                                        /* isHashedValue= */ false))),
                Rule.DENY);

        IntegrityCheckResult result =
                RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);

        assertThat(result.getEffect()).isEqualTo(DENY);
        assertThat(result.getRule()).isEqualTo(rule);
        assertThat(result.getMatchedRules()).containsExactly(rule);
    }

    @Test
    public void testEvaluateRules_orRules() {
        CompoundFormula compoundFormula =
        Rule rule = new Rule(
                new CompoundFormula(
                        CompoundFormula.OR,
                        Arrays.asList(
@@ -201,13 +201,14 @@ public class RuleEvaluatorTest {
                                new StringAtomicFormula(
                                        AtomicFormula.APP_CERTIFICATE,
                                        APP_CERTIFICATE,
                                        /* isHashedValue= */ false)));
        Rule rule = new Rule(compoundFormula, Rule.DENY);
                                        /* isHashedValue= */ false))),
                Rule.DENY);

        IntegrityCheckResult result =
                RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);

        assertThat(result.getEffect()).isEqualTo(DENY);
        assertThat(result.getMatchedRules()).containsExactly(rule);
    }

    @Test
@@ -232,6 +233,7 @@ public class RuleEvaluatorTest {
                RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);

        assertThat(result.getEffect()).isEqualTo(DENY);
        assertThat(result.getMatchedRules()).containsExactly(rule);
    }

    @Test
@@ -243,7 +245,7 @@ public class RuleEvaluatorTest {
                                PACKAGE_NAME_1,
                                /* isHashedValue= */ false),
                        Rule.FORCE_ALLOW);
        CompoundFormula compoundFormula2 =
        Rule rule2 = new Rule(
                new CompoundFormula(
                        CompoundFormula.AND,
                        Arrays.asList(
@@ -254,13 +256,43 @@ public class RuleEvaluatorTest {
                                new StringAtomicFormula(
                                        AtomicFormula.APP_CERTIFICATE,
                                        APP_CERTIFICATE,
                                        /* isHashedValue= */ false)));
        Rule rule2 = new Rule(compoundFormula2, Rule.DENY);
                                        /* isHashedValue= */ false))),
                Rule.DENY);

        IntegrityCheckResult result =
                RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);

        assertThat(result.getEffect()).isEqualTo(ALLOW);
        assertThat(result.getMatchedRules()).containsExactly(rule1);
    }

    @Test
    public void testEvaluateRules_multipleMatches_forceAllow() {
        Rule rule1 =
                new Rule(
                        new StringAtomicFormula(
                                AtomicFormula.PACKAGE_NAME,
                                PACKAGE_NAME_1,
                                /* isHashedValue= */ false),
                        Rule.FORCE_ALLOW);
        Rule rule2 = new Rule(
                new CompoundFormula(
                        CompoundFormula.AND,
                        Arrays.asList(
                                new StringAtomicFormula(
                                        AtomicFormula.PACKAGE_NAME,
                                        PACKAGE_NAME_1,
                                        /* isHashedValue= */ false),
                                new StringAtomicFormula(
                                        AtomicFormula.APP_CERTIFICATE,
                                        APP_CERTIFICATE,
                                        /* isHashedValue= */ false))),
                Rule.FORCE_ALLOW);

        IntegrityCheckResult result =
                RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);

        assertThat(result.getEffect()).isEqualTo(ALLOW);
        assertThat(result.getRule()).isEqualTo(rule1);
        assertThat(result.getMatchedRules()).containsExactly(rule1, rule2);
    }
}
 No newline at end of file
Loading