Loading android/app/src/com/android/bluetooth/le_scan/MsftAdvMonitor.java +77 −0 Original line number Diff line number Diff line Loading @@ -16,7 +16,28 @@ package com.android.bluetooth.le_scan; import android.bluetooth.le.ScanFilter; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.UUID; /** Helper class used to manage MSFT Advertisement Monitors. */ class MsftAdvMonitor { /* Only pattern filtering is supported currently */ // private static final int MSFT_CONDITION_TYPE_ALL = 0x00; private static final int MSFT_CONDITION_TYPE_PATTERNS = 0x01; // private static final int MSFT_CONDITION_TYPE_UUID = 0x02; // private static final int MSFT_CONDITION_TYPE_IRK = 0x03; // private static final int MSFT_CONDITION_TYPE_ADDRESS = 0x04; // Hardcoded values taken from CrOS defaults private static final byte RSSI_THRESHOLD_HIGH = (byte) 0xBF; // 191 private static final byte RSSI_THRESHOLD_LOW = (byte) 0xB0; // 176 private static final byte RSSI_THRESHOLD_LOW_TIME_INTERVAL = (byte) 0x28; // 40s private static final byte RSSI_SAMPLING_PERIOD = (byte) 0x05; // 500ms private static final int FILTER_PATTERN_START_POSITION = (byte) 0x00; static class Monitor { public byte rssi_threshold_high; public byte rssi_threshold_low; Loading @@ -35,4 +56,60 @@ class MsftAdvMonitor { byte addr_type; String bd_addr; } private final Monitor mMonitor = new Monitor(); private final ArrayList<Pattern> mPatterns = new ArrayList<>(); private final Address mAddress = new Address(); // Constructor that converts an APCF-friendly filter to an MSFT-friendly format public MsftAdvMonitor(ScanFilter filter) { // Hardcoded values taken from CrOS defaults mMonitor.rssi_threshold_high = RSSI_THRESHOLD_HIGH; mMonitor.rssi_threshold_low = RSSI_THRESHOLD_LOW; mMonitor.rssi_threshold_low_time_interval = RSSI_THRESHOLD_LOW_TIME_INTERVAL; mMonitor.rssi_sampling_period = RSSI_SAMPLING_PERIOD; mMonitor.condition_type = MSFT_CONDITION_TYPE_PATTERNS; if (filter.getServiceDataUuid() != null && filter.getServiceDataMask() == null) { Pattern pattern = new Pattern(); pattern.ad_type = (byte) 0x16; // Bluetooth Core Spec Part A, Section 1 pattern.start_byte = FILTER_PATTERN_START_POSITION; // Extract the 16-bit UUID (third and fourth bytes) from the 128-bit // UUID in reverse endianness UUID uuid = filter.getServiceDataUuid().getUuid(); ByteBuffer bb = ByteBuffer.allocate(16); // 16 byte (128 bit) UUID bb.putLong(uuid.getMostSignificantBits()); bb.putLong(uuid.getLeastSignificantBits()); pattern.pattern = new byte[] {bb.get(3), bb.get(2)}; mPatterns.add(pattern); } else if (filter.getAdvertisingData() != null && filter.getAdvertisingData().length != 0 && (filter.getAdvertisingDataMask() == null || filter.getAdvertisingDataMask().length == 0)) { Pattern pattern = new Pattern(); pattern.ad_type = (byte) filter.getAdvertisingDataType(); pattern.start_byte = FILTER_PATTERN_START_POSITION; pattern.pattern = filter.getAdvertisingData(); mPatterns.add(pattern); } if (filter.getDeviceAddress() != null) { mAddress.addr_type = (byte) filter.getAddressType(); mAddress.bd_addr = filter.getDeviceAddress(); } } Monitor getMonitor() { return mMonitor; } Pattern[] getPatterns() { return mPatterns.toArray(new Pattern[mPatterns.size()]); } Address getAddress() { return mAddress; } } android/app/tests/unit/src/com/android/bluetooth/le_scan/MsftAdvMonitorTest.java 0 → 100644 +72 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.bluetooth.le_scan; import static com.google.common.truth.Truth.assertThat; import android.bluetooth.le.ScanFilter; import android.os.ParcelUuid; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public final class MsftAdvMonitorTest { private static final String TAG = MsftAdvMonitorTest.class.getSimpleName(); // Hardcoded values taken from CrOS defaults private static final byte RSSI_THRESHOLD_HIGH = (byte) 0xBF; // 191 private static final byte RSSI_THRESHOLD_LOW = (byte) 0xB0; // 176 private static final byte RSSI_THRESHOLD_LOW_TIME_INTERVAL = (byte) 0x28; // 40s private static final byte RSSI_SAMPLING_PERIOD = (byte) 0x05; // 500ms private static final byte CONDITION_TYPE = (byte) 0x01; // MSFT condition type - patterns private static final byte FILTER_PATTERN_START_POSITION = (byte) 0x00; // Retrieved from real Fastpair filter data private static final String FAST_PAIR_UUID = "0000fe2c-0000-1000-8000-00805f9b34fb"; private static final ParcelUuid FAST_PAIR_SERVICE_DATA_UUID = ParcelUuid.fromString(FAST_PAIR_UUID); private static final byte[] FAST_PAIR_SERVICE_DATA = new byte[] {(byte) 0xfc, (byte) 0x12, (byte) 0x8e}; private void assertMonitorConstants(MsftAdvMonitor monitor) { MsftAdvMonitor.Monitor mMonitor = monitor.getMonitor(); assertThat(mMonitor.rssi_threshold_high).isEqualTo(RSSI_THRESHOLD_HIGH); assertThat(mMonitor.rssi_threshold_low).isEqualTo(RSSI_THRESHOLD_LOW); assertThat(mMonitor.rssi_threshold_low_time_interval) .isEqualTo(RSSI_THRESHOLD_LOW_TIME_INTERVAL); assertThat(mMonitor.rssi_sampling_period).isEqualTo(RSSI_SAMPLING_PERIOD); assertThat(mMonitor.condition_type).isEqualTo(CONDITION_TYPE); } @Test public void testFastPairScanFilter() { ScanFilter filter = new ScanFilter.Builder() .setServiceData(FAST_PAIR_SERVICE_DATA_UUID, FAST_PAIR_SERVICE_DATA) .build(); MsftAdvMonitor monitor = new MsftAdvMonitor(filter); assertMonitorConstants(monitor); assertThat(monitor.getPatterns()).hasLength(1); MsftAdvMonitor.Pattern mPattern = monitor.getPatterns()[0]; assertThat(mPattern.ad_type) .isEqualTo((byte) 0x16); // Bluetooth Core Spec Part A, Section 1 assertThat(mPattern.start_byte).isEqualTo(FILTER_PATTERN_START_POSITION); assertThat(mPattern.pattern).isEqualTo(new byte[] {(byte) 0x2c, (byte) 0xfe}); } } Loading
android/app/src/com/android/bluetooth/le_scan/MsftAdvMonitor.java +77 −0 Original line number Diff line number Diff line Loading @@ -16,7 +16,28 @@ package com.android.bluetooth.le_scan; import android.bluetooth.le.ScanFilter; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.UUID; /** Helper class used to manage MSFT Advertisement Monitors. */ class MsftAdvMonitor { /* Only pattern filtering is supported currently */ // private static final int MSFT_CONDITION_TYPE_ALL = 0x00; private static final int MSFT_CONDITION_TYPE_PATTERNS = 0x01; // private static final int MSFT_CONDITION_TYPE_UUID = 0x02; // private static final int MSFT_CONDITION_TYPE_IRK = 0x03; // private static final int MSFT_CONDITION_TYPE_ADDRESS = 0x04; // Hardcoded values taken from CrOS defaults private static final byte RSSI_THRESHOLD_HIGH = (byte) 0xBF; // 191 private static final byte RSSI_THRESHOLD_LOW = (byte) 0xB0; // 176 private static final byte RSSI_THRESHOLD_LOW_TIME_INTERVAL = (byte) 0x28; // 40s private static final byte RSSI_SAMPLING_PERIOD = (byte) 0x05; // 500ms private static final int FILTER_PATTERN_START_POSITION = (byte) 0x00; static class Monitor { public byte rssi_threshold_high; public byte rssi_threshold_low; Loading @@ -35,4 +56,60 @@ class MsftAdvMonitor { byte addr_type; String bd_addr; } private final Monitor mMonitor = new Monitor(); private final ArrayList<Pattern> mPatterns = new ArrayList<>(); private final Address mAddress = new Address(); // Constructor that converts an APCF-friendly filter to an MSFT-friendly format public MsftAdvMonitor(ScanFilter filter) { // Hardcoded values taken from CrOS defaults mMonitor.rssi_threshold_high = RSSI_THRESHOLD_HIGH; mMonitor.rssi_threshold_low = RSSI_THRESHOLD_LOW; mMonitor.rssi_threshold_low_time_interval = RSSI_THRESHOLD_LOW_TIME_INTERVAL; mMonitor.rssi_sampling_period = RSSI_SAMPLING_PERIOD; mMonitor.condition_type = MSFT_CONDITION_TYPE_PATTERNS; if (filter.getServiceDataUuid() != null && filter.getServiceDataMask() == null) { Pattern pattern = new Pattern(); pattern.ad_type = (byte) 0x16; // Bluetooth Core Spec Part A, Section 1 pattern.start_byte = FILTER_PATTERN_START_POSITION; // Extract the 16-bit UUID (third and fourth bytes) from the 128-bit // UUID in reverse endianness UUID uuid = filter.getServiceDataUuid().getUuid(); ByteBuffer bb = ByteBuffer.allocate(16); // 16 byte (128 bit) UUID bb.putLong(uuid.getMostSignificantBits()); bb.putLong(uuid.getLeastSignificantBits()); pattern.pattern = new byte[] {bb.get(3), bb.get(2)}; mPatterns.add(pattern); } else if (filter.getAdvertisingData() != null && filter.getAdvertisingData().length != 0 && (filter.getAdvertisingDataMask() == null || filter.getAdvertisingDataMask().length == 0)) { Pattern pattern = new Pattern(); pattern.ad_type = (byte) filter.getAdvertisingDataType(); pattern.start_byte = FILTER_PATTERN_START_POSITION; pattern.pattern = filter.getAdvertisingData(); mPatterns.add(pattern); } if (filter.getDeviceAddress() != null) { mAddress.addr_type = (byte) filter.getAddressType(); mAddress.bd_addr = filter.getDeviceAddress(); } } Monitor getMonitor() { return mMonitor; } Pattern[] getPatterns() { return mPatterns.toArray(new Pattern[mPatterns.size()]); } Address getAddress() { return mAddress; } }
android/app/tests/unit/src/com/android/bluetooth/le_scan/MsftAdvMonitorTest.java 0 → 100644 +72 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.bluetooth.le_scan; import static com.google.common.truth.Truth.assertThat; import android.bluetooth.le.ScanFilter; import android.os.ParcelUuid; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public final class MsftAdvMonitorTest { private static final String TAG = MsftAdvMonitorTest.class.getSimpleName(); // Hardcoded values taken from CrOS defaults private static final byte RSSI_THRESHOLD_HIGH = (byte) 0xBF; // 191 private static final byte RSSI_THRESHOLD_LOW = (byte) 0xB0; // 176 private static final byte RSSI_THRESHOLD_LOW_TIME_INTERVAL = (byte) 0x28; // 40s private static final byte RSSI_SAMPLING_PERIOD = (byte) 0x05; // 500ms private static final byte CONDITION_TYPE = (byte) 0x01; // MSFT condition type - patterns private static final byte FILTER_PATTERN_START_POSITION = (byte) 0x00; // Retrieved from real Fastpair filter data private static final String FAST_PAIR_UUID = "0000fe2c-0000-1000-8000-00805f9b34fb"; private static final ParcelUuid FAST_PAIR_SERVICE_DATA_UUID = ParcelUuid.fromString(FAST_PAIR_UUID); private static final byte[] FAST_PAIR_SERVICE_DATA = new byte[] {(byte) 0xfc, (byte) 0x12, (byte) 0x8e}; private void assertMonitorConstants(MsftAdvMonitor monitor) { MsftAdvMonitor.Monitor mMonitor = monitor.getMonitor(); assertThat(mMonitor.rssi_threshold_high).isEqualTo(RSSI_THRESHOLD_HIGH); assertThat(mMonitor.rssi_threshold_low).isEqualTo(RSSI_THRESHOLD_LOW); assertThat(mMonitor.rssi_threshold_low_time_interval) .isEqualTo(RSSI_THRESHOLD_LOW_TIME_INTERVAL); assertThat(mMonitor.rssi_sampling_period).isEqualTo(RSSI_SAMPLING_PERIOD); assertThat(mMonitor.condition_type).isEqualTo(CONDITION_TYPE); } @Test public void testFastPairScanFilter() { ScanFilter filter = new ScanFilter.Builder() .setServiceData(FAST_PAIR_SERVICE_DATA_UUID, FAST_PAIR_SERVICE_DATA) .build(); MsftAdvMonitor monitor = new MsftAdvMonitor(filter); assertMonitorConstants(monitor); assertThat(monitor.getPatterns()).hasLength(1); MsftAdvMonitor.Pattern mPattern = monitor.getPatterns()[0]; assertThat(mPattern.ad_type) .isEqualTo((byte) 0x16); // Bluetooth Core Spec Part A, Section 1 assertThat(mPattern.start_byte).isEqualTo(FILTER_PATTERN_START_POSITION); assertThat(mPattern.pattern).isEqualTo(new byte[] {(byte) 0x2c, (byte) 0xfe}); } }