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

Commit 12b110e5 authored by Khaled Abdelmohsen's avatar Khaled Abdelmohsen
Browse files

Implement XML parsing for atomic formula

Parse a rule's atomic formula and effect, and wrap the result in a rule
to be appended to the list of rules parsed so far.

Bug: 143697198
Test: atest FrameworksServicesTests:RuleXmlParserTest
Change-Id: If09503472060929c6ab914b3b178c14c0e7cd314
parent 0a181cd8
Loading
Loading
Loading
Loading
+114 −3
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.server.integrity.parser;
import android.util.Slog;
import android.util.Xml;

import com.android.server.integrity.model.AtomicFormula;
import com.android.server.integrity.model.Formula;
import com.android.server.integrity.model.Rule;

import org.xmlpull.v1.XmlPullParser;
@@ -38,8 +40,15 @@ public final class RuleXmlParser implements RuleParser {

    public static final String TAG = "RuleXmlParser";

    // TODO: Use XML attributes
    private static final String RULE_LIST_TAG = "RuleList";
    private static final String RULE_TAG = "Rule";
    private static final String OPEN_FORMULA_TAG = "OpenFormula";
    private static final String ATOMIC_FORMULA_TAG = "AtomicFormula";
    private static final String EFFECT_TAG = "Effect";
    private static final String KEY_TAG = "Key";
    private static final String OPERATOR_TAG = "Operator";
    private static final String VALUE_TAG = "Value";

    @Override
    public List<Rule> parse(String ruleText) {
@@ -65,7 +74,8 @@ public final class RuleXmlParser implements RuleParser {
        return null;
    }

    private List<Rule> parseRules(XmlPullParser parser) throws IOException, XmlPullParserException {
    private static List<Rule> parseRules(XmlPullParser parser)
            throws IOException, XmlPullParserException {
        List<Rule> rules = new ArrayList<>();

        // Skipping the first event type, which is always {@link XmlPullParser.START_DOCUMENT}
@@ -98,8 +108,109 @@ public final class RuleXmlParser implements RuleParser {
        return rules;
    }

    private Rule parseRule(XmlPullParser parser) {
        // TODO: Implement rule parser.
    private static Rule parseRule(XmlPullParser parser) {
        try {
            Formula formula = null;
            @Rule.Effect int effect = 0;

            boolean isValid = true;
            int eventType;
            while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
                String nodeName = parser.getName();

                if (eventType == XmlPullParser.END_TAG && parser.getName().equals(RULE_TAG)) {
                    break;
                }

                if (eventType == XmlPullParser.START_TAG) {
                    switch (nodeName) {
                        case OPEN_FORMULA_TAG:
                            formula = parseOpenFormula(parser);
                            break;
                        case ATOMIC_FORMULA_TAG:
                            formula = parseAtomicFormula(parser);
                            break;
                        case EFFECT_TAG:
                            effect = Integer.parseInt(extractValue(parser));
                            break;
                        default:
                            isValid = false;
                    }
                } else {
                    isValid = false;
                }
            }

            return isValid ? new Rule(formula, effect) : null;
        } catch (Exception e) {
            // In case of any exceptions arising from constructing the rule, it will be skipped.
            // Rules are assumed to be validated on the server.
            return null;
        }
    }

    private static Formula parseOpenFormula(XmlPullParser parser) {
        // TODO: Implement open formula parser.
        return null;
    }

    private static Formula parseAtomicFormula(XmlPullParser parser)
            throws IOException, XmlPullParserException {
        @AtomicFormula.Key int key = 0;
        @AtomicFormula.Operator int operator = 0;
        String value = null;

        boolean isValid = true;
        int eventType;
        while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
            String nodeName = parser.getName();

            if (eventType == XmlPullParser.END_TAG && parser.getName().equals(ATOMIC_FORMULA_TAG)) {
                break;
            }

            if (eventType == XmlPullParser.START_TAG) {
                switch (nodeName) {
                    case KEY_TAG:
                        key = Integer.parseInt(extractValue(parser));
                        break;
                    case OPERATOR_TAG:
                        operator = Integer.parseInt(extractValue(parser));
                        break;
                    case VALUE_TAG:
                        value = extractValue(parser);
                        break;
                    default:
                        isValid = false;
                }
            }
        }
        return isValid ? constructAtomicFormulaBasedOnKey(key, operator, value) : null;
    }

    private static Formula constructAtomicFormulaBasedOnKey(@AtomicFormula.Key int key,
            @AtomicFormula.Operator int operator, String value) {
        switch (key) {
            case AtomicFormula.PACKAGE_NAME:
            case AtomicFormula.INSTALLER_NAME:
            case AtomicFormula.APP_CERTIFICATE:
            case AtomicFormula.INSTALLER_CERTIFICATE:
                return new AtomicFormula.StringAtomicFormula(key, value);
            case AtomicFormula.PRE_INSTALLED:
                return new AtomicFormula.BooleanAtomicFormula(key, Boolean.parseBoolean(value));
            case AtomicFormula.VERSION_CODE:
                return new AtomicFormula.IntAtomicFormula(key, operator, Integer.parseInt(value));
            default:
                return null;
        }
    }

    private static String extractValue(XmlPullParser parser)
            throws IOException, XmlPullParserException {
        String value = null;
        if (parser.next() == XmlPullParser.TEXT) {
            value = parser.getText();
        }
        return parser.next() == XmlPullParser.END_TAG ? value : null;
    }
}
+142 −16
Original line number Diff line number Diff line
@@ -18,9 +18,10 @@ package com.android.server.integrity.parser;

import static com.android.server.testutils.TestUtils.assertExpectException;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static com.google.common.truth.Truth.assertThat;

import com.android.server.integrity.model.AtomicFormula;
import com.android.server.integrity.model.OpenFormula;
import com.android.server.integrity.model.Rule;

import org.junit.Test;
@@ -29,6 +30,7 @@ import org.junit.runners.JUnit4;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;

@RunWith(JUnit4.class)
@@ -37,14 +39,14 @@ public class RuleXmlParserTest {
    private static final String VALID_RULE_XML = "<RuleList>"
            + "<Rule>"
            + "<OpenFormula>"
            + "<Connector>NOT</Connector>"
            + "<Connector>" + OpenFormula.NOT + "</Connector>"
            + "<AtomicFormula>"
            + "<Key>PACKAGE_NAME</Key>"
            + "<Operator>EQ</Operator>"
            + "<Key>" + AtomicFormula.PACKAGE_NAME + "</Key>"
            + "<Operator>" + AtomicFormula.EQ + "</Operator>"
            + "<Value>com.app.test</Value>"
            + "</AtomicFormula>"
            + "</OpenFormula>"
            + "<Effect>DENY</Effect>"
            + "<Effect>" + Rule.DENY + "</Effect>"
            + "</Rule>"
            + "</RuleList>";

@@ -54,8 +56,7 @@ public class RuleXmlParserTest {

        List<Rule> rules = xmlParser.parse(VALID_RULE_XML);

        assertNotNull(rules);
        assertTrue(rules.isEmpty());
        assertThat(rules).isEmpty();
    }

    @Test
@@ -65,8 +66,133 @@ public class RuleXmlParserTest {

        List<Rule> rules = xmlParser.parse(inputStream);

        assertNotNull(rules);
        assertTrue(rules.isEmpty());
        assertThat(rules).isEmpty();
    }

    @Test
    public void testXmlString_validAtomicFormula_stringValue() {
        String ruleXmlAtomicFormula = "<RuleList>"
                + "<Rule>"
                + "<AtomicFormula>"
                + "<Key>" + AtomicFormula.PACKAGE_NAME + "</Key>"
                + "<Operator>" + AtomicFormula.EQ + "</Operator>"
                + "<Value>com.app.test</Value>"
                + "</AtomicFormula>"
                + "<Effect>" + Rule.DENY + "</Effect>"
                + "</Rule>"
                + "</RuleList>";
        RuleParser xmlParser = new RuleXmlParser();
        Rule expectedRule = new Rule(
                new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test"),
                Rule.DENY);

        List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula);

        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
    }

    @Test
    public void testXmlString_validAtomicFormula_integerValue() {
        String ruleXmlAtomicFormula = "<RuleList>"
                + "<Rule>"
                + "<AtomicFormula>"
                + "<Key>" + AtomicFormula.VERSION_CODE + "</Key>"
                + "<Operator>" + AtomicFormula.EQ + "</Operator>"
                + "<Value>1</Value>"
                + "</AtomicFormula>"
                + "<Effect>" + Rule.DENY + "</Effect>"
                + "</Rule>"
                + "</RuleList>";
        RuleParser xmlParser = new RuleXmlParser();
        Rule expectedRule = new Rule(
                new AtomicFormula.IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1),
                Rule.DENY);

        List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula);

        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
    }

    @Test
    public void testXmlString_validAtomicFormula_booleanValue() {
        String ruleXmlAtomicFormula = "<RuleList>"
                + "<Rule>"
                + "<AtomicFormula>"
                + "<Key>" + AtomicFormula.PRE_INSTALLED + "</Key>"
                + "<Operator>" + AtomicFormula.EQ + "</Operator>"
                + "<Value>true</Value>"
                + "</AtomicFormula>"
                + "<Effect>" + Rule.DENY + "</Effect>"
                + "</Rule>"
                + "</RuleList>";
        RuleParser xmlParser = new RuleXmlParser();
        Rule expectedRule = new Rule(
                new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
                Rule.DENY);

        List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula);

        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
    }

    @Test
    public void testXmlString_validAtomicFormula_differentTagOrder() {
        String ruleXmlAtomicFormula = "<RuleList>"
                + "<Rule>"
                + "<AtomicFormula>"
                + "<Operator>" + AtomicFormula.EQ + "</Operator>"
                + "<Value>com.app.test</Value>"
                + "<Key>" + AtomicFormula.PACKAGE_NAME + "</Key>"
                + "</AtomicFormula>"
                + "<Effect>" + Rule.DENY + "</Effect>"
                + "</Rule>"
                + "</RuleList>";
        RuleParser xmlParser = new RuleXmlParser();
        Rule expectedRule = new Rule(
                new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test"),
                Rule.DENY);

        List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula);

        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
    }

    @Test
    public void testXmlString_invalidAtomicFormula_invalidTags() {
        String ruleXmlAtomicFormula = "<RuleList>"
                + "<Rule>"
                + "<AtomicFormula>"
                + "<BadKey>" + AtomicFormula.PACKAGE_NAME + "</BadKey>"
                + "<Operator>" + AtomicFormula.EQ + "</Operator>"
                + "<Value>com.app.test</Value>"
                + "</AtomicFormula>"
                + "<Effect>" + Rule.DENY + "</Effect>"
                + "</Rule>"
                + "</RuleList>";
        RuleParser xmlParser = new RuleXmlParser();

        List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula);

        assertThat(rules).isEmpty();
    }

    @Test
    public void testXmlString_invalidAtomicFormula() {
        String ruleXmlAtomicFormula = "<RuleList>"
                + "<Rule>"
                + "<AtomicFormula>"
                + "<Key>" + AtomicFormula.VERSION_CODE + "</Key>"
                + "<Operator>" + AtomicFormula.EQ + "</Operator>"
                + "<Value>com.app.test</Value>"
                + "</AtomicFormula>"
                + "<Effect>" + Rule.DENY + "</Effect>"
                + "</Rule>"
                + "</RuleList>";
        RuleParser xmlParser = new RuleXmlParser();

        List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula);

        assertThat(rules).isEmpty();
    }

    @Test
