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.Objects;
27 import java.util.Set;
28 import java.util.UUID;
29 
30 /**
31  * Helper class used to manage advertisement package filters.
32  *
33  * @hide
34  */
35 /* package */class ScanFilterQueue {
36     public static final int TYPE_DEVICE_ADDRESS = 0;
37     public static final int TYPE_SERVICE_DATA_CHANGED = 1;
38     public static final int TYPE_SERVICE_UUID = 2;
39     public static final int TYPE_SOLICIT_UUID = 3;
40     public static final int TYPE_LOCAL_NAME = 4;
41     public static final int TYPE_MANUFACTURER_DATA = 5;
42     public static final int TYPE_SERVICE_DATA = 6;
43 
44     // Max length is 31 - 3(flags) - 2 (one byte for length and one byte for type).
45     private static final int MAX_LEN_PER_FIELD = 26;
46 
47     // Values defined in bluedroid.
48     private static final byte DEVICE_TYPE_ALL = 2;
49 
50     class Entry {
51         public String address;
52         public byte addr_type;
53         public byte type;
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         @Override
hashCode()63         public int hashCode() {
64             return Objects.hash(address, addr_type, type, uuid, uuid_mask,
65                                 name, company, company_mask,
66                                 Arrays.hashCode(data),
67                                 Arrays.hashCode(data_mask));
68         }
69 
70         @Override
equals(Object obj)71         public boolean equals(Object obj) {
72             if (this == obj) {
73                 return true;
74             }
75             if (obj == null || getClass() != obj.getClass()) {
76                 return false;
77             }
78             Entry other = (Entry) obj;
79             return Objects.equals(address, other.address) &&
80                     addr_type == other.addr_type && type == other.type &&
81                     Objects.equals(uuid, other.uuid) &&
82                     Objects.equals(uuid_mask, other.uuid_mask) &&
83                     Objects.equals(name, other.name) &&
84                             company == other.company && company_mask == other.company_mask &&
85                     Objects.deepEquals(data, other.data) &&
86                     Objects.deepEquals(data_mask, other.data_mask);
87         }
88     }
89 
90     private Set<Entry> mEntries = new HashSet<Entry>();
91 
addDeviceAddress(String address, byte type)92     void addDeviceAddress(String address, byte type) {
93         Entry entry = new Entry();
94         entry.type = TYPE_DEVICE_ADDRESS;
95         entry.address = address;
96         entry.addr_type = type;
97         mEntries.add(entry);
98     }
99 
addServiceChanged()100     void addServiceChanged() {
101         Entry entry = new Entry();
102         entry.type = TYPE_SERVICE_DATA_CHANGED;
103         mEntries.add(entry);
104     }
105 
addUuid(UUID uuid)106     void addUuid(UUID uuid) {
107         Entry entry = new Entry();
108         entry.type = TYPE_SERVICE_UUID;
109         entry.uuid = uuid;
110         entry.uuid_mask = new UUID(0, 0);
111         mEntries.add(entry);
112     }
113 
addUuid(UUID uuid, UUID uuid_mask)114     void addUuid(UUID uuid, UUID uuid_mask) {
115         Entry entry = new Entry();
116         entry.type = TYPE_SERVICE_UUID;
117         entry.uuid = uuid;
118         entry.uuid_mask = uuid_mask;
119         mEntries.add(entry);
120     }
121 
addSolicitUuid(UUID uuid)122     void addSolicitUuid(UUID uuid) {
123         Entry entry = new Entry();
124         entry.type = TYPE_SOLICIT_UUID;
125         entry.uuid = uuid;
126         mEntries.add(entry);
127     }
128 
addName(String name)129     void addName(String name) {
130         Entry entry = new Entry();
131         entry.type = TYPE_LOCAL_NAME;
132         entry.name = name;
133         mEntries.add(entry);
134     }
135 
addManufacturerData(int company, byte[] data)136     void addManufacturerData(int company, byte[] data) {
137         Entry entry = new Entry();
138         entry.type = TYPE_MANUFACTURER_DATA;
139         entry.company = company;
140         entry.company_mask = 0xFFFF;
141         entry.data = data;
142         entry.data_mask = new byte[data.length];
143         Arrays.fill(entry.data_mask, (byte) 0xFF);
144         mEntries.add(entry);
145     }
146 
addManufacturerData(int company, int company_mask, byte[] data, byte[] data_mask)147     void addManufacturerData(int company, int company_mask, byte[] data, byte[] data_mask) {
148         Entry entry = new Entry();
149         entry.type = TYPE_MANUFACTURER_DATA;
150         entry.company = company;
151         entry.company_mask = company_mask;
152         entry.data = data;
153         entry.data_mask = data_mask;
154         mEntries.add(entry);
155     }
156 
addServiceData(byte[] data, byte[] dataMask)157     void addServiceData(byte[] data, byte[] dataMask) {
158         Entry entry = new Entry();
159         entry.type = TYPE_SERVICE_DATA;
160         entry.data = data;
161         entry.data_mask = dataMask;
162         mEntries.add(entry);
163     }
164 
pop()165     Entry pop() {
166         if (isEmpty()) {
167             return null;
168         }
169         Iterator<Entry> iterator = mEntries.iterator();
170         Entry entry = iterator.next();
171         iterator.remove();
172         return entry;
173     }
174 
isEmpty()175     boolean isEmpty() {
176         return mEntries.isEmpty();
177     }
178 
clearUuids()179     void clearUuids() {
180         for (Iterator<Entry> it = mEntries.iterator(); it.hasNext();) {
181             Entry entry = it.next();
182             if (entry.type == TYPE_SERVICE_UUID)
183                 it.remove();
184         }
185     }
186 
clear()187     void clear() {
188         mEntries.clear();
189     }
190 
191     /**
192      * Compute feature selection based on the filters presented.
193      */
getFeatureSelection()194     int getFeatureSelection() {
195         int selc = 0;
196         for (Entry entry : mEntries) {
197             selc |= (1 << entry.type);
198         }
199         return selc;
200     }
201 
202     /**
203      * Add ScanFilter to scan filter queue.
204      */
addScanFilter(ScanFilter filter)205     void addScanFilter(ScanFilter filter) {
206         if (filter == null)
207             return;
208         if (filter.getDeviceName() != null) {
209             addName(filter.getDeviceName());
210         }
211         if (filter.getDeviceAddress() != null) {
212             addDeviceAddress(filter.getDeviceAddress(), DEVICE_TYPE_ALL);
213         }
214         if (filter.getServiceUuid() != null) {
215             if (filter.getServiceUuidMask() == null) {
216                 addUuid(filter.getServiceUuid().getUuid());
217             } else {
218                 addUuid(filter.getServiceUuid().getUuid(),
219                         filter.getServiceUuidMask().getUuid());
220             }
221         }
222         if (filter.getManufacturerData() != null) {
223             if (filter.getManufacturerDataMask() == null) {
224                 addManufacturerData(filter.getManufacturerId(), filter.getManufacturerData());
225             } else {
226                 addManufacturerData(filter.getManufacturerId(), 0xFFFF,
227                         filter.getManufacturerData(), filter.getManufacturerDataMask());
228             }
229         }
230         if (filter.getServiceDataUuid() != null && filter.getServiceData() != null) {
231             ParcelUuid serviceDataUuid = filter.getServiceDataUuid();
232             byte[] serviceData = filter.getServiceData();
233             byte[] serviceDataMask = filter.getServiceDataMask();
234             if (serviceDataMask == null) {
235                 serviceDataMask = new byte[serviceData.length];
236                 Arrays.fill(serviceDataMask, (byte) 0xFF);
237             }
238             serviceData = concate(serviceDataUuid, serviceData);
239             serviceDataMask = concate(serviceDataUuid, serviceDataMask);
240             if (serviceData != null && serviceDataMask != null) {
241                 addServiceData(serviceData, serviceDataMask);
242             }
243         }
244     }
245 
concate(ParcelUuid serviceDataUuid, byte[] serviceData)246     private byte[] concate(ParcelUuid serviceDataUuid, byte[] serviceData) {
247         int dataLen = 2 + (serviceData == null ? 0 : serviceData.length);
248         // If data is too long, don't add it to hardware scan filter.
249         if (dataLen > MAX_LEN_PER_FIELD) {
250             return null;
251         }
252         byte[] concated = new byte[dataLen];
253         // Extract 16 bit UUID value.
254         int uuidValue = BluetoothUuid.getServiceIdentifierFromParcelUuid(
255                 serviceDataUuid);
256         // First two bytes are service data UUID in little-endian.
257         concated[0] = (byte) (uuidValue & 0xFF);
258         concated[1] = (byte) ((uuidValue >> 8) & 0xFF);
259         if (serviceData != null) {
260             System.arraycopy(serviceData, 0, concated, 2, serviceData.length);
261         }
262         return concated;
263     }
264 }
265