blob: 97e3f5221c722021925d5c33cd979d987dbdb964 [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
Jakub Pawlowski64307cc2019-04-10 18:48:57 +020019import android.annotation.NonNull;
Wei Wang6d811182014-05-22 12:10:25 -070020import android.annotation.Nullable;
Mathew Inwood4dc66d32018-08-01 15:07:20 +010021import android.annotation.UnsupportedAppUsage;
Wei Wang6d811182014-05-22 12:10:25 -070022import android.bluetooth.BluetoothUuid;
23import android.os.ParcelUuid;
Wei Wang6bf513d2014-08-01 11:12:37 -070024import android.util.ArrayMap;
Wei Wang6d811182014-05-22 12:10:25 -070025import android.util.Log;
Wei Wang6bf513d2014-08-01 11:12:37 -070026import android.util.SparseArray;
Wei Wang6d811182014-05-22 12:10:25 -070027
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.List;
Wei Wang6bf513d2014-08-01 11:12:37 -070031import java.util.Map;
Wei Wang6d811182014-05-22 12:10:25 -070032
33/**
34 * Represents a scan record from Bluetooth LE scan.
35 */
36public final class ScanRecord {
37
38 private static final String TAG = "ScanRecord";
39
40 // The following data type values are assigned by Bluetooth SIG.
41 // For more details refer to Bluetooth 4.1 specification, Volume 3, Part C, Section 18.
42 private static final int DATA_TYPE_FLAGS = 0x01;
43 private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02;
44 private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03;
45 private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04;
46 private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05;
47 private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06;
48 private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07;
49 private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08;
50 private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09;
51 private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
Jakub Pawlowski45033a72017-05-26 13:59:55 -070052 private static final int DATA_TYPE_SERVICE_DATA_16_BIT = 0x16;
53 private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20;
54 private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21;
Nitin Shivpure1555eae2018-04-02 13:45:45 +053055 private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT = 0x14;
56 private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT = 0x1F;
57 private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT = 0x15;
Wei Wang6d811182014-05-22 12:10:25 -070058 private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
59
60 // Flags of the advertising data.
61 private final int mAdvertiseFlags;
62
63 @Nullable
64 private final List<ParcelUuid> mServiceUuids;
Nitin Shivpure1555eae2018-04-02 13:45:45 +053065 @Nullable
66 private final List<ParcelUuid> mServiceSolicitationUuids;
Wei Wang6d811182014-05-22 12:10:25 -070067
Wei Wang6bf513d2014-08-01 11:12:37 -070068 private final SparseArray<byte[]> mManufacturerSpecificData;
Wei Wang6d811182014-05-22 12:10:25 -070069
Wei Wang6bf513d2014-08-01 11:12:37 -070070 private final Map<ParcelUuid, byte[]> mServiceData;
Wei Wang6d811182014-05-22 12:10:25 -070071
72 // Transmission power level(in dB).
73 private final int mTxPowerLevel;
74
75 // Local name of the Bluetooth LE device.
Wei Wangaf74e662014-07-09 14:03:42 -070076 private final String mDeviceName;
Wei Wang6d811182014-05-22 12:10:25 -070077
Wei Wang685c17582014-07-16 22:02:03 -070078 // Raw bytes of scan record.
79 private final byte[] mBytes;
80
Wei Wang6d811182014-05-22 12:10:25 -070081 /**
82 * Returns the advertising flags indicating the discoverable mode and capability of the device.
83 * Returns -1 if the flag field is not set.
84 */
85 public int getAdvertiseFlags() {
86 return mAdvertiseFlags;
87 }
88
89 /**
Wei Wangaf74e662014-07-09 14:03:42 -070090 * Returns a list of service UUIDs within the advertisement that are used to identify the
Wei Wang6bf513d2014-08-01 11:12:37 -070091 * bluetooth GATT services.
Wei Wang6d811182014-05-22 12:10:25 -070092 */
93 public List<ParcelUuid> getServiceUuids() {
94 return mServiceUuids;
95 }
96
97 /**
Nitin Shivpure1555eae2018-04-02 13:45:45 +053098 * Returns a list of service solicitation UUIDs within the advertisement that are used to
99 * identify the Bluetooth GATT services.
100 */
Jakub Pawlowski64307cc2019-04-10 18:48:57 +0200101 @NonNull
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530102 public List<ParcelUuid> getServiceSolicitationUuids() {
103 return mServiceSolicitationUuids;
104 }
105
106 /**
Wei Wang6bf513d2014-08-01 11:12:37 -0700107 * Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific
108 * data.
Wei Wang6d811182014-05-22 12:10:25 -0700109 */
Wei Wang6bf513d2014-08-01 11:12:37 -0700110 public SparseArray<byte[]> getManufacturerSpecificData() {
Wei Wang6d811182014-05-22 12:10:25 -0700111 return mManufacturerSpecificData;
112 }
113
114 /**
Wei Wang6bf513d2014-08-01 11:12:37 -0700115 * Returns the manufacturer specific data associated with the manufacturer id. Returns
116 * {@code null} if the {@code manufacturerId} is not found.
Wei Wang6d811182014-05-22 12:10:25 -0700117 */
Wei Wang6bf513d2014-08-01 11:12:37 -0700118 @Nullable
119 public byte[] getManufacturerSpecificData(int manufacturerId) {
Jakub Pawlowski4c2aa612018-11-28 23:36:53 +0100120 if (mManufacturerSpecificData == null) {
121 return null;
122 }
Wei Wang6bf513d2014-08-01 11:12:37 -0700123 return mManufacturerSpecificData.get(manufacturerId);
Wei Wang6d811182014-05-22 12:10:25 -0700124 }
125
126 /**
Wei Wang6bf513d2014-08-01 11:12:37 -0700127 * Returns a map of service UUID and its corresponding service data.
Wei Wang6d811182014-05-22 12:10:25 -0700128 */
Wei Wang6bf513d2014-08-01 11:12:37 -0700129 public Map<ParcelUuid, byte[]> getServiceData() {
Wei Wang6d811182014-05-22 12:10:25 -0700130 return mServiceData;
131 }
132
133 /**
Wei Wang6bf513d2014-08-01 11:12:37 -0700134 * Returns the service data byte array associated with the {@code serviceUuid}. Returns
135 * {@code null} if the {@code serviceDataUuid} is not found.
136 */
137 @Nullable
138 public byte[] getServiceData(ParcelUuid serviceDataUuid) {
Jakub Pawlowski59880122018-06-04 10:32:06 -0700139 if (serviceDataUuid == null || mServiceData == null) {
Wei Wang6bf513d2014-08-01 11:12:37 -0700140 return null;
141 }
142 return mServiceData.get(serviceDataUuid);
143 }
144
145 /**
Wei Wang6d811182014-05-22 12:10:25 -0700146 * Returns the transmission power level of the packet in dBm. Returns {@link Integer#MIN_VALUE}
147 * if the field is not set. This value can be used to calculate the path loss of a received
148 * packet using the following equation:
149 * <p>
150 * <code>pathloss = txPowerLevel - rssi</code>
151 */
152 public int getTxPowerLevel() {
153 return mTxPowerLevel;
154 }
155
156 /**
Myles Watson1254da62019-10-11 15:49:37 -0700157 * Returns the local name of the BLE device. This is a UTF-8 encoded string.
Wei Wang6d811182014-05-22 12:10:25 -0700158 */
159 @Nullable
Wei Wangaf74e662014-07-09 14:03:42 -0700160 public String getDeviceName() {
161 return mDeviceName;
Wei Wang6d811182014-05-22 12:10:25 -0700162 }
163
Wei Wang685c17582014-07-16 22:02:03 -0700164 /**
165 * Returns raw bytes of scan record.
166 */
167 public byte[] getBytes() {
168 return mBytes;
169 }
170
Wei Wang6d811182014-05-22 12:10:25 -0700171 private ScanRecord(List<ParcelUuid> serviceUuids,
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530172 List<ParcelUuid> serviceSolicitationUuids,
Wei Wang6bf513d2014-08-01 11:12:37 -0700173 SparseArray<byte[]> manufacturerData,
174 Map<ParcelUuid, byte[]> serviceData,
175 int advertiseFlags, int txPowerLevel,
Wei Wang685c17582014-07-16 22:02:03 -0700176 String localName, byte[] bytes) {
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530177 mServiceSolicitationUuids = serviceSolicitationUuids;
Wei Wang6d811182014-05-22 12:10:25 -0700178 mServiceUuids = serviceUuids;
Wei Wang6bf513d2014-08-01 11:12:37 -0700179 mManufacturerSpecificData = manufacturerData;
Wei Wang6d811182014-05-22 12:10:25 -0700180 mServiceData = serviceData;
Wei Wangaf74e662014-07-09 14:03:42 -0700181 mDeviceName = localName;
Wei Wang6d811182014-05-22 12:10:25 -0700182 mAdvertiseFlags = advertiseFlags;
183 mTxPowerLevel = txPowerLevel;
Wei Wang685c17582014-07-16 22:02:03 -0700184 mBytes = bytes;
Wei Wang6d811182014-05-22 12:10:25 -0700185 }
186
187 /**
188 * Parse scan record bytes to {@link ScanRecord}.
189 * <p>
190 * The format is defined in Bluetooth 4.1 specification, Volume 3, Part C, Section 11 and 18.
191 * <p>
192 * All numerical multi-byte entities and values shall use little-endian <strong>byte</strong>
193 * order.
194 *
195 * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
Wei Wang685c17582014-07-16 22:02:03 -0700196 * @hide
Wei Wang6d811182014-05-22 12:10:25 -0700197 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100198 @UnsupportedAppUsage
Wei Wang6d811182014-05-22 12:10:25 -0700199 public static ScanRecord parseFromBytes(byte[] scanRecord) {
200 if (scanRecord == null) {
201 return null;
202 }
203
204 int currentPos = 0;
205 int advertiseFlag = -1;
206 List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530207 List<ParcelUuid> serviceSolicitationUuids = new ArrayList<ParcelUuid>();
Wei Wang6d811182014-05-22 12:10:25 -0700208 String localName = null;
209 int txPowerLevel = Integer.MIN_VALUE;
Wei Wang6bf513d2014-08-01 11:12:37 -0700210
211 SparseArray<byte[]> manufacturerData = new SparseArray<byte[]>();
212 Map<ParcelUuid, byte[]> serviceData = new ArrayMap<ParcelUuid, byte[]>();
Wei Wang6d811182014-05-22 12:10:25 -0700213
214 try {
215 while (currentPos < scanRecord.length) {
216 // length is unsigned int.
217 int length = scanRecord[currentPos++] & 0xFF;
218 if (length == 0) {
219 break;
220 }
221 // Note the length includes the length of the field type itself.
222 int dataLength = length - 1;
223 // fieldType is unsigned int.
224 int fieldType = scanRecord[currentPos++] & 0xFF;
225 switch (fieldType) {
226 case DATA_TYPE_FLAGS:
227 advertiseFlag = scanRecord[currentPos] & 0xFF;
228 break;
229 case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL:
230 case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE:
231 parseServiceUuid(scanRecord, currentPos,
232 dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids);
233 break;
234 case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL:
235 case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE:
236 parseServiceUuid(scanRecord, currentPos, dataLength,
237 BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids);
238 break;
239 case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL:
240 case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE:
241 parseServiceUuid(scanRecord, currentPos, dataLength,
242 BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids);
243 break;
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530244 case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT:
245 parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
246 BluetoothUuid.UUID_BYTES_16_BIT, serviceSolicitationUuids);
247 break;
248 case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT:
249 parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
250 BluetoothUuid.UUID_BYTES_32_BIT, serviceSolicitationUuids);
251 break;
252 case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT:
253 parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
254 BluetoothUuid.UUID_BYTES_128_BIT, serviceSolicitationUuids);
255 break;
Wei Wang6d811182014-05-22 12:10:25 -0700256 case DATA_TYPE_LOCAL_NAME_SHORT:
257 case DATA_TYPE_LOCAL_NAME_COMPLETE:
258 localName = new String(
259 extractBytes(scanRecord, currentPos, dataLength));
260 break;
261 case DATA_TYPE_TX_POWER_LEVEL:
262 txPowerLevel = scanRecord[currentPos];
263 break;
Jakub Pawlowski45033a72017-05-26 13:59:55 -0700264 case DATA_TYPE_SERVICE_DATA_16_BIT:
265 case DATA_TYPE_SERVICE_DATA_32_BIT:
266 case DATA_TYPE_SERVICE_DATA_128_BIT:
Wei Wang6d811182014-05-22 12:10:25 -0700267 int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
Jakub Pawlowski45033a72017-05-26 13:59:55 -0700268 if (fieldType == DATA_TYPE_SERVICE_DATA_32_BIT) {
Jack Hea355e5e2017-08-22 16:06:54 -0700269 serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT;
Jakub Pawlowski45033a72017-05-26 13:59:55 -0700270 } else if (fieldType == DATA_TYPE_SERVICE_DATA_128_BIT) {
Jack Hea355e5e2017-08-22 16:06:54 -0700271 serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT;
Jakub Pawlowski45033a72017-05-26 13:59:55 -0700272 }
273
Wei Wang6d811182014-05-22 12:10:25 -0700274 byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
275 serviceUuidLength);
Wei Wang6bf513d2014-08-01 11:12:37 -0700276 ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom(
277 serviceDataUuidBytes);
278 byte[] serviceDataArray = extractBytes(scanRecord,
279 currentPos + serviceUuidLength, dataLength - serviceUuidLength);
280 serviceData.put(serviceDataUuid, serviceDataArray);
Wei Wang6d811182014-05-22 12:10:25 -0700281 break;
282 case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
Wei Wang6d811182014-05-22 12:10:25 -0700283 // The first two bytes of the manufacturer specific data are
284 // manufacturer ids in little endian.
Jack He2992cd02017-08-22 21:21:23 -0700285 int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8)
286 + (scanRecord[currentPos] & 0xFF);
Wei Wang6bf513d2014-08-01 11:12:37 -0700287 byte[] manufacturerDataBytes = extractBytes(scanRecord, currentPos + 2,
Wei Wangab2ed622014-07-25 15:14:55 -0700288 dataLength - 2);
Wei Wang6bf513d2014-08-01 11:12:37 -0700289 manufacturerData.put(manufacturerId, manufacturerDataBytes);
Wei Wang6d811182014-05-22 12:10:25 -0700290 break;
291 default:
292 // Just ignore, we don't handle such data type.
293 break;
294 }
295 currentPos += dataLength;
296 }
297
298 if (serviceUuids.isEmpty()) {
299 serviceUuids = null;
300 }
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530301 return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData,
302 serviceData, advertiseFlag, txPowerLevel, localName, scanRecord);
Prerepa Viswanadham621085e2014-08-13 16:52:55 -0700303 } catch (Exception e) {
Wei Wang6d811182014-05-22 12:10:25 -0700304 Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord));
Prerepa Viswanadham621085e2014-08-13 16:52:55 -0700305 // As the record is invalid, ignore all the parsed results for this packet
306 // and return an empty record with raw scanRecord bytes in results
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530307 return new ScanRecord(null, null, null, null, -1, Integer.MIN_VALUE, null, scanRecord);
Wei Wang6d811182014-05-22 12:10:25 -0700308 }
309 }
310
Wei Wang685c17582014-07-16 22:02:03 -0700311 @Override
312 public String toString() {
313 return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530314 + ", mServiceSolicitationUuids=" + mServiceSolicitationUuids
Jack Hea355e5e2017-08-22 16:06:54 -0700315 + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(
316 mManufacturerSpecificData)
Wei Wang833559d2014-08-29 10:26:13 -0700317 + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData)
Wei Wang685c17582014-07-16 22:02:03 -0700318 + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]";
319 }
320
Wei Wangaf74e662014-07-09 14:03:42 -0700321 // Parse service UUIDs.
Wei Wang6d811182014-05-22 12:10:25 -0700322 private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength,
323 int uuidLength, List<ParcelUuid> serviceUuids) {
324 while (dataLength > 0) {
325 byte[] uuidBytes = extractBytes(scanRecord, currentPos,
326 uuidLength);
327 serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
328 dataLength -= uuidLength;
329 currentPos += uuidLength;
330 }
331 return currentPos;
332 }
333
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530334 /**
335 * Parse service Solicitation UUIDs.
336 */
337 private static int parseServiceSolicitationUuid(byte[] scanRecord, int currentPos,
338 int dataLength, int uuidLength, List<ParcelUuid> serviceSolicitationUuids) {
339 while (dataLength > 0) {
340 byte[] uuidBytes = extractBytes(scanRecord, currentPos, uuidLength);
341 serviceSolicitationUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
342 dataLength -= uuidLength;
343 currentPos += uuidLength;
344 }
345 return currentPos;
346 }
347
Wei Wang6d811182014-05-22 12:10:25 -0700348 // Helper method to extract bytes from byte array.
349 private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
350 byte[] bytes = new byte[length];
351 System.arraycopy(scanRecord, start, bytes, 0, length);
352 return bytes;
353 }
354}