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 UUID uuid;
54         public UUID uuid_mask;
55         public String name;
56         public int company;
57         public int company_mask;
58         public byte[] data;
59         public byte[] data_mask;
60     }
61 
62     private Set<Entry> mEntries = new HashSet<Entry>();
63 
addDeviceAddress(String address, byte type)64     void addDeviceAddress(String address, byte type) {
65         Entry entry = new Entry();
66         entry.type = TYPE_DEVICE_ADDRESS;
67         entry.address = address;
68         entry.addr_type = type;
69         mEntries.add(entry);
70     }
71 
addServiceChanged()72     void addServiceChanged() {
73         Entry entry = new Entry();
74         entry.type = TYPE_SERVICE_DATA_CHANGED;
75         mEntries.add(entry);
76     }
77 
addUuid(UUID uuid)78     void addUuid(UUID uuid) {
79         Entry entry = new Entry();
80         entry.type = TYPE_SERVICE_UUID;
81         entry.uuid = uuid;
82         entry.uuid_mask = new UUID(0, 0);
83         mEntries.add(entry);
84     }
85 
addUuid(UUID uuid, UUID uuidMask)86     void addUuid(UUID uuid, UUID uuidMask) {
87         Entry entry = new Entry();
88         entry.type = TYPE_SERVICE_UUID;
89         entry.uuid = uuid;
90         entry.uuid_mask = uuidMask;
91         mEntries.add(entry);
92     }
93 
addSolicitUuid(UUID uuid)94     void addSolicitUuid(UUID uuid) {
95         Entry entry = new Entry();
96         entry.type = TYPE_SOLICIT_UUID;
97         entry.uuid = uuid;
98         entry.uuid_mask = new UUID(0, 0);
99         mEntries.add(entry);
100     }
101 
addSolicitUuid(UUID uuid, UUID uuidMask)102     void addSolicitUuid(UUID uuid, UUID uuidMask) {
103         Entry entry = new Entry();
104         entry.type = TYPE_SOLICIT_UUID;
105         entry.uuid = uuid;
106         entry.uuid_mask = uuidMask;
107         mEntries.add(entry);
108     }
109 
addName(String name)110     void addName(String name) {
111         Entry entry = new Entry();
112         entry.type = TYPE_LOCAL_NAME;
113         entry.name = name;
114         mEntries.add(entry);
115     }
116 
addManufacturerData(int company, byte[] data)117     void addManufacturerData(int company, byte[] data) {
118         Entry entry = new Entry();
119         entry.type = TYPE_MANUFACTURER_DATA;
120         entry.company = company;
121         entry.company_mask = 0xFFFF;
122         entry.data = data;
123         entry.data_mask = new byte[data.length];
124         Arrays.fill(entry.data_mask, (byte) 0xFF);
125         mEntries.add(entry);
126     }
127 
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 
addServiceData(byte[] data, byte[] dataMask)138     void addServiceData(byte[] data, byte[] dataMask) {
139         Entry entry = new Entry();
140         entry.type = TYPE_SERVICE_DATA;
141         entry.data = data;
142         entry.data_mask = dataMask;
143         mEntries.add(entry);
144     }
145 
pop()146     Entry pop() {
147         if (mEntries.isEmpty()) {
148             return null;
149         }
150         Iterator<Entry> iterator = mEntries.iterator();
151         Entry entry = iterator.next();
152         iterator.remove();
153         return entry;
154     }
155 
156     /**
157      * Compute feature selection based on the filters presented.
158      */
getFeatureSelection()159     int getFeatureSelection() {
160         int selc = 0;
161         for (Entry entry : mEntries) {
162             selc |= (1 << entry.type);
163         }
164         return selc;
165     }
166 
toArray()167     ScanFilterQueue.Entry[] toArray() {
168         return mEntries.toArray(new ScanFilterQueue.Entry[mEntries.size()]);
169     }
170 
171     /**
172      * Add ScanFilter to scan filter queue.
173      */
addScanFilter(ScanFilter filter)174     void addScanFilter(ScanFilter filter) {
175         if (filter == null) {
176             return;
177         }
178         if (filter.getDeviceName() != null) {
179             addName(filter.getDeviceName());
180         }
181         if (filter.getDeviceAddress() != null) {
182             addDeviceAddress(filter.getDeviceAddress(), DEVICE_TYPE_ALL);
183         }
184         if (filter.getServiceUuid() != null) {
185             if (filter.getServiceUuidMask() == null) {
186                 addUuid(filter.getServiceUuid().getUuid());
187             } else {
188                 addUuid(filter.getServiceUuid().getUuid(), filter.getServiceUuidMask().getUuid());
189             }
190         }
191         if (filter.getServiceSolicitationUuid() != null) {
192             if (filter.getServiceSolicitationUuidMask() == null) {
193                 addSolicitUuid(filter.getServiceSolicitationUuid().getUuid());
194             } else {
195                 addSolicitUuid(filter.getServiceSolicitationUuid().getUuid(),
196                         filter.getServiceSolicitationUuidMask().getUuid());
197             }
198         }
199         if (filter.getManufacturerData() != null) {
200             if (filter.getManufacturerDataMask() == null) {
201                 addManufacturerData(filter.getManufacturerId(), filter.getManufacturerData());
202             } else {
203                 addManufacturerData(filter.getManufacturerId(), 0xFFFF,
204                         filter.getManufacturerData(), filter.getManufacturerDataMask());
205             }
206         }
207         if (filter.getServiceDataUuid() != null && filter.getServiceData() != null) {
208             ParcelUuid serviceDataUuid = filter.getServiceDataUuid();
209             byte[] serviceData = filter.getServiceData();
210             byte[] serviceDataMask = filter.getServiceDataMask();
211             if (serviceDataMask == null) {
212                 serviceDataMask = new byte[serviceData.length];
213                 Arrays.fill(serviceDataMask, (byte) 0xFF);
214             }
215             serviceData = concate(serviceDataUuid, serviceData);
216             serviceDataMask = concate(serviceDataUuid, serviceDataMask);
217             if (serviceData != null && serviceDataMask != null) {
218                 addServiceData(serviceData, serviceDataMask);
219             }
220         }
221     }
222 
concate(ParcelUuid serviceDataUuid, byte[] serviceData)223     private byte[] concate(ParcelUuid serviceDataUuid, byte[] serviceData) {
224         byte[] uuid = BluetoothUuid.uuidToBytes(serviceDataUuid);
225 
226         int dataLen = uuid.length + (serviceData == null ? 0 : serviceData.length);
227         // If data is too long, don't add it to hardware scan filter.
228         if (dataLen > MAX_LEN_PER_FIELD) {
229             return null;
230         }
231         byte[] concated = new byte[dataLen];
232         System.arraycopy(uuid, 0, concated, 0, uuid.length);
233         if (serviceData != null) {
234             System.arraycopy(serviceData, 0, concated, uuid.length, serviceData.length);
235         }
236         return concated;
237     }
238 }
239