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.gatt;
18 
19 import android.bluetooth.BluetoothUuid;
20 import android.bluetooth.le.ScanFilter;
21 import android.os.ParcelUuid;
22 
23 import java.util.Arrays;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.Set;
27 import java.util.UUID;
28 
29 /**
30  * Helper class used to manage advertisement package filters.
31  *
32  * @hide
33  */
34 /* package */class ScanFilterQueue {
35     public static final int TYPE_DEVICE_ADDRESS = 0;
36     public static final int TYPE_SERVICE_DATA_CHANGED = 1;
37     public static final int TYPE_SERVICE_UUID = 2;
38     public static final int TYPE_SOLICIT_UUID = 3;
39     public static final int TYPE_LOCAL_NAME = 4;
40     public static final int TYPE_MANUFACTURER_DATA = 5;
41     public static final int TYPE_SERVICE_DATA = 6;
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     // Values defined in bluedroid.
47     private static final byte DEVICE_TYPE_ALL = 2;
48 
49     class Entry {
50         public byte type;
51         public String address;
52         public byte addr_type;
53         public byte[] irk;
54         public UUID uuid;
55         public UUID uuid_mask;
56         public String name;
57         public int company;
58         public int company_mask;
59         public byte[] data;
60         public byte[] data_mask;
61     }
62 
63     private Set<Entry> mEntries = new HashSet<Entry>();
64 
addDeviceAddress(String address, byte type, byte[] irk)65     void addDeviceAddress(String address, byte type, byte[] irk) {
66         Entry entry = new Entry();
67         entry.type = TYPE_DEVICE_ADDRESS;
68         entry.address = address;
69         entry.addr_type = type;
70         entry.irk = irk;
71         mEntries.add(entry);
72     }
73 
addServiceChanged()74     void addServiceChanged() {
75         Entry entry = new Entry();
76         entry.type = TYPE_SERVICE_DATA_CHANGED;
77         mEntries.add(entry);
78     }
79 
addUuid(UUID uuid)80     void addUuid(UUID uuid) {
81         Entry entry = new Entry();
82         entry.type = TYPE_SERVICE_UUID;
83         entry.uuid = uuid;
84         entry.uuid_mask = new UUID(0, 0);
85         mEntries.add(entry);
86     }
87 
addUuid(UUID uuid, UUID uuidMask)88     void addUuid(UUID uuid, UUID uuidMask) {
89         Entry entry = new Entry();
90         entry.type = TYPE_SERVICE_UUID;
91         entry.uuid = uuid;
92         entry.uuid_mask = uuidMask;
93         mEntries.add(entry);
94     }
95 
addSolicitUuid(UUID uuid)96     void addSolicitUuid(UUID uuid) {
97         Entry entry = new Entry();
98         entry.type = TYPE_SOLICIT_UUID;
99         entry.uuid = uuid;
100         entry.uuid_mask = new UUID(0, 0);
101         mEntries.add(entry);
102     }
103 
addSolicitUuid(UUID uuid, UUID uuidMask)104     void addSolicitUuid(UUID uuid, UUID uuidMask) {
105         Entry entry = new Entry();
106         entry.type = TYPE_SOLICIT_UUID;
107         entry.uuid = uuid;
108         entry.uuid_mask = uuidMask;
109         mEntries.add(entry);
110     }
111 
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 
addManufacturerData(int company, byte[] data)119     void addManufacturerData(int company, byte[] data) {
120         Entry entry = new Entry();
121         entry.type = TYPE_MANUFACTURER_DATA;
122         entry.company = company;
123         entry.company_mask = 0xFFFF;
124         entry.data = data;
125         entry.data_mask = new byte[data.length];
126         Arrays.fill(entry.data_mask, (byte) 0xFF);
127         mEntries.add(entry);
128     }
129 
addManufacturerData(int company, int companyMask, byte[] data, byte[] dataMask)130     void addManufacturerData(int company, int companyMask, byte[] data, byte[] dataMask) {
131         Entry entry = new Entry();
132         entry.type = TYPE_MANUFACTURER_DATA;
133         entry.company = company;
134         entry.company_mask = companyMask;
135         entry.data = data;
136         entry.data_mask = dataMask;
137         mEntries.add(entry);
138     }
139 
addServiceData(byte[] data, byte[] dataMask)140     void addServiceData(byte[] data, byte[] dataMask) {
141         Entry entry = new Entry();
142         entry.type = TYPE_SERVICE_DATA;
143         entry.data = data;
144         entry.data_mask = dataMask;
145         mEntries.add(entry);
146     }
147 
pop()148     Entry pop() {
149         if (mEntries.isEmpty()) {
150             return null;
151         }
152         Iterator<Entry> iterator = mEntries.iterator();
153         Entry entry = iterator.next();
154         iterator.remove();
155         return entry;
156     }
157 
158     /**
159      * Compute feature selection based on the filters presented.
160      */
getFeatureSelection()161     int getFeatureSelection() {
162         int selc = 0;
163         for (Entry entry : mEntries) {
164             selc |= (1 << entry.type);
165         }
166         return selc;
167     }
168 
toArray()169     ScanFilterQueue.Entry[] toArray() {
170         return mEntries.toArray(new ScanFilterQueue.Entry[mEntries.size()]);
171     }
172 
173     /**
174      * Add ScanFilter to scan filter queue.
175      */
addScanFilter(ScanFilter filter)176     void addScanFilter(ScanFilter filter) {
177         if (filter == null) {
178             return;
179         }
180         if (filter.getDeviceName() != null) {
181             addName(filter.getDeviceName());
182         }
183         if (filter.getDeviceAddress() != null) {
184             /*
185              * Pass the addres type here.  This address type will be used for the resolving address,
186              * however, the host stack will force the type to 0x02 for the APCF filter in
187              * btm_ble_adv_filter.cc#BTM_LE_PF_addr_filter(...)
188              */
189             addDeviceAddress(filter.getDeviceAddress(), (byte) filter.getAddressType(), filter.getIrk());
190         }
191         if (filter.getServiceUuid() != null) {
192             if (filter.getServiceUuidMask() == null) {
193                 addUuid(filter.getServiceUuid().getUuid());
194             } else {
195                 addUuid(filter.getServiceUuid().getUuid(), filter.getServiceUuidMask().getUuid());
196             }
197         }
198         if (filter.getServiceSolicitationUuid() != null) {
199             if (filter.getServiceSolicitationUuidMask() == null) {
200                 addSolicitUuid(filter.getServiceSolicitationUuid().getUuid());
201             } else {
202                 addSolicitUuid(filter.getServiceSolicitationUuid().getUuid(),
203                         filter.getServiceSolicitationUuidMask().getUuid());
204             }
205         }
206         if (filter.getManufacturerData() != null) {
207             if (filter.getManufacturerDataMask() == null) {
208                 addManufacturerData(filter.getManufacturerId(), filter.getManufacturerData());
209             } else {
210                 addManufacturerData(filter.getManufacturerId(), 0xFFFF,
211                         filter.getManufacturerData(), filter.getManufacturerDataMask());
212             }
213         }
214         if (filter.getServiceDataUuid() != null && filter.getServiceData() != null) {
215             ParcelUuid serviceDataUuid = filter.getServiceDataUuid();
216             byte[] serviceData = filter.getServiceData();
217             byte[] serviceDataMask = filter.getServiceDataMask();
218             if (serviceDataMask == null) {
219                 serviceDataMask = new byte[serviceData.length];
220                 Arrays.fill(serviceDataMask, (byte) 0xFF);
221             }
222             serviceData = concate(serviceDataUuid, serviceData);
223             serviceDataMask = concate(serviceDataUuid, serviceDataMask);
224             if (serviceData != null && serviceDataMask != null) {
225                 addServiceData(serviceData, serviceDataMask);
226             }
227         }
228     }
229 
concate(ParcelUuid serviceDataUuid, byte[] serviceData)230     private byte[] concate(ParcelUuid serviceDataUuid, byte[] serviceData) {
231         byte[] uuid = BluetoothUuid.uuidToBytes(serviceDataUuid);
232 
233         int dataLen = uuid.length + (serviceData == null ? 0 : serviceData.length);
234         // If data is too long, don't add it to hardware scan filter.
235         if (dataLen > MAX_LEN_PER_FIELD) {
236             return null;
237         }
238         byte[] concated = new byte[dataLen];
239         System.arraycopy(uuid, 0, concated, 0, uuid.length);
240         if (serviceData != null) {
241             System.arraycopy(serviceData, 0, concated, uuid.length, serviceData.length);
242         }
243         return concated;
244     }
245 }
246