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