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

Commit 789f6309 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android (Google) Code Review
Browse files

Merge "Implement per-field matching of ScanRecord." into sc-dev

parents df6594a6 ebd19b81
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

/**
 * Represents a scan record from Bluetooth LE scan.
@@ -168,6 +169,27 @@ public final class ScanRecord {
        return mBytes;
    }

    /**
     * Test if any fields contained inside this scan record are matched by the
     * given matcher.
     *
     * @hide
     */
    public boolean matchesAnyField(@NonNull Predicate<byte[]> matcher) {
        int pos = 0;
        while (pos < mBytes.length) {
            final int length = mBytes[pos] & 0xFF;
            if (length == 0) {
                break;
            }
            if (matcher.test(Arrays.copyOfRange(mBytes, pos, pos + length + 1))) {
                return true;
            }
            pos += length + 1;
        }
        return false;
    }

    private ScanRecord(List<ParcelUuid> serviceUuids,
            List<ParcelUuid> serviceSolicitationUuids,
            SparseArray<byte[]> manufacturerData,
+32 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 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.
-->
<configuration description="Config for Bluetooth test cases">
    <option name="test-suite-tag" value="apct"/>
    <option name="test-suite-tag" value="apct-instrumentation"/>
    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
        <option name="cleanup-apks" value="true" />
        <option name="test-file-name" value="BluetoothTests.apk" />
    </target_preparer>

    <option name="test-suite-tag" value="apct"/>
    <option name="test-tag" value="BluetoothTests"/>

    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
        <option name="package" value="com.android.bluetooth.tests" />
        <option name="hidden-api-checks" value="false"/>
        <option name="runner" value="android.bluetooth.BluetoothTestRunner"/>
    </test>
</configuration>
+76 −1
Original line number Diff line number Diff line
@@ -16,13 +16,18 @@

package android.bluetooth.le;

import android.bluetooth.le.ScanRecord;
import android.os.BytesMatcher;
import android.os.ParcelUuid;
import android.test.suitebuilder.annotation.SmallTest;

import com.android.internal.util.HexDump;

import junit.framework.TestCase;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
 * Unit test cases for {@link ScanRecord}.
@@ -31,6 +36,66 @@ import java.util.Arrays;
 * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
 */
public class ScanRecordTest extends TestCase {
    /**
     * Example raw beacons captured from a Blue Charm BC011
     */
    private static final String RECORD_URL = "0201060303AAFE1716AAFE10EE01626C7565636861726D626561636F6E730009168020691E0EFE13551109426C7565436861726D5F313639363835000000";
    private static final String RECORD_UUID = "0201060303AAFE1716AAFE00EE626C7565636861726D31000000000001000009168020691E0EFE13551109426C7565436861726D5F313639363835000000";
    private static final String RECORD_TLM = "0201060303AAFE1116AAFE20000BF017000008874803FB93540916802069080EFE13551109426C7565436861726D5F313639363835000000000000000000";
    private static final String RECORD_IBEACON = "0201061AFF4C000215426C7565436861726D426561636F6E730EFE1355C509168020691E0EFE13551109426C7565436861726D5F31363936383500000000";

    @SmallTest
    public void testMatchesAnyField_Eddystone_Parser() {
        final List<String> found = new ArrayList<>();
        final Predicate<byte[]> matcher = (v) -> {
            found.add(HexDump.toHexString(v));
            return false;
        };
        ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(RECORD_URL))
                .matchesAnyField(matcher);

        assertEquals(Arrays.asList(
                "020106",
                "0303AAFE",
                "1716AAFE10EE01626C7565636861726D626561636F6E7300",
                "09168020691E0EFE1355",
                "1109426C7565436861726D5F313639363835"), found);
    }

    @SmallTest
    public void testMatchesAnyField_Eddystone() {
        final BytesMatcher matcher = BytesMatcher.decode("⊆0016AAFE/00FFFFFF");
        assertMatchesAnyField(RECORD_URL, matcher);
        assertMatchesAnyField(RECORD_UUID, matcher);
        assertMatchesAnyField(RECORD_TLM, matcher);
        assertNotMatchesAnyField(RECORD_IBEACON, matcher);
    }

    @SmallTest
    public void testMatchesAnyField_iBeacon_Parser() {
        final List<String> found = new ArrayList<>();
        final Predicate<byte[]> matcher = (v) -> {
            found.add(HexDump.toHexString(v));
            return false;
        };
        ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(RECORD_IBEACON))
                .matchesAnyField(matcher);

        assertEquals(Arrays.asList(
                "020106",
                "1AFF4C000215426C7565436861726D426561636F6E730EFE1355C5",
                "09168020691E0EFE1355",
                "1109426C7565436861726D5F313639363835"), found);
    }

    @SmallTest
    public void testMatchesAnyField_iBeacon() {
        final BytesMatcher matcher = BytesMatcher.decode("⊆00FF4C0002/00FFFFFFFF");
        assertNotMatchesAnyField(RECORD_URL, matcher);
        assertNotMatchesAnyField(RECORD_UUID, matcher);
        assertNotMatchesAnyField(RECORD_TLM, matcher);
        assertMatchesAnyField(RECORD_IBEACON, matcher);
    }

    @SmallTest
    public void testParser() {
@@ -70,4 +135,14 @@ public class ScanRecordTest extends TestCase {
        }

    }

    private static void assertMatchesAnyField(String record, BytesMatcher matcher) {
        assertTrue(ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(record))
                .matchesAnyField(matcher));
    }

    private static void assertNotMatchesAnyField(String record, BytesMatcher matcher) {
        assertFalse(ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(record))
                .matchesAnyField(matcher));
    }
}