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

Commit e3c53e70 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Identify the index ranges that needs to be read for a query."

parents 5ba7d903 01f7ece3
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.integrity.model.RuleMetadata;
import com.android.server.integrity.parser.RuleBinaryParser;
import com.android.server.integrity.parser.RuleIndexRange;
import com.android.server.integrity.parser.RuleIndexingController;
import com.android.server.integrity.parser.RuleMetadataParser;
import com.android.server.integrity.parser.RuleParseException;
@@ -152,7 +153,7 @@ public class IntegrityFileManager {
            throws IOException, RuleParseException {
        synchronized (RULES_LOCK) {
            // Try to identify indexes from the index file.
            List<List<Integer>> ruleReadingIndexes =
            List<RuleIndexRange> ruleReadingIndexes =
                    mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata);

            // Read the rules based on the index information.
+50 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.parser;

import android.annotation.Nullable;

/**
 * A wrapper class to represent an indexing range that is identified by the {@link
 * RuleIndexingController}.
 */
public class RuleIndexRange {
    private static int sStartIndex;
    private static int sEndIndex;

    /** Constructor with start and end indexes. */
    public RuleIndexRange(int startIndex, int endIndex) {
        this.sStartIndex = startIndex;
        this.sEndIndex = endIndex;
    }

    /** Returns the startIndex. */
    public int getStartIndex() {
        return sStartIndex;
    }

    /** Returns the end index. */
    public int getEndIndex() {
        return sEndIndex;
    }

    @Override
    public boolean equals(@Nullable Object object) {
        return sStartIndex == ((RuleIndexRange) object).getStartIndex()
                && sEndIndex == ((RuleIndexRange) object).getEndIndex();
    }
}
+49 −13
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.integrity.parser;

import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;

@@ -24,25 +25,27 @@ import android.content.integrity.AppInstallMetadata;

import com.android.server.integrity.model.BitInputStream;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors;

/** Helper class to identify the necessary indexes that needs to be read. */
public class RuleIndexingController {

    private static TreeMap<String, Integer> sPackageNameBasedIndexes;
    private static TreeMap<String, Integer> sAppCertificateBasedIndexes;
    private static TreeMap<String, Integer> sUnindexedRuleIndexes;
    private static LinkedHashMap<String, Integer> sPackageNameBasedIndexes;
    private static LinkedHashMap<String, Integer> sAppCertificateBasedIndexes;
    private static LinkedHashMap<String, Integer> sUnindexedRuleIndexes;

    /**
     * Provide the indexing file to read and the object will be constructed by reading and
     * identifying the indexes.
     */
    public RuleIndexingController(FileInputStream fileInputStream) throws IOException {
        BitInputStream bitInputStream = new BitInputStream(fileInputStream);
    public RuleIndexingController(InputStream inputStream) throws IOException {
        BitInputStream bitInputStream = new BitInputStream(inputStream);
        sPackageNameBasedIndexes = getNextIndexGroup(bitInputStream);
        sAppCertificateBasedIndexes = getNextIndexGroup(bitInputStream);
        sUnindexedRuleIndexes = getNextIndexGroup(bitInputStream);
@@ -52,24 +55,57 @@ public class RuleIndexingController {
     * Returns a list of integers with the starting and ending bytes of the rules that needs to be
     * read and evaluated.
     */
    public List<List<Integer>> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) {
        // TODO(b/145493956): Identify and return the indexes that needs to be read.
        return new ArrayList<>();
    public List<RuleIndexRange> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) {
        ArrayList<RuleIndexRange> indexRanges = new ArrayList();

        // Add the range for package name indexes rules.
        indexRanges.add(
                searchIndexingKeysRangeContainingKey(
                        sPackageNameBasedIndexes, appInstallMetadata.getPackageName()));

        // Add the range for app certificate indexes rules.
        indexRanges.add(
                searchIndexingKeysRangeContainingKey(
                        sAppCertificateBasedIndexes, appInstallMetadata.getAppCertificate()));

        // Add the range for unindexed rules.
        indexRanges.add(
                new RuleIndexRange(
                        sUnindexedRuleIndexes.get(START_INDEXING_KEY),
                        sUnindexedRuleIndexes.get(END_INDEXING_KEY)));

        return indexRanges;
    }

    private TreeMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream)
    private LinkedHashMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream)
            throws IOException {
        TreeMap<String, Integer> keyToIndexMap = new TreeMap<>();
        LinkedHashMap<String, Integer> keyToIndexMap = new LinkedHashMap<>();
        while (bitInputStream.hasNext()) {
            String key = getStringValue(bitInputStream);
            int value = getIntValue(bitInputStream);

            keyToIndexMap.put(key, value);

            if (key == END_INDEXING_KEY) {
            if (key.matches(END_INDEXING_KEY)) {
                break;
            }
        }
        return keyToIndexMap;
    }

    private RuleIndexRange searchIndexingKeysRangeContainingKey(
            LinkedHashMap<String, Integer> indexMap, String searchedKey) {
        TreeSet<String> keyTreeSet =
                indexMap.keySet().stream()
                        .filter(key -> !key.matches(START_INDEXING_KEY) && !key.matches(
                                END_INDEXING_KEY))
                        .collect(Collectors.toCollection(TreeSet::new));

        String minIndex = keyTreeSet.floor(searchedKey);
        String maxIndex = keyTreeSet.ceiling(searchedKey);

        return new RuleIndexRange(
                indexMap.get(minIndex == null ? START_INDEXING_KEY : minIndex),
                indexMap.get(maxIndex == null ? END_INDEXING_KEY : maxIndex));
    }
}
+168 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.parser;

import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
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.google.common.truth.Truth.assertThat;

import android.content.integrity.AppInstallMetadata;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.List;

@RunWith(JUnit4.class)
public class RuleIndexingControllerTest {

    @Test
    public void verifyIndexRangeSearchIsCorrect() throws IOException {
        InputStream inputStream = obtainDefaultIndexingMapForTest();

        RuleIndexingController indexingController = new RuleIndexingController(inputStream);

        AppInstallMetadata appInstallMetadata =
                new AppInstallMetadata.Builder()
                        .setPackageName("ddd")
                        .setAppCertificate("777")
                        .build();

        List<RuleIndexRange> resultingIndexes =
                indexingController.identifyRulesToEvaluate(appInstallMetadata);

        assertThat(resultingIndexes)
                .containsExactly(
                        new RuleIndexRange(200, 300),
                        new RuleIndexRange(700, 800),
                        new RuleIndexRange(900, 945));
    }

    @Test
    public void verifyIndexRangeSearchIsCorrect_keysInFirstAndLastBlock() throws IOException {
        InputStream inputStream = obtainDefaultIndexingMapForTest();

        RuleIndexingController indexingController = new RuleIndexingController(inputStream);

        AppInstallMetadata appInstallMetadata =
                new AppInstallMetadata.Builder()
                        .setPackageName("bbb")
                        .setAppCertificate("999")
                        .build();

        List<RuleIndexRange> resultingIndexes =
                indexingController.identifyRulesToEvaluate(appInstallMetadata);

        assertThat(resultingIndexes)
                .containsExactly(
                        new RuleIndexRange(100, 200),
                        new RuleIndexRange(800, 900),
                        new RuleIndexRange(900, 945));
    }

    @Test
    public void verifyIndexRangeSearchIsCorrect_keysMatchWithValues() throws IOException {
        InputStream inputStream = obtainDefaultIndexingMapForTest();

        RuleIndexingController indexingController = new RuleIndexingController(inputStream);

        AppInstallMetadata appInstallMetadata =
                new AppInstallMetadata.Builder()
                        .setPackageName("ccc")
                        .setAppCertificate("444")
                        .build();

        List<RuleIndexRange> resultingIndexes =
                indexingController.identifyRulesToEvaluate(appInstallMetadata);

        assertThat(resultingIndexes)
                .containsExactly(
                        new RuleIndexRange(200, 300),
                        new RuleIndexRange(700, 800),
                        new RuleIndexRange(900, 945));
    }

    @Test
    public void verifyIndexRangeSearchIsCorrect_noIndexesAvailable() throws IOException {
        byte[] stringBytes =
                getBytes(
                        getKeyValueString(START_INDEXING_KEY, 100)
                                + getKeyValueString(END_INDEXING_KEY, 500)
                                + getKeyValueString(START_INDEXING_KEY, 500)
                                + getKeyValueString(END_INDEXING_KEY, 900)
                                + getKeyValueString(START_INDEXING_KEY, 900)
                                + getKeyValueString(END_INDEXING_KEY, 945));
        ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
        rule.put(stringBytes);
        InputStream inputStream = new ByteArrayInputStream(rule.array());

        RuleIndexingController indexingController = new RuleIndexingController(inputStream);

        AppInstallMetadata appInstallMetadata =
                new AppInstallMetadata.Builder()
                        .setPackageName("ccc")
                        .setAppCertificate("444")
                        .build();

        List<RuleIndexRange> resultingIndexes =
                indexingController.identifyRulesToEvaluate(appInstallMetadata);

        assertThat(resultingIndexes)
                .containsExactly(
                        new RuleIndexRange(100, 500),
                        new RuleIndexRange(500, 900),
                        new RuleIndexRange(900, 945));
    }

    private static InputStream obtainDefaultIndexingMapForTest() {
        byte[] stringBytes =
                getBytes(
                        getKeyValueString(START_INDEXING_KEY, 100)
                                + getKeyValueString("ccc", 200)
                                + getKeyValueString("eee", 300)
                                + getKeyValueString("hhh", 400)
                                + getKeyValueString(END_INDEXING_KEY, 500)
                                + getKeyValueString(START_INDEXING_KEY, 500)
                                + getKeyValueString("111", 600)
                                + getKeyValueString("444", 700)
                                + getKeyValueString("888", 800)
                                + getKeyValueString(END_INDEXING_KEY, 900)
                                + getKeyValueString(START_INDEXING_KEY, 900)
                                + getKeyValueString(END_INDEXING_KEY, 945));
        ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
        rule.put(stringBytes);
        return new ByteArrayInputStream(rule.array());
    }

    private static String getKeyValueString(String key, int value) {
        String isNotHashed = "0";
        return isNotHashed
                + getBits(key.length(), VALUE_SIZE_BITS)
                + getValueBits(key)
                + getBits(value, /* numOfBits= */ 32);
    }
}