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

Commit 1fa4de58 authored by Khaled Abdelmohsen's avatar Khaled Abdelmohsen
Browse files

Implement rule binary serializer

Bug: 143697198
Test: atest FrameworksServicesTests:RuleBinarySerializerTest
Change-Id: Ia804137d8b127a3b2ef8a7b692b7903360fdbe8c
parent 99d990ec
Loading
Loading
Loading
Loading
+86 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -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;
    }
+139 −6
Original line number Diff line number Diff line
@@ -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);
        }
    }
}
+8 −8
Original line number Diff line number Diff line
@@ -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)
+445 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading