• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 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.BluetoothAssignedNumbers.OrganizationId;
20 import android.bluetooth.BluetoothUuid;
21 import android.bluetooth.le.ScanFilter;
22 import android.bluetooth.le.TransportBlockFilter;
23 import android.os.ParcelUuid;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 
27 import java.util.Arrays;
28 import java.util.HashSet;
29 import java.util.Iterator;
30 import java.util.Set;
31 import java.util.UUID;
32 
33 /** Helper class used to manage advertisement package filters. */
34 /* package */ class ScanFilterQueue {
35     @VisibleForTesting static final int TYPE_DEVICE_ADDRESS = 0;
36     @VisibleForTesting static final int TYPE_SERVICE_DATA_CHANGED = 1;
37     @VisibleForTesting static final int TYPE_SERVICE_UUID = 2;
38     @VisibleForTesting static final int TYPE_SOLICIT_UUID = 3;
39     @VisibleForTesting static final int TYPE_LOCAL_NAME = 4;
40     @VisibleForTesting static final int TYPE_MANUFACTURER_DATA = 5;
41     @VisibleForTesting static final int TYPE_SERVICE_DATA = 6;
42     @VisibleForTesting static final int TYPE_TRANSPORT_DISCOVERY_DATA = 7;
43     @VisibleForTesting static final int TYPE_ADVERTISING_DATA_TYPE = 8;
44 
45     private static final int TYPE_INVALID = 0x00; // Meta data type for Transport Block Filter
46     private static final int TYPE_WIFI_NAN_HASH = 0x01; // WIFI NAN HASH type
47 
48     // Max length is 31 - 3(flags) - 2 (one byte for length and one byte for type).
49     private static final int MAX_LEN_PER_FIELD = 26;
50 
51     static class Entry {
52         public byte type;
53         public String address;
54         public byte addr_type;
55         public byte[] irk;
56         public UUID uuid;
57         public UUID uuid_mask;
58         public String name;
59         public int company;
60         public int company_mask;
61         public int ad_type;
62         public byte[] data;
63         public byte[] data_mask;
64         public int org_id;
65         public int tds_flags;
66         public int tds_flags_mask;
67         public int meta_data_type;
68         public byte[] meta_data;
69     }
70 
71     private final Set<Entry> mEntries = new HashSet<>();
72 
73     @VisibleForTesting
addDeviceAddress(String address, byte type, byte[] irk)74     void addDeviceAddress(String address, byte type, byte[] irk) {
75         Entry entry = new Entry();
76         entry.type = TYPE_DEVICE_ADDRESS;
77         entry.address = address;
78         entry.addr_type = type;
79         entry.irk = irk;
80         mEntries.add(entry);
81     }
82 
83     @VisibleForTesting
addUuid(UUID uuid)84     void addUuid(UUID uuid) {
85         addUuid(uuid, new UUID(0, 0));
86     }
87 
88     @VisibleForTesting
addUuid(UUID uuid, UUID uuidMask)89     void addUuid(UUID uuid, UUID uuidMask) {
90         Entry entry = new Entry();
91         entry.type = TYPE_SERVICE_UUID;
92         entry.uuid = uuid;
93         entry.uuid_mask = uuidMask;
94         mEntries.add(entry);
95     }
96 
97     @VisibleForTesting
addSolicitUuid(UUID uuid)98     void addSolicitUuid(UUID uuid) {
99         addSolicitUuid(uuid, new UUID(0, 0));
100     }
101 
102     @VisibleForTesting
addSolicitUuid(UUID uuid, UUID uuidMask)103     void addSolicitUuid(UUID uuid, UUID uuidMask) {
104         Entry entry = new Entry();
105         entry.type = TYPE_SOLICIT_UUID;
106         entry.uuid = uuid;
107         entry.uuid_mask = uuidMask;
108         mEntries.add(entry);
109     }
110 
111     @VisibleForTesting
addName(String name)112     void addName(String name) {
113         Entry entry = new Entry();
114         entry.type = TYPE_LOCAL_NAME;
115         entry.name = name;
116         mEntries.add(entry);
117     }
118 
119     @VisibleForTesting
addManufacturerData(int company, byte[] data)120     void addManufacturerData(int company, byte[] data) {
121         int companyMask = 0xFFFF;
122         byte[] dataMask = new byte[data.length];
123         Arrays.fill(dataMask, (byte) 0xFF);
124         addManufacturerData(company, companyMask, data, dataMask);
125     }
126 
127     @VisibleForTesting
addManufacturerData(int company, int companyMask, byte[] data, byte[] dataMask)128     void addManufacturerData(int company, int companyMask, byte[] data, byte[] dataMask) {
129         Entry entry = new Entry();
130         entry.type = TYPE_MANUFACTURER_DATA;
131         entry.company = company;
132         entry.company_mask = companyMask;
133         entry.data = data;
134         entry.data_mask = dataMask;
135         mEntries.add(entry);
136     }
137 
138     @VisibleForTesting
addServiceData(byte[] data, byte[] dataMask)139     void addServiceData(byte[] data, byte[] dataMask) {
140         Entry entry = new Entry();
141         entry.type = TYPE_SERVICE_DATA;
142         entry.data = data;
143         entry.data_mask = dataMask;
144         mEntries.add(entry);
145     }
146 
147     @VisibleForTesting
addTransportDiscoveryData( int orgId, int tdsFlags, int tdsFlagsMask, byte[] transportData, byte[] transportDataMask, int metaDataType, byte[] metaData)148     void addTransportDiscoveryData(
149             int orgId,
150             int tdsFlags,
151             int tdsFlagsMask,
152             byte[] transportData,
153             byte[] transportDataMask,
154             int metaDataType,
155             byte[] metaData) {
156         Entry entry = new Entry();
157         entry.type = TYPE_TRANSPORT_DISCOVERY_DATA;
158         entry.org_id = orgId;
159         entry.tds_flags = tdsFlags;
160         entry.tds_flags_mask = tdsFlagsMask;
161         entry.data = transportData;
162         entry.data_mask = transportDataMask;
163         entry.meta_data_type = metaDataType;
164         entry.meta_data = metaData;
165         mEntries.add(entry);
166     }
167 
168     @VisibleForTesting
addAdvertisingDataType(int adType, byte[] data, byte[] dataMask)169     void addAdvertisingDataType(int adType, byte[] data, byte[] dataMask) {
170         Entry entry = new Entry();
171         entry.type = TYPE_ADVERTISING_DATA_TYPE;
172         entry.ad_type = adType;
173         entry.data = data;
174         entry.data_mask = dataMask;
175         mEntries.add(entry);
176     }
177 
178     @VisibleForTesting
pop()179     Entry pop() {
180         if (mEntries.isEmpty()) {
181             return null;
182         }
183         Iterator<Entry> iterator = mEntries.iterator();
184         Entry entry = iterator.next();
185         iterator.remove();
186         return entry;
187     }
188 
189     // Compute feature selection based on the filters presented.
getFeatureSelection()190     int getFeatureSelection() {
191         int selection = 0;
192         for (Entry entry : mEntries) {
193             selection |= (1 << entry.type);
194         }
195         return selection;
196     }
197 
toArray()198     ScanFilterQueue.Entry[] toArray() {
199         return mEntries.toArray(new ScanFilterQueue.Entry[mEntries.size()]);
200     }
201 
202     // Add ScanFilter to scan filter queue.
addScanFilter(ScanFilter filter)203     void addScanFilter(ScanFilter filter) {
204         if (filter == null) {
205             return;
206         }
207         if (filter.getDeviceName() != null) {
208             addName(filter.getDeviceName());
209         }
210         if (filter.getDeviceAddress() != null) {
211             /*
212              * Pass the address type here. This address type will be used for the resolving
213              * address, however, the host stack will force the type to 0x02 for the APCF filter
214              * in btm_ble_adv_filter.cc#BTM_LE_PF_addr_filter(...)
215              */
216             addDeviceAddress(
217                     filter.getDeviceAddress(), (byte) filter.getAddressType(), filter.getIrk());
218         }
219         if (filter.getServiceUuid() != null) {
220             if (filter.getServiceUuidMask() == null) {
221                 addUuid(filter.getServiceUuid().getUuid());
222             } else {
223                 addUuid(filter.getServiceUuid().getUuid(), filter.getServiceUuidMask().getUuid());
224             }
225         }
226         if (filter.getServiceSolicitationUuid() != null) {
227             if (filter.getServiceSolicitationUuidMask() == null) {
228                 addSolicitUuid(filter.getServiceSolicitationUuid().getUuid());
229             } else {
230                 addSolicitUuid(
231                         filter.getServiceSolicitationUuid().getUuid(),
232                         filter.getServiceSolicitationUuidMask().getUuid());
233             }
234         }
235         if (filter.getManufacturerData() != null) {
236             if (filter.getManufacturerDataMask() == null) {
237                 addManufacturerData(filter.getManufacturerId(), filter.getManufacturerData());
238             } else {
239                 addManufacturerData(
240                         filter.getManufacturerId(),
241                         0xFFFF,
242                         filter.getManufacturerData(),
243                         filter.getManufacturerDataMask());
244             }
245         }
246         if (filter.getServiceDataUuid() != null && filter.getServiceData() != null) {
247             ParcelUuid serviceDataUuid = filter.getServiceDataUuid();
248             byte[] serviceData = filter.getServiceData();
249             byte[] serviceDataMask = filter.getServiceDataMask();
250             if (serviceDataMask == null) {
251                 serviceDataMask = new byte[serviceData.length];
252                 Arrays.fill(serviceDataMask, (byte) 0xFF);
253             }
254             serviceData = concatenate(serviceDataUuid, serviceData, false);
255             serviceDataMask = concatenate(serviceDataUuid, serviceDataMask, true);
256             if (serviceData != null && serviceDataMask != null) {
257                 addServiceData(serviceData, serviceDataMask);
258             }
259         }
260         if (filter.getAdvertisingDataType() > 0) {
261             addAdvertisingDataType(
262                     filter.getAdvertisingDataType(),
263                     filter.getAdvertisingData(),
264                     filter.getAdvertisingDataMask());
265         }
266         final TransportBlockFilter transportBlockFilter = filter.getTransportBlockFilter();
267         if (transportBlockFilter != null) {
268             if (transportBlockFilter.getOrgId()
269                     == OrganizationId.WIFI_ALLIANCE_NEIGHBOR_AWARENESS_NETWORKING) {
270                 addTransportDiscoveryData(
271                         transportBlockFilter.getOrgId(),
272                         transportBlockFilter.getTdsFlags(),
273                         transportBlockFilter.getTdsFlagsMask(),
274                         null,
275                         null,
276                         TYPE_WIFI_NAN_HASH,
277                         transportBlockFilter.getWifiNanHash());
278             } else {
279                 addTransportDiscoveryData(
280                         transportBlockFilter.getOrgId(),
281                         transportBlockFilter.getTdsFlags(),
282                         transportBlockFilter.getTdsFlagsMask(),
283                         transportBlockFilter.getTransportData(),
284                         transportBlockFilter.getTransportDataMask(),
285                         TYPE_INVALID,
286                         null);
287             }
288         }
289     }
290 
concatenate( ParcelUuid serviceDataUuid, byte[] serviceData, boolean isMask)291     private static byte[] concatenate(
292             ParcelUuid serviceDataUuid, byte[] serviceData, boolean isMask) {
293         byte[] uuid = BluetoothUuid.uuidToBytes(serviceDataUuid);
294 
295         int dataLen = uuid.length + serviceData.length;
296         // If data is too long, don't add it to hardware scan filter.
297         if (dataLen > MAX_LEN_PER_FIELD) {
298             return null;
299         }
300         byte[] concatenated = new byte[dataLen];
301         if (isMask) {
302             // For the UUID portion of the mask fill it with 0xFF to indicate that all bits of the
303             // UUID need to match the service data filter.
304             Arrays.fill(concatenated, 0, uuid.length, (byte) 0xFF);
305         } else {
306             System.arraycopy(uuid, 0, concatenated, 0, uuid.length);
307         }
308         System.arraycopy(serviceData, 0, concatenated, uuid.length, serviceData.length);
309         return concatenated;
310     }
311 }
312