@@ -75,12 +201,12 @@ public class RuleXmlParserTest {
                + "<OpenFormula>"
                + "<Connector>NOT</Connector>"
                + "<AtomicFormula>"
                + "<Key>PACKAGE_NAME</Key>"
                + "<Operator>EQ</Operator>"
                + "<Key>" + AtomicFormula.PACKAGE_NAME + "</Key>"
                + "<Operator>" + AtomicFormula.EQ + "</Operator>"
                + "<Value>com.app.test</Value>"
                + "</AtomicFormula>"
                + "</OpenFormula>"
                + "<Effect>DENY</Effect>"
                + "<Effect>" + Rule.DENY + "</Effect>"
                + "</Rule>";
        RuleParser xmlParser = new RuleXmlParser();

@@ -96,12 +222,12 @@ public class RuleXmlParserTest {
                + "<OpenFormula>"
                + "<Connector>NOT</Connector>"
                + "<AtomicFormula>"
                + "<Key>PACKAGE_NAME</Key>"
                + "<Operator>EQ</Operator>"
                + "<Key>" + AtomicFormula.PACKAGE_NAME + "</Key>"
                + "<Operator>" + AtomicFormula.EQ + "</Operator>"
                + "<Value>com.app.test</Value>"
                + "</AtomicFormula>"
                + "</OpenFormula>"
                + "<Effect>DENY</Effect>"
                + "<Effect>" + Rule.DENY + "</Effect>"
                + "</Rule>";
        InputStream inputStream = new ByteArrayInputStream(ruleXmlWithNoRuleList.getBytes());
        RuleParser xmlParser = new RuleXmlParser();