1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bluetooth.le_scan; 18 19 import android.bluetooth.le.ScanFilter; 20 21 import java.nio.ByteBuffer; 22 import java.util.ArrayList; 23 import java.util.Arrays; 24 import java.util.Objects; 25 import java.util.UUID; 26 27 /** Helper class used to manage MSFT Advertisement Monitors. */ 28 class MsftAdvMonitor { 29 /* Only pattern and address filtering are supported currently */ 30 // private static final int MSFT_CONDITION_TYPE_ALL = 0x00; 31 private static final int MSFT_CONDITION_TYPE_PATTERNS = 0x01; 32 // private static final int MSFT_CONDITION_TYPE_UUID = 0x02; 33 // private static final int MSFT_CONDITION_TYPE_IRK = 0x03; 34 private static final int MSFT_CONDITION_TYPE_ADDRESS = 0x04; 35 36 // Hardcoded values taken from CrOS defaults 37 private static final byte RSSI_THRESHOLD_HIGH = (byte) 0xBF; // 191 38 private static final byte RSSI_THRESHOLD_LOW = (byte) 0xB0; // 176 39 private static final byte RSSI_THRESHOLD_LOW_TIME_INTERVAL = (byte) 0x28; // 40s 40 private static final byte RSSI_SAMPLING_PERIOD = (byte) 0x05; // 500ms 41 private static final int FILTER_PATTERN_START_POSITION = (byte) 0x00; 42 43 static class Monitor { 44 public byte rssi_threshold_high; 45 public byte rssi_threshold_low; 46 public byte rssi_threshold_low_time_interval; 47 public byte rssi_sampling_period; 48 public byte condition_type; 49 } 50 51 static class Pattern { 52 public byte ad_type; 53 public byte start_byte; 54 public byte[] pattern; 55 56 @Override equals(Object o)57 public boolean equals(Object o) { 58 if (o == this) { 59 return true; 60 } 61 if (!(o instanceof Pattern other)) { 62 return false; 63 } 64 65 return other.ad_type == this.ad_type 66 && other.start_byte == this.start_byte 67 && Arrays.equals(other.pattern, this.pattern); 68 } 69 70 @Override hashCode()71 public int hashCode() { 72 return Objects.hash(ad_type, start_byte, Arrays.hashCode(pattern)); 73 } 74 } 75 76 static class Address { 77 byte addr_type; 78 String bd_addr; 79 } 80 81 private final Monitor mMonitor = new Monitor(); 82 private final ArrayList<Pattern> mPatterns = new ArrayList<>(); 83 private final Address mAddress = new Address(); 84 85 // Constructor that converts an APCF-friendly filter to an MSFT-friendly format MsftAdvMonitor(ScanFilter filter)86 MsftAdvMonitor(ScanFilter filter) { 87 // Hardcoded values taken from CrOS defaults 88 mMonitor.rssi_threshold_high = RSSI_THRESHOLD_HIGH; 89 mMonitor.rssi_threshold_low = RSSI_THRESHOLD_LOW; 90 mMonitor.rssi_threshold_low_time_interval = RSSI_THRESHOLD_LOW_TIME_INTERVAL; 91 mMonitor.rssi_sampling_period = RSSI_SAMPLING_PERIOD; 92 mMonitor.condition_type = MSFT_CONDITION_TYPE_PATTERNS; 93 94 if (filter.getDeviceAddress() != null) { 95 mMonitor.condition_type = MSFT_CONDITION_TYPE_ADDRESS; 96 mAddress.addr_type = (byte) filter.getAddressType(); 97 mAddress.bd_addr = filter.getDeviceAddress(); 98 return; 99 } 100 101 if (filter.getServiceDataUuid() != null && dataMaskIsEmpty(filter.getServiceDataMask())) { 102 Pattern pattern = new Pattern(); 103 pattern.ad_type = (byte) 0x16; // Bluetooth Core Spec Part A, Section 1 104 pattern.start_byte = FILTER_PATTERN_START_POSITION; 105 106 // Extract the 16-bit UUID (third and fourth bytes) from the 128-bit 107 // UUID in reverse endianness 108 UUID uuid = filter.getServiceDataUuid().getUuid(); 109 ByteBuffer bb = ByteBuffer.allocate(16); // 16 byte (128 bit) UUID 110 bb.putLong(uuid.getMostSignificantBits()); 111 bb.putLong(uuid.getLeastSignificantBits()); 112 pattern.pattern = new byte[] {bb.get(3), bb.get(2)}; 113 114 mPatterns.add(pattern); 115 } else if (filter.getAdvertisingData() != null 116 && filter.getAdvertisingData().length != 0 117 && dataMaskIsEmpty(filter.getAdvertisingDataMask())) { 118 Pattern pattern = new Pattern(); 119 pattern.ad_type = (byte) filter.getAdvertisingDataType(); 120 pattern.start_byte = FILTER_PATTERN_START_POSITION; 121 pattern.pattern = filter.getAdvertisingData(); 122 mPatterns.add(pattern); 123 } 124 } 125 getMonitor()126 Monitor getMonitor() { 127 return mMonitor; 128 } 129 getPatterns()130 Pattern[] getPatterns() { 131 return mPatterns.toArray(new Pattern[mPatterns.size()]); 132 } 133 getAddress()134 Address getAddress() { 135 return mAddress; 136 } 137 dataMaskIsEmpty(byte[] mask)138 private static boolean dataMaskIsEmpty(byte[] mask) { 139 if (mask == null || mask.length == 0) return true; 140 if (mask.length == 1 && mask[0] == 0) return true; 141 return false; 142 } 143 } 144