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