1 /*
2  * Copyright (C) 2012 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.nfc.handover;
18 
19 import android.bluetooth.BluetoothAdapter;
20 import android.bluetooth.BluetoothClass;
21 import android.bluetooth.BluetoothDevice;
22 import android.bluetooth.BluetoothUuid;
23 import android.bluetooth.OobData;
24 import android.nfc.FormatException;
25 import android.nfc.NdefMessage;
26 import android.nfc.NdefRecord;
27 import android.os.Parcel;
28 import android.os.ParcelUuid;
29 import android.sysprop.NfcProperties;
30 import android.util.Log;
31 
32 import java.nio.BufferUnderflowException;
33 import java.nio.ByteBuffer;
34 import java.nio.ByteOrder;
35 import java.nio.charset.StandardCharsets;
36 import java.util.Arrays;
37 import java.util.Random;
38 
39 /**
40  * Manages handover of NFC to other technologies.
41  */
42 public class HandoverDataParser {
43     private static final String TAG = "NfcHandover";
44     private static final boolean DBG =
45             NfcProperties.debug_enabled().orElse(true);
46 
47     private static final byte[] TYPE_BT_OOB = "application/vnd.bluetooth.ep.oob"
48             .getBytes(StandardCharsets.US_ASCII);
49     private static final byte[] TYPE_BLE_OOB = "application/vnd.bluetooth.le.oob"
50             .getBytes(StandardCharsets.US_ASCII);
51 
52     private static final byte[] TYPE_NOKIA = "nokia.com:bt".getBytes(StandardCharsets.US_ASCII);
53 
54     private static final byte[] RTD_COLLISION_RESOLUTION = {0x63, 0x72}; // "cr";
55 
56     private static final int CARRIER_POWER_STATE_INACTIVE = 0;
57     private static final int CARRIER_POWER_STATE_ACTIVE = 1;
58     private static final int CARRIER_POWER_STATE_ACTIVATING = 2;
59     private static final int CARRIER_POWER_STATE_UNKNOWN = 3;
60 
61     private static final int BT_HANDOVER_TYPE_MAC = 0x1B;
62     private static final int BT_HANDOVER_TYPE_LE_ROLE = 0x1C;
63     private static final int BT_HANDOVER_TYPE_LONG_LOCAL_NAME = 0x09;
64     private static final int BT_HANDOVER_TYPE_SHORT_LOCAL_NAME = 0x08;
65     private static final int BT_HANDOVER_TYPE_16_BIT_UUIDS_PARTIAL = 0x02;
66     private static final int BT_HANDOVER_TYPE_16_BIT_UUIDS_COMPLETE = 0x03;
67     private static final int BT_HANDOVER_TYPE_32_BIT_UUIDS_PARTIAL = 0x04;
68     private static final int BT_HANDOVER_TYPE_32_BIT_UUIDS_COMPLETE = 0x05;
69     private static final int BT_HANDOVER_TYPE_128_BIT_UUIDS_PARTIAL = 0x06;
70     private static final int BT_HANDOVER_TYPE_128_BIT_UUIDS_COMPLETE = 0x07;
71     private static final int BT_HANDOVER_TYPE_CLASS_OF_DEVICE = 0x0D;
72     private static final int BT_HANDOVER_TYPE_SECURITY_MANAGER_TK = 0x10;
73     private static final int BT_HANDOVER_TYPE_APPEARANCE = 0x19;
74     private static final int BT_HANDOVER_TYPE_LE_SC_CONFIRMATION = 0x22;
75     private static final int BT_HANDOVER_TYPE_LE_SC_RANDOM = 0x23;
76 
77     public static final int BT_HANDOVER_LE_ROLE_CENTRAL_ONLY = 0x01;
78 
79     public static final int SECURITY_MANAGER_TK_SIZE = 16;
80     public static final int SECURITY_MANAGER_LE_SC_C_SIZE = 16;
81     public static final int SECURITY_MANAGER_LE_SC_R_SIZE = 16;
82     private static final int CLASS_OF_DEVICE_SIZE = 3;
83 
84     private final BluetoothAdapter mBluetoothAdapter;
85 
86     private final Object mLock = new Object();
87     // Variables below synchronized on mLock
88 
89     private String mLocalBluetoothAddress;
90 
91     public static class BluetoothHandoverData {
92         public boolean valid = false;
93         public BluetoothDevice device;
94         public String name;
95         public boolean carrierActivating = false;
96         public int transport = BluetoothDevice.TRANSPORT_AUTO;
97         public OobData oobData;
98         public ParcelUuid[] uuids = null;
99         public BluetoothClass btClass = null;
100     }
101 
102     public static class IncomingHandoverData {
103         public final NdefMessage handoverSelect;
104         public final BluetoothHandoverData handoverData;
105 
IncomingHandoverData(NdefMessage handoverSelect, BluetoothHandoverData handoverData)106         public IncomingHandoverData(NdefMessage handoverSelect,
107                                     BluetoothHandoverData handoverData) {
108             this.handoverSelect = handoverSelect;
109             this.handoverData = handoverData;
110         }
111     }
112 
HandoverDataParser()113     public HandoverDataParser() {
114         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
115     }
116 
createCollisionRecord()117     static NdefRecord createCollisionRecord() {
118         byte[] random = new byte[2];
119         new Random().nextBytes(random);
120         return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, RTD_COLLISION_RESOLUTION, null, random);
121     }
122 
createBluetoothAlternateCarrierRecord(boolean activating)123     NdefRecord createBluetoothAlternateCarrierRecord(boolean activating) {
124         byte[] payload = new byte[4];
125         payload[0] = (byte) (activating ? CARRIER_POWER_STATE_ACTIVATING :
126             CARRIER_POWER_STATE_ACTIVE);  // Carrier Power State: Activating or active
127         payload[1] = 1;   // length of carrier data reference
128         payload[2] = 'b'; // carrier data reference: ID for Bluetooth OOB data record
129         payload[3] = 0;  // Auxiliary data reference count
130         return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_ALTERNATIVE_CARRIER, null,
131                 payload);
132     }
133 
createBluetoothOobDataRecord()134     NdefRecord createBluetoothOobDataRecord() {
135         byte[] payload = new byte[8];
136         // Note: this field should be little-endian per the BTSSP spec
137         // The Android 4.1 implementation used big-endian order here.
138         // No single Android implementation has ever interpreted this
139         // length field when parsing this record though.
140         payload[0] = (byte) (payload.length & 0xFF);
141         payload[1] = (byte) ((payload.length >> 8) & 0xFF);
142 
143         synchronized (mLock) {
144             if (mLocalBluetoothAddress == null) {
145                 mLocalBluetoothAddress = mBluetoothAdapter.getAddress();
146             }
147 
148             byte[] addressBytes = addressToReverseBytes(mLocalBluetoothAddress);
149             if (addressBytes != null) {
150                 System.arraycopy(addressBytes, 0, payload, 2, 6);
151             } else {
152                 // don't cache unknown result
153                 mLocalBluetoothAddress = null;
154             }
155         }
156 
157         return new NdefRecord(NdefRecord.TNF_MIME_MEDIA, TYPE_BT_OOB, new byte[]{'b'}, payload);
158     }
159 
isHandoverSupported()160     public boolean isHandoverSupported() {
161         return (mBluetoothAdapter != null);
162     }
163 
createHandoverRequestMessage()164     public NdefMessage createHandoverRequestMessage() {
165         if (mBluetoothAdapter == null) {
166             return null;
167         }
168 
169         NdefRecord[] dataRecords = new NdefRecord[] {
170                 createBluetoothOobDataRecord()
171         };
172         return new NdefMessage(
173                 createHandoverRequestRecord(),
174                 dataRecords);
175     }
176 
createBluetoothHandoverSelectMessage(boolean activating)177     NdefMessage createBluetoothHandoverSelectMessage(boolean activating) {
178         return new NdefMessage(createHandoverSelectRecord(
179                 createBluetoothAlternateCarrierRecord(activating)),
180                 createBluetoothOobDataRecord());
181     }
182 
createHandoverSelectRecord(NdefRecord alternateCarrier)183     NdefRecord createHandoverSelectRecord(NdefRecord alternateCarrier) {
184         NdefMessage nestedMessage = new NdefMessage(alternateCarrier);
185         byte[] nestedPayload = nestedMessage.toByteArray();
186 
187         ByteBuffer payload = ByteBuffer.allocate(nestedPayload.length + 1);
188         payload.put((byte)0x12);  // connection handover v1.2
189         payload.put(nestedPayload);
190 
191         byte[] payloadBytes = new byte[payload.position()];
192         payload.position(0);
193         payload.get(payloadBytes);
194         return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_HANDOVER_SELECT, null,
195                 payloadBytes);
196     }
197 
createHandoverRequestRecord()198     NdefRecord createHandoverRequestRecord() {
199         NdefRecord[] messages = new NdefRecord[] {
200                 createBluetoothAlternateCarrierRecord(false)
201         };
202 
203         NdefMessage nestedMessage = new NdefMessage(createCollisionRecord(), messages);
204 
205         byte[] nestedPayload = nestedMessage.toByteArray();
206 
207         ByteBuffer payload = ByteBuffer.allocate(nestedPayload.length + 1);
208         payload.put((byte) 0x12);  // connection handover v1.2
209         payload.put(nestedMessage.toByteArray());
210 
211         byte[] payloadBytes = new byte[payload.position()];
212         payload.position(0);
213         payload.get(payloadBytes);
214         return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_HANDOVER_REQUEST, null,
215                 payloadBytes);
216     }
217 
218     /**
219      * Returns null if message is not a Handover Request,
220      * returns the IncomingHandoverData (Hs + parsed data) if it is.
221      */
getIncomingHandoverData(NdefMessage handoverRequest)222     public IncomingHandoverData getIncomingHandoverData(NdefMessage handoverRequest) {
223         if (handoverRequest == null) return null;
224         if (mBluetoothAdapter == null) return null;
225 
226         if (DBG) Log.d(TAG, "getIncomingHandoverData():" + handoverRequest.toString());
227 
228         NdefRecord handoverRequestRecord = handoverRequest.getRecords()[0];
229         if (handoverRequestRecord.getTnf() != NdefRecord.TNF_WELL_KNOWN) {
230             return null;
231         }
232 
233         if (!Arrays.equals(handoverRequestRecord.getType(), NdefRecord.RTD_HANDOVER_REQUEST)) {
234             return null;
235         }
236 
237         // we have a handover request, look for BT OOB record
238         BluetoothHandoverData bluetoothData = null;
239         for (NdefRecord dataRecord : handoverRequest.getRecords()) {
240             if (dataRecord.getTnf() == NdefRecord.TNF_MIME_MEDIA) {
241                 if (Arrays.equals(dataRecord.getType(), TYPE_BT_OOB)) {
242                     bluetoothData = parseBtOob(ByteBuffer.wrap(dataRecord.getPayload()));
243                 }
244             }
245         }
246 
247         NdefMessage hs = tryBluetoothHandoverRequest(bluetoothData);
248         if (hs != null) {
249             return new IncomingHandoverData(hs, bluetoothData);
250         }
251 
252         return null;
253     }
254 
getOutgoingHandoverData(NdefMessage handoverSelect)255     public BluetoothHandoverData getOutgoingHandoverData(NdefMessage handoverSelect) {
256         return parseBluetooth(handoverSelect);
257     }
258 
tryBluetoothHandoverRequest(BluetoothHandoverData bluetoothData)259     private NdefMessage tryBluetoothHandoverRequest(BluetoothHandoverData bluetoothData) {
260         NdefMessage selectMessage = null;
261         if (bluetoothData != null) {
262             // Note: there could be a race where we conclude
263             // that Bluetooth is already enabled, and shortly
264             // after the user turns it off. That will cause
265             // the transfer to fail, but there's nothing
266             // much we can do about it anyway. It shouldn't
267             // be common for the user to be changing BT settings
268             // while waiting to receive a picture.
269             boolean bluetoothActivating = !mBluetoothAdapter.isEnabled();
270 
271             // return BT OOB record so they can perform handover
272             selectMessage = (createBluetoothHandoverSelectMessage(bluetoothActivating));
273             if (DBG) Log.d(TAG, "Waiting for incoming transfer, [" +
274                     bluetoothData.device.getAddress() + "]->[" + mLocalBluetoothAddress + "]");
275         }
276 
277         return selectMessage;
278     }
279 
280 
281 
isCarrierActivating(NdefRecord handoverRec, byte[] carrierId)282     boolean isCarrierActivating(NdefRecord handoverRec, byte[] carrierId) {
283         byte[] payload = handoverRec.getPayload();
284         if (payload == null || payload.length <= 1) return false;
285         // Skip version
286         byte[] payloadNdef = new byte[payload.length - 1];
287         System.arraycopy(payload, 1, payloadNdef, 0, payload.length - 1);
288         NdefMessage msg;
289         try {
290             msg = new NdefMessage(payloadNdef);
291         } catch (FormatException e) {
292             return false;
293         }
294 
295         for (NdefRecord alt : msg.getRecords()) {
296             byte[] acPayload = alt.getPayload();
297             if (acPayload != null) {
298                 ByteBuffer buf = ByteBuffer.wrap(acPayload);
299                 int cps = buf.get() & 0x03; // Carrier Power State is in lower 2 bits
300                 int carrierRefLength = buf.get() & 0xFF;
301                 if (carrierRefLength != carrierId.length) return false;
302 
303                 byte[] carrierRefId = new byte[carrierRefLength];
304                 buf.get(carrierRefId);
305                 if (Arrays.equals(carrierRefId, carrierId)) {
306                     // Found match, returning whether power state is activating
307                     return (cps == CARRIER_POWER_STATE_ACTIVATING);
308                 }
309             }
310         }
311 
312         return true;
313     }
314 
parseBluetoothHandoverSelect(NdefMessage m)315     BluetoothHandoverData parseBluetoothHandoverSelect(NdefMessage m) {
316         // TODO we could parse this a lot more strictly; right now
317         // we just search for a BT OOB record, and try to cross-reference
318         // the carrier state inside the 'hs' payload.
319         for (NdefRecord oob : m.getRecords()) {
320             if (oob.getTnf() == NdefRecord.TNF_MIME_MEDIA &&
321                     Arrays.equals(oob.getType(), TYPE_BT_OOB)) {
322                 BluetoothHandoverData data = parseBtOob(ByteBuffer.wrap(oob.getPayload()));
323                 if (data != null && isCarrierActivating(m.getRecords()[0], oob.getId())) {
324                     data.carrierActivating = true;
325                 }
326                 return data;
327             }
328 
329             if (oob.getTnf() == NdefRecord.TNF_MIME_MEDIA &&
330                     Arrays.equals(oob.getType(), TYPE_BLE_OOB)) {
331                 return parseBleOob(ByteBuffer.wrap(oob.getPayload()));
332             }
333         }
334 
335         return null;
336     }
337 
parseBluetooth(NdefMessage m)338     public BluetoothHandoverData parseBluetooth(NdefMessage m) {
339         NdefRecord r = m.getRecords()[0];
340         short tnf = r.getTnf();
341         byte[] type = r.getType();
342 
343         // Check for BT OOB record
344         if (r.getTnf() == NdefRecord.TNF_MIME_MEDIA && Arrays.equals(r.getType(), TYPE_BT_OOB)) {
345             return parseBtOob(ByteBuffer.wrap(r.getPayload()));
346         }
347 
348         // Check for BLE OOB record
349         if (r.getTnf() == NdefRecord.TNF_MIME_MEDIA && Arrays.equals(r.getType(), TYPE_BLE_OOB)) {
350             return parseBleOob(ByteBuffer.wrap(r.getPayload()));
351         }
352 
353         // Check for Handover Select, followed by a BT OOB record
354         if (tnf == NdefRecord.TNF_WELL_KNOWN &&
355                 Arrays.equals(type, NdefRecord.RTD_HANDOVER_SELECT)) {
356             return parseBluetoothHandoverSelect(m);
357         }
358 
359         // Check for Nokia BT record, found on some Nokia BH-505 Headsets
360         if (tnf == NdefRecord.TNF_EXTERNAL_TYPE && Arrays.equals(type, TYPE_NOKIA)) {
361             return parseNokia(ByteBuffer.wrap(r.getPayload()));
362         }
363 
364         return null;
365     }
366 
parseNokia(ByteBuffer payload)367     BluetoothHandoverData parseNokia(ByteBuffer payload) {
368         BluetoothHandoverData result = new BluetoothHandoverData();
369         result.valid = false;
370 
371         try {
372             payload.position(1);
373             byte[] address = new byte[6];
374             payload.get(address);
375             result.device = mBluetoothAdapter.getRemoteDevice(address);
376             result.valid = true;
377             payload.position(14);
378             int nameLength = payload.get();
379             byte[] nameBytes = new byte[nameLength];
380             payload.get(nameBytes);
381             result.name = new String(nameBytes, StandardCharsets.UTF_8);
382         } catch (IllegalArgumentException e) {
383             Log.i(TAG, "nokia: invalid BT address");
384         } catch (BufferUnderflowException e) {
385             Log.i(TAG, "nokia: payload shorter than expected");
386         }
387         if (result.valid && result.name == null) result.name = "";
388         return result;
389     }
390 
parseBtOob(ByteBuffer payload)391     BluetoothHandoverData parseBtOob(ByteBuffer payload) {
392         BluetoothHandoverData result = new BluetoothHandoverData();
393         result.valid = false;
394 
395         try {
396             payload.position(2); // length
397             byte[] address = parseMacFromBluetoothRecord(payload);
398             result.device = mBluetoothAdapter.getRemoteDevice(address);
399             result.valid = true;
400 
401             while (payload.remaining() > 0) {
402                 boolean success = false;
403                 byte[] nameBytes;
404                 int len = payload.get();
405                 int type = payload.get();
406                 switch (type) {
407                     case BT_HANDOVER_TYPE_SHORT_LOCAL_NAME:
408                         nameBytes = new byte[len - 1];
409                         payload.get(nameBytes);
410                         result.name = new String(nameBytes, StandardCharsets.UTF_8);
411                         success = true;
412                         break;
413                     case BT_HANDOVER_TYPE_LONG_LOCAL_NAME:
414                         if (result.name != null) break;  // prefer short name
415                         nameBytes = new byte[len - 1];
416                         payload.get(nameBytes);
417                         result.name = new String(nameBytes, StandardCharsets.UTF_8);
418                         success = true;
419                         break;
420                     case BT_HANDOVER_TYPE_16_BIT_UUIDS_PARTIAL:
421                     case BT_HANDOVER_TYPE_16_BIT_UUIDS_COMPLETE:
422                     case BT_HANDOVER_TYPE_32_BIT_UUIDS_PARTIAL:
423                     case BT_HANDOVER_TYPE_32_BIT_UUIDS_COMPLETE:
424                     case BT_HANDOVER_TYPE_128_BIT_UUIDS_PARTIAL:
425                     case BT_HANDOVER_TYPE_128_BIT_UUIDS_COMPLETE:
426                         result.uuids = parseUuidFromBluetoothRecord(payload, type, len - 1);
427                         if (result.uuids != null) {
428                             success = true;
429                         }
430                         break;
431                     case BT_HANDOVER_TYPE_CLASS_OF_DEVICE:
432                         if (len - 1 != CLASS_OF_DEVICE_SIZE) {
433                             Log.i(TAG, "BT OOB: invalid size of Class of Device, should be " +
434                                   CLASS_OF_DEVICE_SIZE + " bytes.");
435                             break;
436                         }
437                         result.btClass = parseBluetoothClassFromBluetoothRecord(payload);
438                         success = true;
439                         break;
440                     default:
441                         break;
442                 }
443                 if (!success) {
444                     payload.position(payload.position() + len - 1);
445                 }
446             }
447         } catch (IllegalArgumentException e) {
448             Log.i(TAG, "BT OOB: invalid BT address");
449         } catch (BufferUnderflowException e) {
450             Log.i(TAG, "BT OOB: payload shorter than expected");
451         }
452         if (result.valid && result.name == null) result.name = "";
453         return result;
454     }
455 
parseBleOob(ByteBuffer payload)456     BluetoothHandoverData parseBleOob(ByteBuffer payload) {
457         BluetoothHandoverData result = new BluetoothHandoverData();
458         result.valid = false;
459         result.transport = BluetoothDevice.TRANSPORT_LE;
460 
461         try {
462 
463             byte[] bdaddr = null;
464             byte role = 0xF; // invalid default
465             byte[] leScC = null;
466             byte[] leScR = null;
467             byte[] nameBytes = null;
468             byte[] securityManagerTK = null;
469             while (payload.remaining() > 0) {
470                 int len = payload.get();
471                 int type = payload.get();
472                 switch (type) {
473                     case BT_HANDOVER_TYPE_MAC: // mac address
474                         int startpos = payload.position();
475                         bdaddr = new byte[7]; // 6 bytes for mac, 1 for address type
476                         payload.get(bdaddr);
477                         payload.position(startpos);
478 
479                         byte[] address = parseMacFromBluetoothRecord(payload);
480                         payload.position(payload.position() + 1); // advance over random byte
481                         result.device = mBluetoothAdapter.getRemoteDevice(address);
482                         result.valid = true;
483                         break;
484 
485                     case BT_HANDOVER_TYPE_LE_ROLE:
486                         role = payload.get();
487                         if (role == BT_HANDOVER_LE_ROLE_CENTRAL_ONLY) {
488                             // only central role supported, can't pair
489                             result.valid = false;
490                             return result;
491                         }
492                         break;
493 
494                     case BT_HANDOVER_TYPE_LONG_LOCAL_NAME:
495                         nameBytes = new byte[len - 1];
496                         payload.get(nameBytes);
497                         result.name = new String(nameBytes, StandardCharsets.UTF_8);
498                         break;
499 
500                     case BT_HANDOVER_TYPE_SECURITY_MANAGER_TK:
501                         if (len-1 != SECURITY_MANAGER_TK_SIZE) {
502                             Log.i(TAG, "BT OOB: invalid size of SM TK, should be " +
503                                   SECURITY_MANAGER_TK_SIZE + " bytes.");
504                             break;
505                         }
506 
507                         securityManagerTK = new byte[len - 1];
508                         payload.get(securityManagerTK);
509                         break;
510 
511                     case BT_HANDOVER_TYPE_LE_SC_CONFIRMATION:
512                         if (len - 1 != SECURITY_MANAGER_LE_SC_C_SIZE) {
513                             Log.i(TAG, "BT OOB: invalid size of LE SC Confirmation, should be " +
514                                   SECURITY_MANAGER_LE_SC_C_SIZE + " bytes.");
515                             break;
516                         }
517 
518                         leScC = new byte[len - 1];
519                         payload.get(leScC);
520                         break;
521 
522                     case BT_HANDOVER_TYPE_LE_SC_RANDOM:
523                         if (len-1 != SECURITY_MANAGER_LE_SC_R_SIZE) {
524                             Log.i(TAG, "BT OOB: invalid size of LE SC Random, should be " +
525                                   SECURITY_MANAGER_LE_SC_R_SIZE + " bytes.");
526                             break;
527                         }
528 
529                         leScR = new byte[len - 1];
530                         payload.get(leScR);
531                         break;
532 
533                     default:
534                         payload.position(payload.position() + len - 1);
535                         break;
536                 }
537             }
538             result.oobData = new OobData.LeBuilder(leScC, bdaddr, (int)(role & 0xFF))
539                 .setRandomizerHash(leScR)
540                 .setDeviceName(nameBytes)
541                 .setLeTemporaryKey(securityManagerTK)
542                 .build();
543         } catch (IllegalArgumentException e) {
544             Log.i(TAG, "BLE OOB: error parsing OOB data", e);
545         } catch (BufferUnderflowException e) {
546             Log.i(TAG, "BT OOB: payload shorter than expected");
547         }
548         if (result.valid && result.name == null) result.name = "";
549         return result;
550     }
551 
parseMacFromBluetoothRecord(ByteBuffer payload)552     private byte[] parseMacFromBluetoothRecord(ByteBuffer payload) {
553         byte[] address = new byte[6];
554         payload.get(address);
555         // ByteBuffer.order(LITTLE_ENDIAN) doesn't work for
556         // ByteBuffer.get(byte[]), so manually swap order
557         for (int i = 0; i < 3; i++) {
558             byte temp = address[i];
559             address[i] = address[5 - i];
560             address[5 - i] = temp;
561         }
562         return address;
563     }
564 
addressToReverseBytes(String address)565     static byte[] addressToReverseBytes(String address) {
566         if (address == null) {
567             Log.w(TAG, "BT address is null");
568             return null;
569         }
570         String[] split = address.split(":");
571         if (split.length < 6) {
572             Log.w(TAG, "BT address " + address + " is invalid");
573             return null;
574         }
575         byte[] result = new byte[split.length];
576 
577         for (int i = 0; i < split.length; i++) {
578             // need to parse as int because parseByte() expects a signed byte
579             result[split.length - 1 - i] = (byte)Integer.parseInt(split[i], 16);
580         }
581 
582         return result;
583     }
584 
parseUuidFromBluetoothRecord(ByteBuffer payload, int type, int len)585     private ParcelUuid[] parseUuidFromBluetoothRecord(ByteBuffer payload, int type, int len) {
586         int uuidSize;
587         switch (type) {
588             case BT_HANDOVER_TYPE_16_BIT_UUIDS_PARTIAL:
589             case BT_HANDOVER_TYPE_16_BIT_UUIDS_COMPLETE:
590                 uuidSize = BluetoothUuid.UUID_BYTES_16_BIT;
591                 break;
592             case BT_HANDOVER_TYPE_32_BIT_UUIDS_PARTIAL:
593             case BT_HANDOVER_TYPE_32_BIT_UUIDS_COMPLETE:
594                 uuidSize = BluetoothUuid.UUID_BYTES_32_BIT;
595                 break;
596             case BT_HANDOVER_TYPE_128_BIT_UUIDS_PARTIAL:
597             case BT_HANDOVER_TYPE_128_BIT_UUIDS_COMPLETE:
598                 uuidSize = BluetoothUuid.UUID_BYTES_128_BIT;
599                 break;
600             default:
601                 Log.i(TAG, "BT OOB: invalid size of UUID");
602                 return null;
603         }
604 
605         if (len == 0 || len % uuidSize != 0) {
606             Log.i(TAG, "BT OOB: invalid size of UUIDs, should be multiples of UUID bytes length");
607             return null;
608         }
609 
610         int num = len / uuidSize;
611         ParcelUuid[] uuids = new ParcelUuid[num];
612         byte[] data = new byte[uuidSize];
613         for (int i = 0; i < num; i++) {
614             payload.get(data);
615             uuids[i] = BluetoothUuid.parseUuidFrom(data);
616         }
617         return uuids;
618     }
619 
parseBluetoothClassFromBluetoothRecord(ByteBuffer payload)620     private BluetoothClass parseBluetoothClassFromBluetoothRecord(ByteBuffer payload) {
621         byte[] btClass = new byte[CLASS_OF_DEVICE_SIZE];
622         payload.get(btClass);
623 
624         ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
625         buffer.put(btClass);
626         buffer.order(ByteOrder.LITTLE_ENDIAN);
627 
628         Parcel parcel = Parcel.obtain();
629         parcel.writeInt(buffer.getInt(0));
630         parcel.setDataPosition(0);
631         BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(parcel);
632         parcel.recycle();
633         return bluetoothClass;
634     }
635 }
636