blob: 2174255a3619ae110f6d70633a7b3026f11ab424 [file] [log] [blame]
Wei Wang6d811182014-05-22 12:10:25 -07001/*
2 * Copyright (C) 2014 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
17package android.bluetooth.le;
18
19import android.annotation.Nullable;
Mathew Inwood4dc66d32018-08-01 15:07:20 +010020import android.annotation.UnsupportedAppUsage;
Wei Wang6d811182014-05-22 12:10:25 -070021import android.bluetooth.BluetoothUuid;
22import android.os.ParcelUuid;
Wei Wang6bf513d2014-08-01 11:12:37 -070023import android.util.ArrayMap;
Wei Wang6d811182014-05-22 12:10:25 -070024import android.util.Log;
Wei Wang6bf513d2014-08-01 11:12:37 -070025import android.util.SparseArray;
Wei Wang6d811182014-05-22 12:10:25 -070026
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.List;
Wei Wang6bf513d2014-08-01 11:12:37 -070030import java.util.Map;
Wei Wang6d811182014-05-22 12:10:25 -070031
32/**
33 * Represents a scan record from Bluetooth LE scan.
34 */
35public final class ScanRecord {
36
37 private static final String TAG = "ScanRecord";
38
39 // The following data type values are assigned by Bluetooth SIG.
40 // For more details refer to Bluetooth 4.1 specification, Volume 3, Part C, Section 18.
41 private static final int DATA_TYPE_FLAGS = 0x01;
42 private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02;
43 private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03;
44 private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04;
45 private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05;
46 private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06;
47 private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07;
48 private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08;
49 private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09;
50 private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
Jakub Pawlowski45033a72017-05-26 13:59:55 -070051 private static final int DATA_TYPE_SERVICE_DATA_16_BIT = 0x16;
52 private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20;
53 private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21;
Nitin Shivpure1555eae2018-04-02 13:45:45 +053054 private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT = 0x14;
55 private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT = 0x1F;
56 private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT = 0x15;
Wei Wang6d811182014-05-22 12:10:25 -070057 private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
58
59 // Flags of the advertising data.
60 private final int mAdvertiseFlags;
61
62 @Nullable
63 private final List<ParcelUuid> mServiceUuids;
Nitin Shivpure1555eae2018-04-02 13:45:45 +053064 @Nullable
65 private final List<ParcelUuid> mServiceSolicitationUuids;
Wei Wang6d811182014-05-22 12:10:25 -070066
Wei Wang6bf513d2014-08-01 11:12:37 -070067 private final SparseArray<byte[]> mManufacturerSpecificData;
Wei Wang6d811182014-05-22 12:10:25 -070068
Wei Wang6bf513d2014-08-01 11:12:37 -070069 private final Map<ParcelUuid, byte[]> mServiceData;
Wei Wang6d811182014-05-22 12:10:25 -070070
71 // Transmission power level(in dB).
72 private final int mTxPowerLevel;
73
74 // Local name of the Bluetooth LE device.
Wei Wangaf74e662014-07-09 14:03:42 -070075 private final String mDeviceName;
Wei Wang6d811182014-05-22 12:10:25 -070076
Wei Wang685c17582014-07-16 22:02:03 -070077 // Raw bytes of scan record.
78 private final byte[] mBytes;
79
Wei Wang6d811182014-05-22 12:10:25 -070080 /**
81 * Returns the advertising flags indicating the discoverable mode and capability of the device.
82 * Returns -1 if the flag field is not set.
83 */
84 public int getAdvertiseFlags() {
85 return mAdvertiseFlags;
86 }
87
88 /**
Wei Wangaf74e662014-07-09 14:03:42 -070089 * Returns a list of service UUIDs within the advertisement that are used to identify the
Wei Wang6bf513d2014-08-01 11:12:37 -070090 * bluetooth GATT services.
Wei Wang6d811182014-05-22 12:10:25 -070091 */
92 public List<ParcelUuid> getServiceUuids() {
93 return mServiceUuids;
94 }
95
96 /**
Nitin Shivpure1555eae2018-04-02 13:45:45 +053097 * Returns a list of service solicitation UUIDs within the advertisement that are used to
98 * identify the Bluetooth GATT services.
99 */
100 @Nullable
101 public List<ParcelUuid> getServiceSolicitationUuids() {
102 return mServiceSolicitationUuids;
103 }
104
105 /**
Wei Wang6bf513d2014-08-01 11:12:37 -0700106 * Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific
107 * data.
Wei Wang6d811182014-05-22 12:10:25 -0700108 */
Wei Wang6bf513d2014-08-01 11:12:37 -0700109 public SparseArray<byte[]> getManufacturerSpecificData() {
Wei Wang6d811182014-05-22 12:10:25 -0700110 return mManufacturerSpecificData;
111 }
112
113 /**
Wei Wang6bf513d2014-08-01 11:12:37 -0700114 * Returns the manufacturer specific data associated with the manufacturer id. Returns
115 * {@code null} if the {@code manufacturerId} is not found.
Wei Wang6d811182014-05-22 12:10:25 -0700116 */
Wei Wang6bf513d2014-08-01 11:12:37 -0700117 @Nullable
118 public byte[] getManufacturerSpecificData(int manufacturerId) {
Jakub Pawlowski4c2aa612018-11-28 23:36:53 +0100119 if (mManufacturerSpecificData == null) {
120 return null;
121 }
Wei Wang6bf513d2014-08-01 11:12:37 -0700122 return mManufacturerSpecificData.get(manufacturerId);
Wei Wang6d811182014-05-22 12:10:25 -0700123 }
124
125 /**
Wei Wang6bf513d2014-08-01 11:12:37 -0700126 * Returns a map of service UUID and its corresponding service data.
Wei Wang6d811182014-05-22 12:10:25 -0700127 */
Wei Wang6bf513d2014-08-01 11:12:37 -0700128 public Map<ParcelUuid, byte[]> getServiceData() {
Wei Wang6d811182014-05-22 12:10:25 -0700129 return mServiceData;
130 }
131
132 /**
Wei Wang6bf513d2014-08-01 11:12:37 -0700133 * Returns the service data byte array associated with the {@code serviceUuid}. Returns
134 * {@code null} if the {@code serviceDataUuid} is not found.
135 */
136 @Nullable
137 public byte[] getServiceData(ParcelUuid serviceDataUuid) {
Jakub Pawlowski59880122018-06-04 10:32:06 -0700138 if (serviceDataUuid == null || mServiceData == null) {
Wei Wang6bf513d2014-08-01 11:12:37 -0700139 return null;
140 }
141 return mServiceData.get(serviceDataUuid);
142 }
143
144 /**
Wei Wang6d811182014-05-22 12:10:25 -0700145 * Returns the transmission power level of the packet in dBm. Returns {@link Integer#MIN_VALUE}
146 * if the field is not set. This value can be used to calculate the path loss of a received
147 * packet using the following equation:
148 * <p>
149 * <code>pathloss = txPowerLevel - rssi</code>
150 */
151 public int getTxPowerLevel() {
152 return mTxPowerLevel;
153 }
154
155 /**
156 * Returns the local name of the BLE device. The is a UTF-8 encoded string.
157 */
158 @Nullable
Wei Wangaf74e662014-07-09 14:03:42 -0700159 public String getDeviceName() {
160 return mDeviceName;
Wei Wang6d811182014-05-22 12:10:25 -0700161 }
162
Wei Wang685c17582014-07-16 22:02:03 -0700163 /**
164 * Returns raw bytes of scan record.
165 */
166 public byte[] getBytes() {
167 return mBytes;
168 }
169
Wei Wang6d811182014-05-22 12:10:25 -0700170 private ScanRecord(List<ParcelUuid> serviceUuids,
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530171 List<ParcelUuid> serviceSolicitationUuids,
Wei Wang6bf513d2014-08-01 11:12:37 -0700172 SparseArray<byte[]> manufacturerData,
173 Map<ParcelUuid, byte[]> serviceData,
174 int advertiseFlags, int txPowerLevel,
Wei Wang685c17582014-07-16 22:02:03 -0700175 String localName, byte[] bytes) {
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530176 mServiceSolicitationUuids = serviceSolicitationUuids;
Wei Wang6d811182014-05-22 12:10:25 -0700177 mServiceUuids = serviceUuids;
Wei Wang6bf513d2014-08-01 11:12:37 -0700178 mManufacturerSpecificData = manufacturerData;
Wei Wang6d811182014-05-22 12:10:25 -0700179 mServiceData = serviceData;
Wei Wangaf74e662014-07-09 14:03:42 -0700180 mDeviceName = localName;
Wei Wang6d811182014-05-22 12:10:25 -0700181 mAdvertiseFlags = advertiseFlags;
182 mTxPowerLevel = txPowerLevel;
Wei Wang685c17582014-07-16 22:02:03 -0700183 mBytes = bytes;
Wei Wang6d811182014-05-22 12:10:25 -0700184 }
185
186 /**
187 * Parse scan record bytes to {@link ScanRecord}.
188 * <p>
189 * The format is defined in Bluetooth 4.1 specification, Volume 3, Part C, Section 11 and 18.
190 * <p>
191 * All numerical multi-byte entities and values shall use little-endian <strong>byte</strong>
192 * order.
193 *
194 * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
Wei Wang685c17582014-07-16 22:02:03 -0700195 * @hide
Wei Wang6d811182014-05-22 12:10:25 -0700196 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100197 @UnsupportedAppUsage
Wei Wang6d811182014-05-22 12:10:25 -0700198 public static ScanRecord parseFromBytes(byte[] scanRecord) {
199 if (scanRecord == null) {
200 return null;
201 }
202
203 int currentPos = 0;
204 int advertiseFlag = -1;
205 List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530206 List<ParcelUuid> serviceSolicitationUuids = new ArrayList<ParcelUuid>();
Wei Wang6d811182014-05-22 12:10:25 -0700207 String localName = null;
208 int txPowerLevel = Integer.MIN_VALUE;
Wei Wang6bf513d2014-08-01 11:12:37 -0700209
210 SparseArray<byte[]> manufacturerData = new SparseArray<byte[]>();
211 Map<ParcelUuid, byte[]> serviceData = new ArrayMap<ParcelUuid, byte[]>();
Wei Wang6d811182014-05-22 12:10:25 -0700212
213 try {
214 while (currentPos < scanRecord.length) {
215 // length is unsigned int.
216 int length = scanRecord[currentPos++] & 0xFF;
217 if (length == 0) {
218 break;
219 }
220 // Note the length includes the length of the field type itself.
221 int dataLength = length - 1;
222 // fieldType is unsigned int.
223 int fieldType = scanRecord[currentPos++] & 0xFF;
224 switch (fieldType) {
225 case DATA_TYPE_FLAGS:
226 advertiseFlag = scanRecord[currentPos] & 0xFF;
227 break;
228 case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL:
229 case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE:
230 parseServiceUuid(scanRecord, currentPos,
231 dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids);
232 break;
233 case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL:
234 case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE:
235 parseServiceUuid(scanRecord, currentPos, dataLength,
236 BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids);
237 break;
238 case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL:
239 case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE:
240 parseServiceUuid(scanRecord, currentPos, dataLength,
241 BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids);
242 break;
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530243 case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT:
244 parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
245 BluetoothUuid.UUID_BYTES_16_BIT, serviceSolicitationUuids);
246 break;
247 case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT:
248 parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
249 BluetoothUuid.UUID_BYTES_32_BIT, serviceSolicitationUuids);
250 break;
251 case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT:
252 parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
253 BluetoothUuid.UUID_BYTES_128_BIT, serviceSolicitationUuids);
254 break;
Wei Wang6d811182014-05-22 12:10:25 -0700255 case DATA_TYPE_LOCAL_NAME_SHORT:
256 case DATA_TYPE_LOCAL_NAME_COMPLETE:
257 localName = new String(
258 extractBytes(scanRecord, currentPos, dataLength));
259 break;
260 case DATA_TYPE_TX_POWER_LEVEL:
261 txPowerLevel = scanRecord[currentPos];
262 break;
Jakub Pawlowski45033a72017-05-26 13:59:55 -0700263 case DATA_TYPE_SERVICE_DATA_16_BIT:
264 case DATA_TYPE_SERVICE_DATA_32_BIT:
265 case DATA_TYPE_SERVICE_DATA_128_BIT:
Wei Wang6d811182014-05-22 12:10:25 -0700266 int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
Jakub Pawlowski45033a72017-05-26 13:59:55 -0700267 if (fieldType == DATA_TYPE_SERVICE_DATA_32_BIT) {
Jack Hea355e5e2017-08-22 16:06:54 -0700268 serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT;
Jakub Pawlowski45033a72017-05-26 13:59:55 -0700269 } else if (fieldType == DATA_TYPE_SERVICE_DATA_128_BIT) {
Jack Hea355e5e2017-08-22 16:06:54 -0700270 serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT;
Jakub Pawlowski45033a72017-05-26 13:59:55 -0700271 }
272
Wei Wang6d811182014-05-22 12:10:25 -0700273 byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
274 serviceUuidLength);
Wei Wang6bf513d2014-08-01 11:12:37 -0700275 ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom(
276 serviceDataUuidBytes);
277 byte[] serviceDataArray = extractBytes(scanRecord,
278 currentPos + serviceUuidLength, dataLength - serviceUuidLength);
279 serviceData.put(serviceDataUuid, serviceDataArray);
Wei Wang6d811182014-05-22 12:10:25 -0700280 break;
281 case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
Wei Wang6d811182014-05-22 12:10:25 -0700282 // The first two bytes of the manufacturer specific data are
283 // manufacturer ids in little endian.
Jack He2992cd02017-08-22 21:21:23 -0700284 int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8)
285 + (scanRecord[currentPos] & 0xFF);
Wei Wang6bf513d2014-08-01 11:12:37 -0700286 byte[] manufacturerDataBytes = extractBytes(scanRecord, currentPos + 2,
Wei Wangab2ed622014-07-25 15:14:55 -0700287 dataLength - 2);
Wei Wang6bf513d2014-08-01 11:12:37 -0700288 manufacturerData.put(manufacturerId, manufacturerDataBytes);
Wei Wang6d811182014-05-22 12:10:25 -0700289 break;
290 default:
291 // Just ignore, we don't handle such data type.
292 break;
293 }
294 currentPos += dataLength;
295 }
296
297 if (serviceUuids.isEmpty()) {
298 serviceUuids = null;
299 }
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530300 if (serviceSolicitationUuids.isEmpty()) {
301 serviceSolicitationUuids = null;
302 }
303 return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData,
304 serviceData, advertiseFlag, txPowerLevel, localName, scanRecord);
Prerepa Viswanadham621085e2014-08-13 16:52:55 -0700305 } catch (Exception e) {
Wei Wang6d811182014-05-22 12:10:25 -0700306 Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord));
Prerepa Viswanadham621085e2014-08-13 16:52:55 -0700307 // As the record is invalid, ignore all the parsed results for this packet
308 // and return an empty record with raw scanRecord bytes in results
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530309 return new ScanRecord(null, null, null, null, -1, Integer.MIN_VALUE, null, scanRecord);
Wei Wang6d811182014-05-22 12:10:25 -0700310 }
311 }
312
Wei Wang685c17582014-07-16 22:02:03 -0700313 @Override
314 public String toString() {
315 return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530316 + ", mServiceSolicitationUuids=" + mServiceSolicitationUuids
Jack Hea355e5e2017-08-22 16:06:54 -0700317 + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(
318 mManufacturerSpecificData)
Wei Wang833559d2014-08-29 10:26:13 -0700319 + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData)
Wei Wang685c17582014-07-16 22:02:03 -0700320 + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]";
321 }
322
Wei Wangaf74e662014-07-09 14:03:42 -0700323 // Parse service UUIDs.
Wei Wang6d811182014-05-22 12:10:25 -0700324 private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength,
325 int uuidLength, List<ParcelUuid> serviceUuids) {
326 while (dataLength > 0) {
327 byte[] uuidBytes = extractBytes(scanRecord, currentPos,
328 uuidLength);
329 serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
330 dataLength -= uuidLength;
331 currentPos += uuidLength;
332 }
333 return currentPos;
334 }
335
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530336 /**
337 * Parse service Solicitation UUIDs.
338 */
339 private static int parseServiceSolicitationUuid(byte[] scanRecord, int currentPos,
340 int dataLength, int uuidLength, List<ParcelUuid> serviceSolicitationUuids) {
341 while (dataLength > 0) {
342 byte[] uuidBytes = extractBytes(scanRecord, currentPos, uuidLength);
343 serviceSolicitationUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
344 dataLength -= uuidLength;
345 currentPos += uuidLength;
346 }
347 return currentPos;
348 }
349
Wei Wang6d811182014-05-22 12:10:25 -0700350 // Helper method to extract bytes from byte array.
351 private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
352 byte[] bytes = new byte[length];
353 System.arraycopy(scanRecord, start, bytes, 0, length);
354 return bytes;
355 }
356}