Implement rule binary serializer

Bug: 143697198
Test: atest FrameworksServicesTests:RuleBinarySerializerTest
Change-Id: Ia804137d8b127a3b2ef8a7b692b7903360fdbe8c
This commit is contained in:
Khaled Abdelmohsen
2019-11-13 15:13:19 +00:00
parent 99d990ec1b
commit 1fa4de584c
7 changed files with 732 additions and 20 deletions

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.integrity.model;
import java.util.BitSet;
/** A wrapper class for writing a stream of bits. */
public class BitOutputStream {
private BitSet mBitSet;
private int mIndex;
public BitOutputStream() {
mBitSet = new BitSet();
mIndex = 0;
}
/**
* Set the next number of bits in the stream to value.
*
* @param numOfBits The number of bits used to represent the value.
* @param value The value to convert to bits.
*/
public void setNext(int numOfBits, int value) {
if (numOfBits <= 0) {
return;
}
int offset = 1 << (numOfBits - 1);
while (numOfBits-- > 0) {
mBitSet.set(mIndex, (value & offset) != 0);
offset >>= 1;
mIndex++;
}
}
/**
* Set the next bit in the stream to value.
*
* @param value The value to set the bit to.
*/
public void setNext(boolean value) {
mBitSet.set(mIndex, value);
mIndex++;
}
/** Set the next bit in the stream to true. */
public void setNext() {
setNext(/* value= */ true);
}
/** Convert BitSet in big-endian to ByteArray in big-endian. */
public byte[] toByteArray() {
int bitSetSize = mBitSet.length();
int numOfBytes = bitSetSize / 8;
if (bitSetSize % 8 != 0) {
numOfBytes++;
}
byte[] bytes = new byte[numOfBytes];
for (int i = 0; i < mBitSet.length(); i++) {
if (mBitSet.get(i)) {
bytes[i / 8] |= 1 << (7 - (i % 8));
}
}
return bytes;
}
/** Clear the stream. */
public void clear() {
mBitSet.clear();
mIndex = 0;
}
}

View File

