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

Commit 01f7ece3 authored by Omer Nebil Yaveroglu's avatar Omer Nebil Yaveroglu
Browse files

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

Bug: 145493956
Test: atest FrameworksServicesTests:RuleIndexingControllerTest
Change-Id: I0bf4dca861725683fffdb14af175ae3eb4992d74
parent 4f5ceae9
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);
    }
}