Loading framework/java/android/bluetooth/le/ScanRecord.java +22 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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, Loading framework/tests/AndroidTest.xml 0 → 100644 +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> framework/tests/src/android/bluetooth/le/ScanRecordTest.java +76 −1 Original line number Diff line number Diff line Loading @@ -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}. Loading @@ -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() { Loading Loading @@ -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)); } } Loading
framework/java/android/bluetooth/le/ScanRecord.java +22 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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, Loading
framework/tests/AndroidTest.xml 0 → 100644 +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>
framework/tests/src/android/bluetooth/le/ScanRecordTest.java +76 −1 Original line number Diff line number Diff line Loading @@ -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}. Loading @@ -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() { Loading Loading @@ -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)); } }