@@ -25,13 +25,13 @@ import java.util.List;
public class RuleBinaryParser implements RuleParser {
@Override
public List<Rule> parse(byte[] ruleBytes) {
public List<Rule> parse(byte[] ruleBytes) throws RuleParseException {
// TODO: Implement binary text parser.
return null;
}
@Override
public List<Rule> parse(InputStream inputStream) {
public List<Rule> parse(InputStream inputStream) throws RuleParseException {
// TODO: Implement stream parser.
return null;
}

View File

@@ -16,24 +16,157 @@
package com.android.server.integrity.serializer;
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
import android.content.integrity.Formula;
import android.content.integrity.Rule;
import com.android.server.integrity.model.BitOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;
/** A helper class to serialize rules from the {@link Rule} model to Xml representation. */
/** A helper class to serialize rules from the {@link Rule} model to Binary representation. */
public class RuleBinarySerializer implements RuleSerializer {
public static final int FORMAT_VERSION_BITS = 5;
public static final int EFFECT_BITS = 3;
public static final int KEY_BITS = 4;
public static final int OPERATOR_BITS = 3;
public static final int CONNECTOR_BITS = 2;
public static final int SEPARATOR_BITS = 2;
public static final int VALUE_SIZE_BITS = 5;
public static final int ATOMIC_FORMULA_START = 0;
public static final int COMPOUND_FORMULA_START = 1;
public static final int COMPOUND_FORMULA_END = 2;
public static final int DEFAULT_FORMAT_VERSION = 1;
// Get the byte representation for a list of rules, and write them to an output stream.
@Override
public void serialize(
List<Rule> rules, Optional<Integer> formatVersion, OutputStream outputStream) {
// TODO: Implement stream serializer.
List<Rule> rules, Optional<Integer> formatVersion, OutputStream outputStream)
throws RuleSerializeException {
try {
BitOutputStream bitOutputStream = new BitOutputStream();
int formatVersionValue = formatVersion.orElse(DEFAULT_FORMAT_VERSION);
bitOutputStream.setNext(FORMAT_VERSION_BITS, formatVersionValue);
outputStream.write(bitOutputStream.toByteArray());
for (Rule rule : rules) {
bitOutputStream.clear();
serializeRule(rule, bitOutputStream);
outputStream.write(bitOutputStream.toByteArray());
}
} catch (Exception e) {
throw new RuleSerializeException(e.getMessage(), e);
}
}
// Get the byte representation for a list of rules.
@Override
public byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion) {
// TODO: Implement text serializer.
return null;
public byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion)
throws RuleSerializeException {
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
serialize(rules, formatVersion, byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
} catch (Exception e) {
throw new RuleSerializeException(e.getMessage(), e);
}
}
private void serializeRule(Rule rule, BitOutputStream bitOutputStream) {
if (rule == null) {
throw new IllegalArgumentException("Null rule can not be serialized");
}
// Start with a '1' bit to mark the start of a rule.
bitOutputStream.setNext();
serializeFormula(rule.getFormula(), bitOutputStream);
bitOutputStream.setNext(EFFECT_BITS, rule.getEffect());
// End with a '1' bit to mark the end of a rule.
bitOutputStream.setNext();
}
private void serializeFormula(Formula formula, BitOutputStream bitOutputStream) {
if (formula instanceof AtomicFormula) {
serializeAtomicFormula((AtomicFormula) formula, bitOutputStream);
} else if (formula instanceof CompoundFormula) {
serializeCompoundFormula((CompoundFormula) formula, bitOutputStream);
} else {
throw new IllegalArgumentException(
String.format("Invalid formula type: %s", formula.getClass()));
}
}
private void serializeCompoundFormula(
CompoundFormula compoundFormula, BitOutputStream bitOutputStream) {
if (compoundFormula == null) {
throw new IllegalArgumentException("Null compound formula can not be serialized");
}
bitOutputStream.setNext(SEPARATOR_BITS, COMPOUND_FORMULA_START);
bitOutputStream.setNext(CONNECTOR_BITS, compoundFormula.getConnector());
for (Formula formula : compoundFormula.getFormulas()) {
serializeFormula(formula, bitOutputStream);
}
bitOutputStream.setNext(SEPARATOR_BITS, COMPOUND_FORMULA_END);
}
private void serializeAtomicFormula(
AtomicFormula atomicFormula, BitOutputStream bitOutputStream) {
if (atomicFormula == null) {
throw new IllegalArgumentException("Null atomic formula can not be serialized");
}
bitOutputStream.setNext(SEPARATOR_BITS, ATOMIC_FORMULA_START);
bitOutputStream.setNext(KEY_BITS, atomicFormula.getKey());
if (atomicFormula instanceof AtomicFormula.StringAtomicFormula) {
AtomicFormula.StringAtomicFormula stringAtomicFormula =
(AtomicFormula.StringAtomicFormula) atomicFormula;
bitOutputStream.setNext(OPERATOR_BITS, AtomicFormula.EQ);
serializeValue(
stringAtomicFormula.getValue(),
stringAtomicFormula.getIsHashedValue(),
bitOutputStream);
} else if (atomicFormula instanceof AtomicFormula.IntAtomicFormula) {
AtomicFormula.IntAtomicFormula intAtomicFormula =
(AtomicFormula.IntAtomicFormula) atomicFormula;
bitOutputStream.setNext(OPERATOR_BITS, intAtomicFormula.getOperator());
serializeValue(
String.valueOf(intAtomicFormula.getValue()),
/* isHashedValue= */ false,
bitOutputStream);
} else if (atomicFormula instanceof AtomicFormula.BooleanAtomicFormula) {
AtomicFormula.BooleanAtomicFormula booleanAtomicFormula =
(AtomicFormula.BooleanAtomicFormula) atomicFormula;
bitOutputStream.setNext(OPERATOR_BITS, AtomicFormula.EQ);
serializeValue(
booleanAtomicFormula.getValue() ? "1" : "0",
/* isHashedValue= */ false,
bitOutputStream);
} else {
throw new IllegalArgumentException(
String.format("Invalid atomic formula type: %s", atomicFormula.getClass()));
}
}
private void serializeValue(
String value, boolean isHashedValue, BitOutputStream bitOutputStream) {
byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8);
bitOutputStream.setNext(isHashedValue);
bitOutputStream.setNext(VALUE_SIZE_BITS, valueBytes.length);
for (byte valueByte : valueBytes) {
bitOutputStream.setNext(/* numOfBits= */ 8, valueByte);
}
}
}

View File

@@ -39,7 +39,7 @@ public class RuleXmlSerializer implements RuleSerializer {
private static final String RULE_LIST_TAG = "RL";
private static final String RULE_TAG = "R";
private static final String OPEN_FORMULA_TAG = "OF";
private static final String COMPOUND_FORMULA_TAG = "OF";
private static final String ATOMIC_FORMULA_TAG = "AF";
private static final String EFFECT_ATTRIBUTE = "E";
private static final String KEY_ATTRIBUTE = "K";
@@ -78,13 +78,13 @@ public class RuleXmlSerializer implements RuleSerializer {
private void serializeRules(List<Rule> rules, XmlSerializer xmlSerializer) throws IOException {
xmlSerializer.startTag(NAMESPACE, RULE_LIST_TAG);
for (Rule rule : rules) {
serialize(rule, xmlSerializer);
serializeRule(rule, xmlSerializer);
}
xmlSerializer.endTag(NAMESPACE, RULE_LIST_TAG);
xmlSerializer.endDocument();
}
private void serialize(Rule rule, XmlSerializer xmlSerializer) throws IOException {
private void serializeRule(Rule rule, XmlSerializer xmlSerializer) throws IOException {
if (rule == null) {
return;
}
@@ -98,25 +98,25 @@ public class RuleXmlSerializer implements RuleSerializer {
if (formula instanceof AtomicFormula) {
serializeAtomicFormula((AtomicFormula) formula, xmlSerializer);
} else if (formula instanceof CompoundFormula) {
serializeOpenFormula((CompoundFormula) formula, xmlSerializer);
serializeCompoundFormula((CompoundFormula) formula, xmlSerializer);
} else {
throw new IllegalArgumentException(
String.format("Invalid formula type: %s", formula.getClass()));
}
}
private void serializeOpenFormula(CompoundFormula compoundFormula, XmlSerializer xmlSerializer)
throws IOException {
private void serializeCompoundFormula(
CompoundFormula compoundFormula, XmlSerializer xmlSerializer) throws IOException {
if (compoundFormula == null) {
return;
}
xmlSerializer.startTag(NAMESPACE, OPEN_FORMULA_TAG);
xmlSerializer.startTag(NAMESPACE, COMPOUND_FORMULA_TAG);
serializeAttributeValue(
CONNECTOR_ATTRIBUTE, String.valueOf(compoundFormula.getConnector()), xmlSerializer);
for (Formula formula : compoundFormula.getFormulas()) {
serializeFormula(formula, xmlSerializer);
}
xmlSerializer.endTag(NAMESPACE, OPEN_FORMULA_TAG);
xmlSerializer.endTag(NAMESPACE, COMPOUND_FORMULA_TAG);
}
private void serializeAtomicFormula(AtomicFormula atomicFormula, XmlSerializer xmlSerializer)

View File

@@ -0,0 +1,445 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.integrity.serializer;
import static com.android.server.integrity.utils.TestUtils.getBits;
import static com.android.server.integrity.utils.TestUtils.getBytes;
import static com.android.server.integrity.utils.TestUtils.getValueBits;
import static com.android.server.testutils.TestUtils.assertExpectException;
import static com.google.common.truth.Truth.assertThat;
import android.content.integrity.AppInstallMetadata;
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
import android.content.integrity.Formula;
import android.content.integrity.Rule;
import androidx.annotation.NonNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
@RunWith(JUnit4.class)
public class RuleBinarySerializerTest {
private static final String COMPOUND_FORMULA_START =
getBits(
RuleBinarySerializer.COMPOUND_FORMULA_START,
RuleBinarySerializer.SEPARATOR_BITS);
private static final String COMPOUND_FORMULA_END =
getBits(RuleBinarySerializer.COMPOUND_FORMULA_END, RuleBinarySerializer.SEPARATOR_BITS);
private static final String ATOMIC_FORMULA_START =
getBits(RuleBinarySerializer.ATOMIC_FORMULA_START, RuleBinarySerializer.SEPARATOR_BITS);
private static final String NOT =
getBits(CompoundFormula.NOT, RuleBinarySerializer.CONNECTOR_BITS);
private static final String AND =
getBits(CompoundFormula.AND, RuleBinarySerializer.CONNECTOR_BITS);
private static final String OR =
getBits(CompoundFormula.OR, RuleBinarySerializer.CONNECTOR_BITS);
private static final String PACKAGE_NAME =
getBits(AtomicFormula.PACKAGE_NAME, RuleBinarySerializer.KEY_BITS);
private static final String APP_CERTIFICATE =
getBits(AtomicFormula.APP_CERTIFICATE, RuleBinarySerializer.KEY_BITS);
private static final String VERSION_CODE =
getBits(AtomicFormula.VERSION_CODE, RuleBinarySerializer.KEY_BITS);
private static final String PRE_INSTALLED =
getBits(AtomicFormula.PRE_INSTALLED, RuleBinarySerializer.KEY_BITS);
private static final String EQ = getBits(AtomicFormula.EQ, RuleBinarySerializer.OPERATOR_BITS);
private static final String IS_NOT_HASHED = "0";
private static final String DENY = getBits(Rule.DENY, RuleBinarySerializer.EFFECT_BITS);
private static final String START_BIT = "1";
private static final String END_BIT = "1";
private static final byte[] DEFAULT_FORMAT_VERSION =
getBytes(
getBits(
RuleBinarySerializer.DEFAULT_FORMAT_VERSION,
RuleBinarySerializer.FORMAT_VERSION_BITS));
@Test
public void testBinaryString_serializeEmptyRule() throws Exception {
Rule rule = null;
RuleSerializer binarySerializer = new RuleBinarySerializer();
assertExpectException(
RuleSerializeException.class,
/* expectedExceptionMessageRegex= */ "Null rule can not be serialized",
() ->
binarySerializer.serialize(
Collections.singletonList(rule),
/* formatVersion= */ Optional.empty()));
}
@Test
public void testBinaryStream_serializeValidCompoundFormula() throws Exception {
String packageName = "com.test.app";
Rule rule =
new Rule(
new CompoundFormula(
CompoundFormula.NOT,
Collections.singletonList(
new AtomicFormula.StringAtomicFormula(
AtomicFormula.PACKAGE_NAME,
packageName,
/* isHashedValue= */ false))),
Rule.DENY);
RuleSerializer binarySerializer = new RuleBinarySerializer();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
String expectedBits =
START_BIT
+ COMPOUND_FORMULA_START
+ NOT
+ ATOMIC_FORMULA_START
+ PACKAGE_NAME
+ EQ
+ IS_NOT_HASHED
+ getBits(packageName.length(), RuleBinarySerializer.VALUE_SIZE_BITS)
+ getValueBits(packageName)
+ COMPOUND_FORMULA_END
+ DENY
+ END_BIT;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION);
byteArrayOutputStream.write(getBytes(expectedBits));
byte[] expectedRules = byteArrayOutputStream.toByteArray();
binarySerializer.serialize(
Collections.singletonList(rule),
/* formatVersion= */ Optional.empty(),
outputStream);
byte[] actualRules = outputStream.toByteArray();
assertThat(actualRules).isEqualTo(expectedRules);
}
@Test
public void testBinaryString_serializeValidCompoundFormula_notConnector() throws Exception {
String packageName = "com.test.app";
Rule rule =
new Rule(
new CompoundFormula(
CompoundFormula.NOT,
Collections.singletonList(
new AtomicFormula.StringAtomicFormula(
AtomicFormula.PACKAGE_NAME,
packageName,
/* isHashedValue= */ false))),
Rule.DENY);
RuleSerializer binarySerializer = new RuleBinarySerializer();
String expectedBits =
START_BIT
+ COMPOUND_FORMULA_START
+ NOT
+ ATOMIC_FORMULA_START
+ PACKAGE_NAME
+ EQ
+ IS_NOT_HASHED
+ getBits(packageName.length(), RuleBinarySerializer.VALUE_SIZE_BITS)
+ getValueBits(packageName)
+ COMPOUND_FORMULA_END
+ DENY
+ END_BIT;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION);
byteArrayOutputStream.write(getBytes(expectedBits));
byte[] expectedRules = byteArrayOutputStream.toByteArray();
byte[] actualRules =
binarySerializer.serialize(
Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
assertThat(actualRules).isEqualTo(expectedRules);
}
@Test
public void testBinaryString_serializeValidCompoundFormula_andConnector() throws Exception {
String packageName = "com.test.app";
String appCertificate = "test_cert";
Rule rule =
new Rule(
new CompoundFormula(
CompoundFormula.AND,
Arrays.asList(
new AtomicFormula.StringAtomicFormula(
AtomicFormula.PACKAGE_NAME,
packageName,
/* isHashedValue= */ false),
new AtomicFormula.StringAtomicFormula(
AtomicFormula.APP_CERTIFICATE,
appCertificate,
/* isHashedValue= */ false))),
Rule.DENY);
RuleSerializer binarySerializer = new RuleBinarySerializer();
String expectedBits =
START_BIT
+ COMPOUND_FORMULA_START
+ AND
+ ATOMIC_FORMULA_START
+ PACKAGE_NAME
+ EQ
+ IS_NOT_HASHED
+ getBits(packageName.length(), RuleBinarySerializer.VALUE_SIZE_BITS)
+ getValueBits(packageName)
+ ATOMIC_FORMULA_START
+ APP_CERTIFICATE
+ EQ
+ IS_NOT_HASHED
+ getBits(appCertificate.length(), RuleBinarySerializer.VALUE_SIZE_BITS)
+ getValueBits(appCertificate)
+ COMPOUND_FORMULA_END
+ DENY
+ END_BIT;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION);
byteArrayOutputStream.write(getBytes(expectedBits));
byte[] expectedRules = byteArrayOutputStream.toByteArray();
byte[] actualRules =
binarySerializer.serialize(
Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
assertThat(actualRules).isEqualTo(expectedRules);
}
@Test
public void testBinaryString_serializeValidCompoundFormula_orConnector() throws Exception {
String packageName = "com.test.app";
String appCertificate = "test_cert";
Rule rule =
new Rule(
new CompoundFormula(
CompoundFormula.OR,
Arrays.asList(
new AtomicFormula.StringAtomicFormula(
AtomicFormula.PACKAGE_NAME,
packageName,
/* isHashedValue= */ false),
new AtomicFormula.StringAtomicFormula(
AtomicFormula.APP_CERTIFICATE,
appCertificate,
/* isHashedValue= */ false))),
Rule.DENY);
RuleSerializer binarySerializer = new RuleBinarySerializer();
String expectedBits =
START_BIT
+ COMPOUND_FORMULA_START
+ OR
+ ATOMIC_FORMULA_START
+ PACKAGE_NAME
+ EQ
+ IS_NOT_HASHED
+ getBits(packageName.length(), RuleBinarySerializer.VALUE_SIZE_BITS)
+ getValueBits(packageName)
+ ATOMIC_FORMULA_START
+ APP_CERTIFICATE
+ EQ
+ IS_NOT_HASHED
+ getBits(appCertificate.length(), RuleBinarySerializer.VALUE_SIZE_BITS)
+ getValueBits(appCertificate)
+ COMPOUND_FORMULA_END
+ DENY
+ END_BIT;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION);
byteArrayOutputStream.write(getBytes(expectedBits));
byte[] expectedRules = byteArrayOutputStream.toByteArray();
byte[] actualRules =
binarySerializer.serialize(
Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
assertThat(actualRules).isEqualTo(expectedRules);
}
@Test
public void testBinaryString_serializeValidAtomicFormula_stringValue() throws Exception {
String packageName = "com.test.app";
Rule rule =
new Rule(
new AtomicFormula.StringAtomicFormula(
AtomicFormula.PACKAGE_NAME,
packageName,
/* isHashedValue= */ false),
Rule.DENY);
RuleSerializer binarySerializer = new RuleBinarySerializer();
String expectedBits =
START_BIT
+ ATOMIC_FORMULA_START
+ PACKAGE_NAME
+ EQ
+ IS_NOT_HASHED
+ getBits(packageName.length(), RuleBinarySerializer.VALUE_SIZE_BITS)
+ getValueBits(packageName)
+ DENY
+ END_BIT;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION);
byteArrayOutputStream.write(getBytes(expectedBits));
byte[] expectedRules = byteArrayOutputStream.toByteArray();
byte[] actualRules =
binarySerializer.serialize(
Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
assertThat(actualRules).isEqualTo(expectedRules);
}
@Test
public void testBinaryString_serializeValidAtomicFormula_integerValue() throws Exception {
String versionCode = "1";
Rule rule =
new Rule(
new AtomicFormula.IntAtomicFormula(
AtomicFormula.VERSION_CODE,
AtomicFormula.EQ,
Integer.parseInt(versionCode)),
Rule.DENY);
RuleSerializer binarySerializer = new RuleBinarySerializer();
String expectedBits =
START_BIT
+ ATOMIC_FORMULA_START
+ VERSION_CODE
+ EQ
+ IS_NOT_HASHED
+ getBits(versionCode.length(), RuleBinarySerializer.VALUE_SIZE_BITS)
+ getValueBits(versionCode)
+ DENY
+ END_BIT;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION);
byteArrayOutputStream.write(getBytes(expectedBits));
byte[] expectedRules = byteArrayOutputStream.toByteArray();
byte[] actualRules =
binarySerializer.serialize(
Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
assertThat(actualRules).isEqualTo(expectedRules);
}
@Test
public void testBinaryString_serializeValidAtomicFormula_booleanValue() throws Exception {
String preInstalled = "1";
Rule rule =
new Rule(
new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
Rule.DENY);
RuleSerializer binarySerializer = new RuleBinarySerializer();
String expectedBits =
START_BIT
+ ATOMIC_FORMULA_START
+ PRE_INSTALLED
+ EQ
+ IS_NOT_HASHED
+ getBits(preInstalled.length(), RuleBinarySerializer.VALUE_SIZE_BITS)
+ getValueBits(preInstalled)
+ DENY
+ END_BIT;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION);
byteArrayOutputStream.write(getBytes(expectedBits));
byte[] expectedRules = byteArrayOutputStream.toByteArray();
byte[] actualRules =
binarySerializer.serialize(
Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
assertThat(actualRules).isEqualTo(expectedRules);
}
@Test
public void testBinaryString_serializeInvalidFormulaType() throws Exception {
Formula invalidFormula = getInvalidFormula();
Rule rule = new Rule(invalidFormula, Rule.DENY);
RuleSerializer binarySerializer = new RuleBinarySerializer();
assertExpectException(
RuleSerializeException.class,
/* expectedExceptionMessageRegex= */ "Invalid formula type",
() ->
binarySerializer.serialize(
Collections.singletonList(rule),
/* formatVersion= */ Optional.empty()));
}
@Test
public void testBinaryString_serializeFormatVersion() throws Exception {
int formatVersion = 1;
RuleSerializer binarySerializer = new RuleBinarySerializer();
String expectedBits = getBits(formatVersion, RuleBinarySerializer.FORMAT_VERSION_BITS);
byte[] expectedRules = getBytes(expectedBits);
byte[] actualRules =
binarySerializer.serialize(
Collections.emptyList(), /* formatVersion= */ Optional.of(formatVersion));
assertThat(actualRules).isEqualTo(expectedRules);
}
private static Formula getInvalidFormula() {
return new Formula() {
@Override
public boolean isSatisfied(AppInstallMetadata appInstallMetadata) {
return false;
}
@Override
public int getTag() {
return 0;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
@NonNull
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return super.toString();
}
@Override
protected void finalize() throws Throwable {
super.finalize();
}
};
}
}

View File

@@ -91,7 +91,7 @@ public class RuleXmlSerializerTest {
}
@Test
public void testXmlStream_serializeValidOpenFormula() throws Exception {
public void testXmlStream_serializeValidCompoundFormula() throws Exception {
Rule rule =
new Rule(
new CompoundFormula(
@@ -134,7 +134,7 @@ public class RuleXmlSerializerTest {
}
@Test
public void testXmlString_serializeValidOpenFormula_notConnector() throws Exception {
public void testXmlString_serializeValidCompoundFormula_notConnector() throws Exception {
Rule rule =
new Rule(
new CompoundFormula(
@@ -174,7 +174,7 @@ public class RuleXmlSerializerTest {
}
@Test
public void testXmlString_serializeValidOpenFormula_andConnector() throws Exception {
public void testXmlString_serializeValidCompoundFormula_andConnector() throws Exception {
Rule rule =
new Rule(
new CompoundFormula(
@@ -224,7 +224,7 @@ public class RuleXmlSerializerTest {
}
@Test
public void testXmlString_serializeValidOpenFormula_orConnector() throws Exception {
public void testXmlString_serializeValidCompoundFormula_orConnector() throws Exception {
Rule rule =
new Rule(
new CompoundFormula(

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.integrity.utils;
public class TestUtils {
public static String getBits(int component, int numOfBits) {
return String.format("%" + numOfBits + "s", Integer.toBinaryString(component))
.replace(' ', '0');
}
public static String getValueBits(String value) {
StringBuilder stringBuilder = new StringBuilder();
for (byte valueByte : value.getBytes()) {
stringBuilder.append(getBits(valueByte, /* numOfBits= */ 8));
}
return stringBuilder.toString();
}
public static byte[] getBytes(String bits) {
int bitStringSize = bits.length();
int numOfBytes = bitStringSize / 8;
if (bitStringSize % 8 != 0) {
numOfBytes++;
}
byte[] bytes = new byte[numOfBytes];
for (int i = 0; i < bits.length(); i++) {
if (bits.charAt(i) == '1') {
bytes[i / 8] |= 1 << (7 - (i % 8));
}
}
return bytes;
}
}