diff --git a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java index bf31bb2c3665f..5e24d21eb09ad 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java @@ -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 parse(String ruleText) { @@ -65,7 +74,8 @@ public final class RuleXmlParser implements RuleParser { return null; } - private List parseRules(XmlPullParser parser) throws IOException, XmlPullParserException { + private static List parseRules(XmlPullParser parser) + throws IOException, XmlPullParserException { List 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; + } } diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java index 86e544d409fe4..1aa9046bb1a1c 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java @@ -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 = "" + "" + "" - + "NOT" + + "" + OpenFormula.NOT + "" + "" - + "PACKAGE_NAME" - + "EQ" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + "com.app.test" + "" + "" - + "DENY" + + "" + Rule.DENY + "" + "" + ""; @@ -54,8 +56,7 @@ public class RuleXmlParserTest { List rules = xmlParser.parse(VALID_RULE_XML); - assertNotNull(rules); - assertTrue(rules.isEmpty()); + assertThat(rules).isEmpty(); } @Test @@ -65,8 +66,133 @@ public class RuleXmlParserTest { List rules = xmlParser.parse(inputStream); - assertNotNull(rules); - assertTrue(rules.isEmpty()); + assertThat(rules).isEmpty(); + } + + @Test + public void testXmlString_validAtomicFormula_stringValue() { + String ruleXmlAtomicFormula = "" + + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + Rule.DENY + "" + + "" + + ""; + RuleParser xmlParser = new RuleXmlParser(); + Rule expectedRule = new Rule( + new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test"), + Rule.DENY); + + List rules = xmlParser.parse(ruleXmlAtomicFormula); + + assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); + } + + @Test + public void testXmlString_validAtomicFormula_integerValue() { + String ruleXmlAtomicFormula = "" + + "" + + "" + + "" + AtomicFormula.VERSION_CODE + "" + + "" + AtomicFormula.EQ + "" + + "1" + + "" + + "" + Rule.DENY + "" + + "" + + ""; + RuleParser xmlParser = new RuleXmlParser(); + Rule expectedRule = new Rule( + new AtomicFormula.IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1), + Rule.DENY); + + List rules = xmlParser.parse(ruleXmlAtomicFormula); + + assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); + } + + @Test + public void testXmlString_validAtomicFormula_booleanValue() { + String ruleXmlAtomicFormula = "" + + "" + + "" + + "" + AtomicFormula.PRE_INSTALLED + "" + + "" + AtomicFormula.EQ + "" + + "true" + + "" + + "" + Rule.DENY + "" + + "" + + ""; + RuleParser xmlParser = new RuleXmlParser(); + Rule expectedRule = new Rule( + new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true), + Rule.DENY); + + List rules = xmlParser.parse(ruleXmlAtomicFormula); + + assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); + } + + @Test + public void testXmlString_validAtomicFormula_differentTagOrder() { + String ruleXmlAtomicFormula = "" + + "" + + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + + "" + Rule.DENY + "" + + "" + + ""; + RuleParser xmlParser = new RuleXmlParser(); + Rule expectedRule = new Rule( + new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test"), + Rule.DENY); + + List rules = xmlParser.parse(ruleXmlAtomicFormula); + + assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); + } + + @Test + public void testXmlString_invalidAtomicFormula_invalidTags() { + String ruleXmlAtomicFormula = "" + + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + Rule.DENY + "" + + "" + + ""; + RuleParser xmlParser = new RuleXmlParser(); + + List rules = xmlParser.parse(ruleXmlAtomicFormula); + + assertThat(rules).isEmpty(); + } + + @Test + public void testXmlString_invalidAtomicFormula() { + String ruleXmlAtomicFormula = "" + + "" + + "" + + "" + AtomicFormula.VERSION_CODE + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + Rule.DENY + "" + + "" + + ""; + RuleParser xmlParser = new RuleXmlParser(); + + List rules = xmlParser.parse(ruleXmlAtomicFormula); + + assertThat(rules).isEmpty(); } @Test @@ -75,12 +201,12 @@ public class RuleXmlParserTest { + "" + "NOT" + "" - + "PACKAGE_NAME" - + "EQ" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + "com.app.test" + "" + "" - + "DENY" + + "" + Rule.DENY + "" + ""; RuleParser xmlParser = new RuleXmlParser(); @@ -96,12 +222,12 @@ public class RuleXmlParserTest { + "" + "NOT" + "" - + "PACKAGE_NAME" - + "EQ" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + "com.app.test" + "" + "" - + "DENY" + + "" + Rule.DENY + "" + ""; InputStream inputStream = new ByteArrayInputStream(ruleXmlWithNoRuleList.getBytes()); RuleParser xmlParser = new RuleXmlParser();