blob: 6e1218064096f4f5793cf06b0f99e6e49458f95d [file] [log] [blame]
Khaled Abdelmohsen7c56df62019-10-31 16:59:43 +00001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.integrity.serializer;
18
Omer Nebil Yaveroglua00ba072019-12-20 17:25:40 +000019import static com.android.server.integrity.serializer.RuleIndexingDetails.APP_CERTIFICATE_INDEXED;
20import static com.android.server.integrity.serializer.RuleIndexingDetails.NOT_INDEXED;
21import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAGE_NAME_INDEXED;
22
Song Pan75147d52019-11-19 00:57:46 +000023import android.content.integrity.AtomicFormula;
24import android.content.integrity.CompoundFormula;
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +000025import android.content.integrity.IntegrityFormula;
Song Pan75147d52019-11-19 00:57:46 +000026import android.content.integrity.Rule;
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +000027import android.util.Xml;
28
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +000029import org.xmlpull.v1.XmlSerializer;
Khaled Abdelmohsen7c56df62019-10-31 16:59:43 +000030
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +000031import java.io.IOException;
32import java.io.OutputStream;
33import java.io.StringWriter;
34import java.nio.charset.StandardCharsets;
35import java.util.List;
Omer Nebil Yaveroglua00ba072019-12-20 17:25:40 +000036import java.util.Map;
Khaled Abdelmohsen47483d52019-11-28 12:28:44 +000037import java.util.Optional;
Omer Nebil Yaveroglu8d46f342020-01-13 15:28:11 +000038import java.util.stream.Collectors;
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +000039
Song Pan75147d52019-11-19 00:57:46 +000040/** A helper class to serialize rules from the {@link Rule} model to Xml representation. */
Khaled Abdelmohsen7c56df62019-10-31 16:59:43 +000041public class RuleXmlSerializer implements RuleSerializer {
42
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +000043 public static final String TAG = "RuleXmlSerializer";
44 private static final String NAMESPACE = "";
45
Khaled Abdelmohsen6d77f942019-11-12 16:47:01 +000046 private static final String RULE_LIST_TAG = "RL";
47 private static final String RULE_TAG = "R";
Khaled Abdelmohsen1fa4de52019-11-13 15:13:19 +000048 private static final String COMPOUND_FORMULA_TAG = "OF";
Khaled Abdelmohsen6d77f942019-11-12 16:47:01 +000049 private static final String ATOMIC_FORMULA_TAG = "AF";
Khaled Abdelmohsenb05d7062019-11-19 01:22:23 +000050 private static final String EFFECT_ATTRIBUTE = "E";
51 private static final String KEY_ATTRIBUTE = "K";
52 private static final String OPERATOR_ATTRIBUTE = "O";
53 private static final String VALUE_ATTRIBUTE = "V";
54 private static final String CONNECTOR_ATTRIBUTE = "C";
Khaled Abdelmohsen1efff872019-11-25 16:44:20 +000055 private static final String IS_HASHED_VALUE_ATTRIBUTE = "H";
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +000056
Khaled Abdelmohsen7c56df62019-10-31 16:59:43 +000057 @Override
Khaled Abdelmohsen47483d52019-11-28 12:28:44 +000058 public void serialize(
Omer Nebil Yaveroglu2935a212020-01-03 14:33:51 +000059 List<Rule> rules,
60 Optional<Integer> formatVersion,
61 OutputStream outputStream,
62 OutputStream indexingOutputStream)
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +000063 throws RuleSerializeException {
64 try {
65 XmlSerializer xmlSerializer = Xml.newSerializer();
66 xmlSerializer.setOutput(outputStream, StandardCharsets.UTF_8.name());
67 serializeRules(rules, xmlSerializer);
Omer Nebil Yaveroglu2935a212020-01-03 14:33:51 +000068
69 // TODO(b/145493956): Implement the indexing logic.
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +000070 } catch (Exception e) {
71 throw new RuleSerializeException(e.getMessage(), e);
72 }
Khaled Abdelmohsen7c56df62019-10-31 16:59:43 +000073 }
74
75 @Override
Khaled Abdelmohsen52d93412019-12-04 16:19:28 +000076 public byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion)
Khaled Abdelmohsen47483d52019-11-28 12:28:44 +000077 throws RuleSerializeException {
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +000078 try {
79 XmlSerializer xmlSerializer = Xml.newSerializer();
80 StringWriter writer = new StringWriter();
81 xmlSerializer.setOutput(writer);
82 serializeRules(rules, xmlSerializer);
Khaled Abdelmohsen52d93412019-12-04 16:19:28 +000083 return writer.toString().getBytes(StandardCharsets.UTF_8);
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +000084 } catch (Exception e) {
85 throw new RuleSerializeException(e.getMessage(), e);
86 }
87 }
88
Omer Nebil Yaveroglua00ba072019-12-20 17:25:40 +000089 private void serializeRules(List<Rule> rules, XmlSerializer xmlSerializer)
90 throws RuleSerializeException {
91 try {
92 // Determine the indexing groups and the order of the rules within each indexed group.
Omer Nebil Yaveroglu8d46f342020-01-13 15:28:11 +000093 Map<Integer, Map<String, List<Rule>>> indexedRules =
Omer Nebil Yaveroglua00ba072019-12-20 17:25:40 +000094 RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules);
95
96 // Write the XML formatted rules in order.
97 xmlSerializer.startTag(NAMESPACE, RULE_LIST_TAG);
98
99 serializeRuleList(indexedRules.get(PACKAGE_NAME_INDEXED), xmlSerializer);
100 serializeRuleList(indexedRules.get(APP_CERTIFICATE_INDEXED), xmlSerializer);
101 serializeRuleList(indexedRules.get(NOT_INDEXED), xmlSerializer);
102
103 xmlSerializer.endTag(NAMESPACE, RULE_LIST_TAG);
104 xmlSerializer.endDocument();
105 } catch (Exception e) {
106 throw new RuleSerializeException(e.getMessage(), e);
107 }
108 }
109
Omer Nebil Yaveroglu8d46f342020-01-13 15:28:11 +0000110 private void serializeRuleList(Map<String, List<Rule>> rulesMap, XmlSerializer xmlSerializer)
Omer Nebil Yaveroglua00ba072019-12-20 17:25:40 +0000111 throws IOException {
Omer Nebil Yaveroglu8d46f342020-01-13 15:28:11 +0000112 List<String> sortedKeyList =
113 rulesMap.keySet().stream().sorted().collect(Collectors.toList());
114 for (String key : sortedKeyList) {
115 for (Rule rule : rulesMap.get(key)) {
Omer Nebil Yaveroglud562ee02020-01-02 16:20:02 +0000116 serializeRule(rule, xmlSerializer);
117 }
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +0000118 }
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +0000119 }
120
Khaled Abdelmohsen1fa4de52019-11-13 15:13:19 +0000121 private void serializeRule(Rule rule, XmlSerializer xmlSerializer) throws IOException {
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +0000122 if (rule == null) {
123 return;
124 }
125 xmlSerializer.startTag(NAMESPACE, RULE_TAG);
Khaled Abdelmohsenb05d7062019-11-19 01:22:23 +0000126 serializeAttributeValue(EFFECT_ATTRIBUTE, String.valueOf(rule.getEffect()), xmlSerializer);
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +0000127 serializeFormula(rule.getFormula(), xmlSerializer);
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +0000128 xmlSerializer.endTag(NAMESPACE, RULE_TAG);
129 }
130
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000131 private void serializeFormula(IntegrityFormula formula, XmlSerializer xmlSerializer)
132 throws IOException {
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +0000133 if (formula instanceof AtomicFormula) {
134 serializeAtomicFormula((AtomicFormula) formula, xmlSerializer);
Song Pan75147d52019-11-19 00:57:46 +0000135 } else if (formula instanceof CompoundFormula) {
Khaled Abdelmohsen1fa4de52019-11-13 15:13:19 +0000136 serializeCompoundFormula((CompoundFormula) formula, xmlSerializer);
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +0000137 } else {
138 throw new IllegalArgumentException(
139 String.format("Invalid formula type: %s", formula.getClass()));
140 }
141 }
142
Khaled Abdelmohsen1fa4de52019-11-13 15:13:19 +0000143 private void serializeCompoundFormula(
144 CompoundFormula compoundFormula, XmlSerializer xmlSerializer) throws IOException {
Song Pan75147d52019-11-19 00:57:46 +0000145 if (compoundFormula == null) {
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +0000146 return;
147 }
Khaled Abdelmohsen1fa4de52019-11-13 15:13:19 +0000148 xmlSerializer.startTag(NAMESPACE, COMPOUND_FORMULA_TAG);
Khaled Abdelmohsen47483d52019-11-28 12:28:44 +0000149 serializeAttributeValue(
150 CONNECTOR_ATTRIBUTE, String.valueOf(compoundFormula.getConnector()), xmlSerializer);
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000151 for (IntegrityFormula formula : compoundFormula.getFormulas()) {
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +0000152 serializeFormula(formula, xmlSerializer);
153 }
Khaled Abdelmohsen1fa4de52019-11-13 15:13:19 +0000154 xmlSerializer.endTag(NAMESPACE, COMPOUND_FORMULA_TAG);
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +0000155 }
156
157 private void serializeAtomicFormula(AtomicFormula atomicFormula, XmlSerializer xmlSerializer)
158 throws IOException {
159 if (atomicFormula == null) {
160 return;
161 }
162 xmlSerializer.startTag(NAMESPACE, ATOMIC_FORMULA_TAG);
Khaled Abdelmohsen47483d52019-11-28 12:28:44 +0000163 serializeAttributeValue(
164 KEY_ATTRIBUTE, String.valueOf(atomicFormula.getKey()), xmlSerializer);
Khaled Abdelmohsen8bc377c2019-12-09 13:08:37 +0000165 if (atomicFormula.getTag() == AtomicFormula.STRING_ATOMIC_FORMULA_TAG) {
Khaled Abdelmohsen47483d52019-11-28 12:28:44 +0000166 serializeAttributeValue(
167 VALUE_ATTRIBUTE,
168 ((AtomicFormula.StringAtomicFormula) atomicFormula).getValue(),
169 xmlSerializer);
170 serializeAttributeValue(
171 IS_HASHED_VALUE_ATTRIBUTE,
Khaled Abdelmohsen1efff872019-11-25 16:44:20 +0000172 String.valueOf(
173 ((AtomicFormula.StringAtomicFormula) atomicFormula).getIsHashedValue()),
174 xmlSerializer);
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000175 } else if (atomicFormula.getTag() == AtomicFormula.LONG_ATOMIC_FORMULA_TAG) {
Khaled Abdelmohsen47483d52019-11-28 12:28:44 +0000176 serializeAttributeValue(
177 OPERATOR_ATTRIBUTE,
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000178 String.valueOf(((AtomicFormula.LongAtomicFormula) atomicFormula).getOperator()),
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +0000179 xmlSerializer);
Khaled Abdelmohsen47483d52019-11-28 12:28:44 +0000180 serializeAttributeValue(
181 VALUE_ATTRIBUTE,
Omer Nebil Yaveroglu15395f52020-01-22 12:14:44 +0000182 String.valueOf(((AtomicFormula.LongAtomicFormula) atomicFormula).getValue()),
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +0000183 xmlSerializer);
Khaled Abdelmohsen8bc377c2019-12-09 13:08:37 +0000184 } else if (atomicFormula.getTag() == AtomicFormula.BOOLEAN_ATOMIC_FORMULA_TAG) {
Khaled Abdelmohsen47483d52019-11-28 12:28:44 +0000185 serializeAttributeValue(
186 VALUE_ATTRIBUTE,
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +0000187 String.valueOf(((AtomicFormula.BooleanAtomicFormula) atomicFormula).getValue()),
188 xmlSerializer);
189 } else {
190 throw new IllegalArgumentException(
191 String.format("Invalid atomic formula type: %s", atomicFormula.getClass()));
192 }
193 xmlSerializer.endTag(NAMESPACE, ATOMIC_FORMULA_TAG);
194 }
195
Khaled Abdelmohsen47483d52019-11-28 12:28:44 +0000196 private void serializeAttributeValue(
197 String attribute, String value, XmlSerializer xmlSerializer) throws IOException {
Khaled Abdelmohsen0efe7cb2019-11-08 22:18:54 +0000198 if (value == null) {
199 return;
200 }
Khaled Abdelmohsenb05d7062019-11-19 01:22:23 +0000201 xmlSerializer.attribute(NAMESPACE, attribute, value);
Khaled Abdelmohsen7c56df62019-10-31 16:59:43 +0000202 }
203}