Merge "Remove bytes matcher from base"
diff --git a/core/java/android/os/BytesMatcher.java b/core/java/android/os/BytesMatcher.java
deleted file mode 100644
index 8974c5e..0000000
--- a/core/java/android/os/BytesMatcher.java
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright (C) 2021 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 android.os;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.bluetooth.BluetoothUuid;
-import android.net.MacAddress;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.util.HexDump;
-
-import java.util.ArrayList;
-import java.util.function.Predicate;
-
-/**
- * Predicate that tests if a given {@code byte[]} value matches a set of
- * configured rules.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader rule
- * might accept that same value, or vice versa.
- * <p>
- * Matchers can contain rules of varying lengths, and tested values will only be
- * matched against rules of the exact same length. This is designed to support
- * {@link BluetoothUuid} style values which can be variable length.
- *
- * @hide
- */
-public class BytesMatcher implements Predicate<byte[]> {
- private static final String TAG = "BytesMatcher";
-
- private static final char TYPE_EXACT_ACCEPT = '+';
- private static final char TYPE_EXACT_REJECT = '-';
- private static final char TYPE_PREFIX_ACCEPT = '⊆';
- private static final char TYPE_PREFIX_REJECT = '⊈';
-
- private final ArrayList<Rule> mRules = new ArrayList<>();
-
- private static class Rule {
- public final char type;
- public final @NonNull byte[] value;
- public final @Nullable byte[] mask;
-
- public Rule(char type, @NonNull byte[] value, @Nullable byte[] mask) {
- if (mask != null && value.length != mask.length) {
- throw new IllegalArgumentException(
- "Expected length " + value.length + " but found " + mask.length);
- }
- this.type = type;
- this.value = value;
- this.mask = mask;
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- encode(builder);
- return builder.toString();
- }
-
- public void encode(@NonNull StringBuilder builder) {
- builder.append(type);
- builder.append(HexDump.toHexString(value));
- if (mask != null) {
- builder.append('/');
- builder.append(HexDump.toHexString(mask));
- }
- }
-
- public boolean test(@NonNull byte[] value) {
- switch (type) {
- case TYPE_EXACT_ACCEPT:
- case TYPE_EXACT_REJECT:
- if (value.length != this.value.length) {
- return false;
- }
- break;
- case TYPE_PREFIX_ACCEPT:
- case TYPE_PREFIX_REJECT:
- if (value.length < this.value.length) {
- return false;
- }
- break;
- }
- for (int i = 0; i < this.value.length; i++) {
- byte local = this.value[i];
- byte remote = value[i];
- if (this.mask != null) {
- local &= this.mask[i];
- remote &= this.mask[i];
- }
- if (local != remote) {
- return false;
- }
- }
- return true;
- }
- }
-
- /**
- * Add a rule that will result in {@link #test(byte[])} returning
- * {@code true} when a value being tested matches it. This rule will only
- * match values of the exact same length.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader
- * rule might accept that same value, or vice versa.
- *
- * @param value to be matched
- * @param mask to be applied to both values before testing for equality; if
- * {@code null} then both values must match exactly
- */
- public void addExactAcceptRule(@NonNull byte[] value, @Nullable byte[] mask) {
- mRules.add(new Rule(TYPE_EXACT_ACCEPT, value, mask));
- }
-
- /**
- * Add a rule that will result in {@link #test(byte[])} returning
- * {@code false} when a value being tested matches it. This rule will only
- * match values of the exact same length.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader
- * rule might accept that same value, or vice versa.
- *
- * @param value to be matched
- * @param mask to be applied to both values before testing for equality; if
- * {@code null} then both values must match exactly
- */
- public void addExactRejectRule(@NonNull byte[] value, @Nullable byte[] mask) {
- mRules.add(new Rule(TYPE_EXACT_REJECT, value, mask));
- }
-
- /**
- * Add a rule that will result in {@link #test(byte[])} returning
- * {@code true} when a value being tested matches it. This rule will match
- * values of the exact same length or longer.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader
- * rule might accept that same value, or vice versa.
- *
- * @param value to be matched
- * @param mask to be applied to both values before testing for equality; if
- * {@code null} then both values must match exactly
- */
- public void addPrefixAcceptRule(@NonNull byte[] value, @Nullable byte[] mask) {
- mRules.add(new Rule(TYPE_PREFIX_ACCEPT, value, mask));
- }
-
- /**
- * Add a rule that will result in {@link #test(byte[])} returning
- * {@code false} when a value being tested matches it. This rule will match
- * values of the exact same length or longer.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader
- * rule might accept that same value, or vice versa.
- *
- * @param value to be matched
- * @param mask to be applied to both values before testing for equality; if
- * {@code null} then both values must match exactly
- */
- public void addPrefixRejectRule(@NonNull byte[] value, @Nullable byte[] mask) {
- mRules.add(new Rule(TYPE_PREFIX_REJECT, value, mask));
- }
-
- /**
- * Test if the given {@code ParcelUuid} value matches the set of rules
- * configured in this matcher.
- */
- public boolean testBluetoothUuid(@NonNull ParcelUuid value) {
- return test(BluetoothUuid.uuidToBytes(value));
- }
-
- /**
- * Test if the given {@code MacAddress} value matches the set of rules
- * configured in this matcher.
- */
- public boolean testMacAddress(@NonNull MacAddress value) {
- return test(value.toByteArray());
- }
-
- /**
- * Test if the given {@code byte[]} value matches the set of rules
- * configured in this matcher.
- */
- @Override
- public boolean test(@NonNull byte[] value) {
- return test(value, false);
- }
-
- /**
- * Test if the given {@code byte[]} value matches the set of rules
- * configured in this matcher.
- */
- public boolean test(@NonNull byte[] value, boolean defaultValue) {
- final int size = mRules.size();
- for (int i = 0; i < size; i++) {
- final Rule rule = mRules.get(i);
- if (rule.test(value)) {
- switch (rule.type) {
- case TYPE_EXACT_ACCEPT:
- case TYPE_PREFIX_ACCEPT:
- return true;
- case TYPE_EXACT_REJECT:
- case TYPE_PREFIX_REJECT:
- return false;
- }
- }
- }
- return defaultValue;
- }
-
- /**
- * Encode the given matcher into a human-readable {@link String} which can
- * be used to transport matchers across device boundaries.
- * <p>
- * The human-readable format is an ordered list separated by commas, where
- * each rule is a {@code +} or {@code -} symbol indicating if the match
- * should be accepted or rejected, then followed by a hex value and an
- * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid
- * encoded matcher.
- *
- * @see #decode(String)
- */
- public static @NonNull String encode(@NonNull BytesMatcher matcher) {
- final StringBuilder builder = new StringBuilder();
- final int size = matcher.mRules.size();
- for (int i = 0; i < size; i++) {
- final Rule rule = matcher.mRules.get(i);
- rule.encode(builder);
- builder.append(',');
- }
- if (builder.length() > 0) {
- builder.deleteCharAt(builder.length() - 1);
- }
- return builder.toString();
- }
-
- /**
- * Decode the given human-readable {@link String} used to transport matchers
- * across device boundaries.
- * <p>
- * The human-readable format is an ordered list separated by commas, where
- * each rule is a {@code +} or {@code -} symbol indicating if the match
- * should be accepted or rejected, then followed by a hex value and an
- * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid
- * encoded matcher.
- *
- * @see #encode(BytesMatcher)
- */
- public static @NonNull BytesMatcher decode(@Nullable String value) {
- final BytesMatcher matcher = new BytesMatcher();
- if (TextUtils.isEmpty(value)) return matcher;
-
- final int length = value.length();
- for (int i = 0; i < length;) {
- final char type = value.charAt(i);
-
- int nextRule = value.indexOf(',', i);
- int nextMask = value.indexOf('/', i);
-
- if (nextRule == -1) nextRule = length;
- if (nextMask > nextRule) nextMask = -1;
-
- final byte[] ruleValue;
- final byte[] ruleMask;
- if (nextMask >= 0) {
- ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextMask));
- ruleMask = HexDump.hexStringToByteArray(value.substring(nextMask + 1, nextRule));
- } else {
- ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextRule));
- ruleMask = null;
- }
-
- switch (type) {
- case TYPE_EXACT_ACCEPT:
- matcher.addExactAcceptRule(ruleValue, ruleMask);
- break;
- case TYPE_EXACT_REJECT:
- matcher.addExactRejectRule(ruleValue, ruleMask);
- break;
- case TYPE_PREFIX_ACCEPT:
- matcher.addPrefixAcceptRule(ruleValue, ruleMask);
- break;
- case TYPE_PREFIX_REJECT:
- matcher.addPrefixRejectRule(ruleValue, ruleMask);
- break;
- default:
- Log.w(TAG, "Ignoring unknown type " + type);
- break;
- }
-
- i = nextRule + 1;
- }
- return matcher;
- }
-}
diff --git a/core/tests/bluetoothtests/Android.bp b/core/tests/bluetoothtests/Android.bp
index a2e4dff..68416dd 100644
--- a/core/tests/bluetoothtests/Android.bp
+++ b/core/tests/bluetoothtests/Android.bp
@@ -15,7 +15,10 @@
"android.test.runner",
"android.test.base",
],
- static_libs: ["junit"],
+ static_libs: [
+ "junit",
+ "modules-utils-bytesmatcher",
+ ],
platform_apis: true,
certificate: "platform",
}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
index c287ea9..4e817d4 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
@@ -16,11 +16,11 @@
package android.bluetooth.le;
-import android.os.BytesMatcher;
import android.os.ParcelUuid;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.util.HexDump;
+import com.android.modules.utils.BytesMatcher;
import junit.framework.TestCase;
diff --git a/core/tests/coretests/src/android/os/BytesMatcherTest.java b/core/tests/coretests/src/android/os/BytesMatcherTest.java
deleted file mode 100644
index b28e309..0000000
--- a/core/tests/coretests/src/android/os/BytesMatcherTest.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2021 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 android.os;
-
-import static com.android.internal.util.HexDump.hexStringToByteArray;
-
-import android.bluetooth.BluetoothUuid;
-import android.net.MacAddress;
-
-import androidx.test.filters.SmallTest;
-
-import junit.framework.TestCase;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-@SmallTest
-public class BytesMatcherTest extends TestCase {
- @Test
- public void testEmpty() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("");
- assertFalse(matcher.test(hexStringToByteArray("cafe")));
- assertFalse(matcher.test(hexStringToByteArray("")));
- }
-
- @Test
- public void testExact() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+cafe");
- assertTrue(matcher.test(hexStringToByteArray("cafe")));
- assertFalse(matcher.test(hexStringToByteArray("beef")));
- assertFalse(matcher.test(hexStringToByteArray("ca")));
- assertFalse(matcher.test(hexStringToByteArray("cafe00")));
- }
-
- @Test
- public void testMask() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+cafe/ff00");
- assertTrue(matcher.test(hexStringToByteArray("cafe")));
- assertTrue(matcher.test(hexStringToByteArray("ca88")));
- assertFalse(matcher.test(hexStringToByteArray("beef")));
- assertFalse(matcher.test(hexStringToByteArray("ca")));
- assertFalse(matcher.test(hexStringToByteArray("cafe00")));
- }
-
- @Test
- public void testPrefix() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("⊆cafe,⊆beef/ff00");
- assertTrue(matcher.test(hexStringToByteArray("cafe")));
- assertFalse(matcher.test(hexStringToByteArray("caff")));
- assertTrue(matcher.test(hexStringToByteArray("cafecafe")));
- assertFalse(matcher.test(hexStringToByteArray("ca")));
- assertTrue(matcher.test(hexStringToByteArray("beef")));
- assertTrue(matcher.test(hexStringToByteArray("beff")));
- assertTrue(matcher.test(hexStringToByteArray("beffbeff")));
- assertFalse(matcher.test(hexStringToByteArray("be")));
- }
-
- @Test
- public void testMacAddress() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+cafe00112233/ffffff000000");
- assertTrue(matcher.testMacAddress(
- MacAddress.fromString("ca:fe:00:00:00:00")));
- assertFalse(matcher.testMacAddress(
- MacAddress.fromString("f0:0d:00:00:00:00")));
- }
-
- @Test
- public void testBluetoothUuid() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+cafe/ff00");
- assertTrue(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("cafe"))));
- assertFalse(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("beef"))));
- }
-
- /**
- * Verify that single matcher can be configured to match Bluetooth UUIDs of
- * varying lengths.
- */
- @Test
- public void testBluetoothUuid_Mixed() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+aaaa/ff00,+bbbbbbbb/ffff0000");
- assertTrue(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("aaaa"))));
- assertFalse(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("bbbb"))));
- assertTrue(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("bbbbbbbb"))));
- assertFalse(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("aaaaaaaa"))));
- }
-
- @Test
- public void testSerialize_Empty() throws Exception {
- BytesMatcher matcher = new BytesMatcher();
- matcher = BytesMatcher.decode(BytesMatcher.encode(matcher));
-
- // Also very empty and null values
- BytesMatcher.decode("");
- BytesMatcher.decode(null);
- }
-
- @Test
- public void testSerialize_Exact() throws Exception {
- BytesMatcher matcher = new BytesMatcher();
- matcher.addExactRejectRule(hexStringToByteArray("cafe00112233"),
- hexStringToByteArray("ffffff000000"));
- matcher.addExactRejectRule(hexStringToByteArray("beef00112233"),
- null);
- matcher.addExactAcceptRule(hexStringToByteArray("000000000000"),
- hexStringToByteArray("000000000000"));
-
- assertFalse(matcher.test(hexStringToByteArray("cafe00ffffff")));
- assertFalse(matcher.test(hexStringToByteArray("beef00112233")));
- assertTrue(matcher.test(hexStringToByteArray("beef00ffffff")));
-
- // Bounce through serialization pass and confirm it still works
- matcher = BytesMatcher.decode(BytesMatcher.encode(matcher));
-
- assertFalse(matcher.test(hexStringToByteArray("cafe00ffffff")));
- assertFalse(matcher.test(hexStringToByteArray("beef00112233")));
- assertTrue(matcher.test(hexStringToByteArray("beef00ffffff")));
- }
-
- @Test
- public void testSerialize_Prefix() throws Exception {
- BytesMatcher matcher = new BytesMatcher();
- matcher.addExactRejectRule(hexStringToByteArray("aa"), null);
- matcher.addExactAcceptRule(hexStringToByteArray("bb"), null);
- matcher.addPrefixAcceptRule(hexStringToByteArray("aa"), null);
- matcher.addPrefixRejectRule(hexStringToByteArray("bb"), null);
-
- assertFalse(matcher.test(hexStringToByteArray("aa")));
- assertTrue(matcher.test(hexStringToByteArray("bb")));
- assertTrue(matcher.test(hexStringToByteArray("aaaa")));
- assertFalse(matcher.test(hexStringToByteArray("bbbb")));
-
- // Bounce through serialization pass and confirm it still works
- matcher = BytesMatcher.decode(BytesMatcher.encode(matcher));
-
- assertFalse(matcher.test(hexStringToByteArray("aa")));
- assertTrue(matcher.test(hexStringToByteArray("bb")));
- assertTrue(matcher.test(hexStringToByteArray("aaaa")));
- assertFalse(matcher.test(hexStringToByteArray("bbbb")));
- }
-
- @Test
- public void testOrdering_RejectFirst() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("-ff/0f,+ff/f0");
- assertFalse(matcher.test(hexStringToByteArray("ff")));
- assertTrue(matcher.test(hexStringToByteArray("f0")));
- assertFalse(matcher.test(hexStringToByteArray("0f")));
- }
-
- @Test
- public void testOrdering_AcceptFirst() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+ff/f0,-ff/0f");
- assertTrue(matcher.test(hexStringToByteArray("ff")));
- assertTrue(matcher.test(hexStringToByteArray("f0")));
- assertFalse(matcher.test(hexStringToByteArray("0f")));
- }
-}