1 /*
2  * Copyright (C) 2021 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 package com.android.car.bluetooth;
17 
18 import static com.android.car.bluetooth.FastPairUtils.AccountKey;
19 
20 import android.bluetooth.BluetoothAdapter;
21 import android.bluetooth.BluetoothDevice;
22 import android.bluetooth.BluetoothGatt;
23 import android.bluetooth.BluetoothGattCharacteristic;
24 import android.bluetooth.BluetoothGattDescriptor;
25 import android.bluetooth.BluetoothGattServer;
26 import android.bluetooth.BluetoothGattServerCallback;
27 import android.bluetooth.BluetoothGattService;
28 import android.bluetooth.BluetoothManager;
29 import android.content.BroadcastReceiver;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.IntentFilter;
33 import android.os.Handler;
34 import android.os.ParcelUuid;
35 import android.util.Base64;
36 import android.util.IndentingPrintWriter;
37 import android.util.Log;
38 
39 import com.android.car.CarServiceUtils;
40 
41 import java.math.BigInteger;
42 import java.nio.ByteBuffer;
43 import java.nio.ByteOrder;
44 import java.security.KeyFactory;
45 import java.security.KeyPairGenerator;
46 import java.security.MessageDigest;
47 import java.security.PrivateKey;
48 import java.security.PublicKey;
49 import java.security.interfaces.ECPublicKey;
50 import java.security.spec.ECParameterSpec;
51 import java.security.spec.ECPoint;
52 import java.security.spec.ECPrivateKeySpec;
53 import java.security.spec.ECPublicKeySpec;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.List;
57 import java.util.Random;
58 
59 import javax.crypto.Cipher;
60 import javax.crypto.KeyAgreement;
61 import javax.crypto.spec.SecretKeySpec;
62 
63 /**
64  * The FastPairGattServer is responsible for all 2 way communications with the Fast Pair Seeker.
65  * It is running in the background over BLE whenever the Fast Pair Service is running, waiting for a
66  * Seeker to connect, after which time it manages the authentication an performs the steps as
67  * required by the Fast Pair Specification.
68  */
69 class FastPairGattServer {
70     // Service ID assigned for FastPair.
71     public static final ParcelUuid FAST_PAIR_SERVICE_UUID = ParcelUuid
72             .fromString("0000FE2C-0000-1000-8000-00805f9b34fb");
73     public static final ParcelUuid FAST_PAIR_MODEL_ID_UUID = ParcelUuid
74             .fromString("FE2C1233-8366-4814-8EB0-01DE32100BEA");
75     public static final ParcelUuid KEY_BASED_PAIRING_UUID = ParcelUuid
76             .fromString("FE2C1234-8366-4814-8EB0-01DE32100BEA");
77     public static final ParcelUuid PASSKEY_UUID = ParcelUuid
78             .fromString("FE2C1235-8366-4814-8EB0-01DE32100BEA");
79     public static final ParcelUuid ACCOUNT_KEY_UUID = ParcelUuid
80             .fromString("FE2C1236-8366-4814-8EB0-01DE32100BEA");
81     public static final ParcelUuid CLIENT_CHARACTERISTIC_CONFIG = ParcelUuid
82             .fromString("00002902-0000-1000-8000-00805f9b34fb");
83     public static final ParcelUuid DEVICE_NAME_CHARACTERISTIC_CONFIG = ParcelUuid
84             .fromString("00002A00-0000-1000-8000-00805f9b34fb");
85     private static final String TAG = "FastPairGattServer";
86     private static final boolean DBG = FastPairUtils.DBG;
87     private static final int MAX_KEY_COUNT = 10;
88     private static final int KEY_LIFESPAN = 10_000;
89     private static final int INVALID = -1;
90 
91     private final boolean mAutomaticPasskeyConfirmation;
92     private final byte[] mModelId;
93     private final String mPrivateAntiSpoof;
94     private final Context mContext;
95 
96     private ArrayList<AccountKey> mKeys = new ArrayList<>();
97     private BluetoothGattServer mBluetoothGattServer;
98     private BluetoothManager mBluetoothManager;
99     private int mPairingPasskey = INVALID;
100     private int mFailureCount = 0;
101     private int mSuccessCount = 0;
102     private BluetoothGattService mFastPairService = new BluetoothGattService(
103             FAST_PAIR_SERVICE_UUID.getUuid(), BluetoothGattService.SERVICE_TYPE_PRIMARY);
104     private Callbacks mCallbacks;
105     private SecretKeySpec mSharedSecretKey;
106     private byte[] mEncryptedResponse;
107     private BluetoothDevice mLocalRpaDevice;
108     private BluetoothDevice mRemotePairingDevice;
109     private BluetoothDevice mRemoteGattDevice;
110 
111     interface Callbacks {
112         /**
113          * Notify the Provider of completion to a GATT session
114          * @param successful
115          */
onPairingCompleted(boolean successful)116         void onPairingCompleted(boolean successful);
117     }
118 
119     /**
120      * Check if a client is connected to this GATT server
121      * @return true if connected;
122      */
isConnected()123     public boolean isConnected() {
124         return (mRemoteGattDevice != null);
125     }
126 
updateLocalRpa(BluetoothDevice device)127     public void updateLocalRpa(BluetoothDevice device) {
128         mLocalRpaDevice = device;
129     }
130 
131     private Runnable mClearSharedSecretKey = new Runnable() {
132         @Override
133         public void run() {
134             mSharedSecretKey = null;
135         }
136     };
137     private final Handler mHandler = new Handler(
138             CarServiceUtils.getHandlerThread(FastPairUtils.THREAD_NAME).getLooper());
139     private BluetoothGattCharacteristic mModelIdCharacteristic;
140     private BluetoothGattCharacteristic mKeyBasedPairingCharacteristic;
141     private BluetoothGattCharacteristic mPasskeyCharacteristic;
142     private BluetoothGattCharacteristic mAccountKeyCharacteristic;
143     private BluetoothGattCharacteristic mDeviceNameCharacteristic;
144 
145     /**
146      * GATT server callbacks responsible for servicing read and write calls from the remote device
147      */
148     private BluetoothGattServerCallback mBluetoothGattServerCallback =
149             new BluetoothGattServerCallback() {
150         @Override
151         public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
152             super.onConnectionStateChange(device, status, newState);
153             if (DBG) {
154                 Log.d(TAG, "onConnectionStateChange " + newState + "Device: " + device.toString());
155             }
156             if (newState == 0) {
157                 mPairingPasskey = -1;
158                 mSharedSecretKey = null;
159                 mRemoteGattDevice = null;
160                 mCallbacks.onPairingCompleted(false);
161             } else if (newState > 0) {
162                 mRemoteGattDevice = device;
163             }
164         }
165 
166         @Override
167         public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
168                 BluetoothGattCharacteristic characteristic) {
169             super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
170             if (DBG) {
171                 Log.d(TAG, "onCharacteristicReadRequest");
172             }
173             if (characteristic == mModelIdCharacteristic) {
174                 if (DBG) {
175                     Log.d(TAG, "reading model ID");
176                 }
177             }
178             mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset,
179                     characteristic.getValue());
180         }
181 
182 
183         @Override
184         public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
185                 BluetoothGattCharacteristic characteristic, boolean preparedWrite,
186                 boolean responseNeeded,
187                 int offset, byte[] value) {
188             super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite,
189                     responseNeeded, offset, value);
190             if (DBG) {
191                 Log.d(TAG, "onWrite uuid()" + characteristic.getUuid() + "Length" + value.length);
192             }
193             if (characteristic == mAccountKeyCharacteristic) {
194                 if (DBG) {
195                     Log.d(TAG, "onWriteAccountKeyCharacteristic");
196                 }
197                 processAccountKey(value);
198 
199                 mBluetoothGattServer
200                         .sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset,
201                                 characteristic.getValue());
202 
203             } else if (characteristic == mKeyBasedPairingCharacteristic) {
204                 if (DBG) {
205                     Log.d(TAG, "KeyBasedPairingCharacteristic");
206                 }
207                 processKeyBasedPairing(value);
208                 mKeyBasedPairingCharacteristic.setValue(mEncryptedResponse);
209                 mBluetoothGattServer
210                         .sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset,
211                                 mEncryptedResponse);
212                 mBluetoothGattServer
213                         .notifyCharacteristicChanged(device, mDeviceNameCharacteristic, false);
214                 mBluetoothGattServer
215                         .notifyCharacteristicChanged(device, mKeyBasedPairingCharacteristic, false);
216 
217             } else if (characteristic == mPasskeyCharacteristic) {
218                 if (DBG) {
219                     Log.d(TAG, "onWritePasskey" + characteristic.getUuid());
220                 }
221                 mBluetoothGattServer
222                         .sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset,
223                                 mEncryptedResponse);
224                 processPairingKey(value);
225 
226             } else {
227                 Log.w(TAG, "onWriteOther" + characteristic.getUuid());
228             }
229         }
230 
231         @Override
232         public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
233                 BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded,
234                 int offset, byte[] value) {
235             if (DBG) {
236                 Log.d(TAG, "onDescriptorWriteRequest");
237             }
238             mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset,
239                     descriptor.getValue());
240         }
241     };
242 
243     /**
244      *  Receive incoming pairing requests such that we can confirm Keys match.
245      */
246     BroadcastReceiver mPairingAttemptsReceiver = new BroadcastReceiver() {
247         @Override
248         public void onReceive(Context context, Intent intent) {
249             if (DBG) {
250                 Log.d(TAG, intent.getAction());
251             }
252             if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction())) {
253                 mRemotePairingDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
254                 mPairingPasskey = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, INVALID);
255                 if (DBG) {
256                     Log.d(TAG, "DeviceAddress: " + mRemotePairingDevice
257                             + " PairingCode: " + mPairingPasskey);
258                 }
259                 sendPairingResponse(mPairingPasskey);
260             }
261         }
262     };
263 
264     /**
265      * FastPairGattServer
266      * @param context user specific context on which to make callse
267      * @param modelId assigned Fast Pair Model ID
268      * @param antiSpoof assigned Fast Pair private Anti Spoof key
269      * @param callbacks callbacks used to report back current pairing status
270      * @param automaticAcceptance automatically accept an incoming pairing request that has been
271      *     authenticated through the Fast Pair protocol without further user interaction.
272      */
FastPairGattServer(Context context, int modelId, String antiSpoof, Callbacks callbacks, boolean automaticAcceptance)273     FastPairGattServer(Context context, int modelId, String antiSpoof,
274             Callbacks callbacks, boolean automaticAcceptance) {
275         mContext = context;
276         mCallbacks = callbacks;
277         mPrivateAntiSpoof = antiSpoof;
278         mAutomaticPasskeyConfirmation = automaticAcceptance;
279         mBluetoothManager = context.getSystemService(BluetoothManager.class);
280 
281         mBluetoothGattServer = mBluetoothManager
282                 .openGattServer(context, mBluetoothGattServerCallback);
283         if (DBG) {
284             Log.d(TAG, "mBTManager: " + mBluetoothManager.toString() + " GATT: "
285                     + mBluetoothGattServer);
286         }
287         ByteBuffer modelIdBytes = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(
288                 modelId);
289         mModelId = Arrays.copyOfRange(modelIdBytes.array(), 0, 3);
290         setup();
291     }
292 
setSharedSecretKey(byte[] key)293     void setSharedSecretKey(byte[] key) {
294         mSharedSecretKey = new SecretKeySpec(key, "AES");
295         mHandler.postDelayed(mClearSharedSecretKey, KEY_LIFESPAN);
296     }
297 
298     /**
299      * Utilize the key set via setSharedSecretKey to attempt to encrypt the provided data
300      * @param decoded data to be encrypted
301      * @return encrypted data upon success; null otherwise
302      */
encrypt(byte[] decoded)303     byte[] encrypt(byte[] decoded) {
304         try {
305             Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
306             cipher.init(Cipher.ENCRYPT_MODE, mSharedSecretKey);
307             mHandler.removeCallbacks(mClearSharedSecretKey);
308             mHandler.postDelayed(mClearSharedSecretKey, KEY_LIFESPAN);
309             return cipher.doFinal(decoded);
310 
311         } catch (Exception e) {
312             Log.e(TAG, e.toString());
313         }
314         if (DBG) {
315             Log.w(TAG, "Encryption Failed, clear key");
316         }
317         mHandler.removeCallbacks(mClearSharedSecretKey);
318         mSharedSecretKey = null;
319         return null;
320     }
321     /**
322      * Utilize the key set via setSharedSecretKey to attempt to decrypt the provided data
323      * @param encoded data to be decrypted
324      * @return decrypted data upon success; null otherwise
325      */
decrypt(byte[] encoded)326     byte[] decrypt(byte[] encoded) {
327         try {
328             Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
329             cipher.init(Cipher.DECRYPT_MODE, mSharedSecretKey);
330             mHandler.removeCallbacks(mClearSharedSecretKey);
331             mHandler.postDelayed(mClearSharedSecretKey, KEY_LIFESPAN);
332             return cipher.doFinal(encoded);
333 
334         } catch (Exception e) {
335             Log.e(TAG, e.toString());
336         }
337         mHandler.removeCallbacks(mClearSharedSecretKey);
338         mSharedSecretKey = null;
339         return null;
340     }
341 
342     /**
343      * The final step of the Fast Pair procedure involves receiving an account key from the
344      * Fast Pair seeker, authenticating it, and then storing it for future use.
345      * @param accountKey
346      */
processAccountKey(byte[] accountKey)347     void processAccountKey(byte[] accountKey) {
348         byte[] decodedAccountKey = decrypt(accountKey);
349         if (decodedAccountKey != null && decodedAccountKey[0] == 0x04) {
350             if (DBG) {
351                 Log.d(TAG, "ReceivedAccountKey" + decodedAccountKey[0]);
352             }
353             FastPairUtils.AccountKey receivedKey = new FastPairUtils.AccountKey(decodedAccountKey);
354             if (!mKeys.contains(receivedKey)) {
355                 mKeys.add(receivedKey);
356             }
357             // due to space restrictions in the protocol we can only store 10 keys
358             while (mKeys.size() > MAX_KEY_COUNT) {
359                 mKeys.remove(0);
360             }
361             FastPairUtils.writeStoredAccountKeys(mContext, mKeys);
362             mSuccessCount++;
363         } else {
364             if (DBG) {
365                 Log.d(TAG, "Invalid Account Key");
366             }
367         }
368     }
369 
370     /**
371      * New pairings based upon model ID requires the Fast Pair provider to authenticate to the
372      * seeker that it is in possession of the private key associated with the model ID advertised,
373      * this is accomplished via Eliptic-curve Diffie-Hellman
374      * @param localPrivateKey
375      * @param remotePublicKey
376      * @return
377      */
calculateAntiSpoofing(byte[] localPrivateKey, byte[] remotePublicKey)378     AccountKey calculateAntiSpoofing(byte[] localPrivateKey, byte[] remotePublicKey) {
379         try {
380             if (DBG) {
381                 Log.d(TAG, "Calculating secret key from remotePublicKey");
382             }
383             // Initialize the EC key generator
384             KeyFactory keyFactory = KeyFactory.getInstance("EC");
385             KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
386             ECParameterSpec ecParameterSpec = ((ECPublicKey) kpg.generateKeyPair().getPublic())
387                     .getParams();
388             // Use the private anti-spoofing key
389             ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(
390                     new BigInteger(1, localPrivateKey),
391                     ecParameterSpec);
392             // Calculate the public point utilizing the data received from the remote device
393             ECPoint publicPoint = new ECPoint(new BigInteger(1, Arrays.copyOf(remotePublicKey, 32)),
394                     new BigInteger(1, Arrays.copyOfRange(remotePublicKey, 32, 64)));
395             ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(publicPoint, ecParameterSpec);
396             PrivateKey privateKey = keyFactory.generatePrivate(ecPrivateKeySpec);
397             PublicKey publicKey = keyFactory.generatePublic(ecPublicKeySpec);
398 
399             // Generate a shared secret
400             KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
401             keyAgreement.init(privateKey);
402             keyAgreement.doPhase(publicKey, true);
403             byte[] sharedSecret = keyAgreement.generateSecret();
404 
405             // Use the first 16 bytes of a hash of the shared secret as the session key
406             final byte[] digest = MessageDigest.getInstance("SHA-256").digest(sharedSecret);
407 
408             byte[] AESAntiSpoofingKey = Arrays.copyOf(digest, 16);
409             if (DBG) {
410                 Log.d(TAG, "Key calculated");
411             }
412             return new AccountKey(AESAntiSpoofingKey);
413         } catch (Exception e) {
414             Log.w(TAG, e.toString());
415             return null;
416         }
417     }
418 
419     /**
420      * Determine if this pairing request is based on the anti-spoof keys associated with the model
421      * id or stored account keys.
422      * @param accountKey
423      * @return
424      */
processKeyBasedPairing(byte[] pairingRequest)425     boolean processKeyBasedPairing(byte[] pairingRequest) {
426         if (mFailureCount >= 10) return false;
427 
428         List<SecretKeySpec> possibleKeys = new ArrayList<>();
429         if (pairingRequest.length == 80) {
430             // if the pairingRequest is 80 bytes long try the anit-spoof key
431             final byte[] remotePublicKey = Arrays.copyOfRange(pairingRequest, 16, 80);
432 
433             possibleKeys
434                     .add(calculateAntiSpoofing(Base64.decode(mPrivateAntiSpoof, 0), remotePublicKey)
435                             .getKeySpec());
436         } else {
437             // otherwise the pairing request is the encrypted request, try all the stored account
438             // keys
439             List<AccountKey> storedAccountKeys = FastPairUtils.readStoredAccountKeys(mContext);
440             for (AccountKey key : storedAccountKeys) {
441                 possibleKeys.add(new SecretKeySpec(key.toBytes(), "AES"));
442             }
443         }
444 
445         byte[] encryptedRequest = Arrays.copyOfRange(pairingRequest, 0, 16);
446         if (DBG) {
447             Log.d(TAG, "Checking " + possibleKeys.size() + " Keys");
448         }
449         // check all the keys for a valid pairing request
450         for (SecretKeySpec key : possibleKeys) {
451             if (DBG) {
452                 Log.d(TAG, "Checking possibleKey");
453             }
454             if (validatePairingRequest(encryptedRequest, key)) {
455                 return true;
456             }
457         }
458         Log.w(TAG, "No Matching Key found");
459         mFailureCount++;
460         mSharedSecretKey = null;
461         return false;
462     }
463 
464     /**
465      * Check if the pairing request is a valid request.
466      * A request is valid if its decrypted value is of type 0x00 or 0x10 and it contains either the
467      * seekers public or current BLE address
468      * @param encryptedRequest the request to decrypt and validate
469      * @param secretKeySpec the key to use while attempting to decrypt the request
470      * @return true if the key matches, false otherwise
471      */
validatePairingRequest(byte[] encryptedRequest, SecretKeySpec secretKeySpec)472     boolean validatePairingRequest(byte[] encryptedRequest, SecretKeySpec secretKeySpec) {
473         // Decrypt the request
474         mSharedSecretKey = secretKeySpec;
475         byte[] decryptedRequest = decrypt(encryptedRequest);
476         if (decryptedRequest == null) {
477             return false;
478         }
479         if (DBG) {
480             Log.d(TAG, "Decrypted" + decryptedRequest[0] + "Flags" + decryptedRequest[1]);
481         }
482         // Check that the request is either a Key-based Pairing Request or an Action Request
483         if (decryptedRequest[0] == 0 || decryptedRequest[0] == 0x10) {
484             String localAddress = BluetoothAdapter.getDefaultAdapter().getAddress();
485             byte[] localAddressBytes = FastPairUtils.getBytesFromAddress(localAddress);
486             // Extract the remote address bytes from the message
487             byte[] remoteAddressBytes = Arrays.copyOfRange(decryptedRequest, 2, 8);
488             BluetoothDevice localDevice = BluetoothAdapter.getDefaultAdapter()
489                     .getRemoteDevice(localAddress);
490             BluetoothDevice reportedDevice = BluetoothAdapter.getDefaultAdapter()
491                     .getRemoteDevice(remoteAddressBytes);
492             if (DBG) {
493                 Log.d(TAG, "Local RPA = " + mLocalRpaDevice);
494                 Log.d(TAG, "Decrypted, LocalMacAddress: " + localAddress + " remoteAddress: "
495                         + reportedDevice.toString());
496             }
497             // Test that the received device address matches this devices address
498             if (reportedDevice.equals(localDevice) || reportedDevice.equals(mLocalRpaDevice)) {
499                 if (DBG) {
500                     Log.d(TAG, "SecretKey Validated");
501                 }
502                 // encrypt and respond to the seeker with the local public address
503                 byte[] rawResponse = new byte[16];
504                 new Random().nextBytes(rawResponse);
505                 rawResponse[0] = 0x01;
506                 System.arraycopy(localAddressBytes, 0, rawResponse, 1, 6);
507                 mEncryptedResponse = encrypt(rawResponse);
508                 return encryptedRequest != null;
509             }
510         }
511         return false;
512     }
513 
514     /**
515      * Extract the 6 digit Bluetooth Simple Secure Passkey from the received message and confirm
516      * it matches the key received through the Bluetooth pairing procedure.
517      * If the passkeys match and automatic passkey confirmation is enabled, approve of the pairing.
518      * If the passkeys do not match reject the pairing.
519      * @param pairingKey
520      * @return true if the procedure completed, although pairing may not have been approved
521      */
processPairingKey(byte[] pairingKey)522     boolean processPairingKey(byte[] pairingKey) {
523         byte[] decryptedRequest = decrypt(pairingKey);
524         if (decryptedRequest == null) {
525             return false;
526         }
527         int passkey = Byte.toUnsignedInt(decryptedRequest[1]) * 65536
528                 + Byte.toUnsignedInt(decryptedRequest[2]) * 256
529                 + Byte.toUnsignedInt(decryptedRequest[3]);
530 
531         if (DBG) {
532             Log.d(TAG, "PairingKey , MessageType " + decryptedRequest[0]
533                     + "FastPair Passkey = " + passkey + "Bluetooth Passkey = " + mPairingPasskey);
534         }
535         // compare the Bluetooth received passkey with the Fast Pair received passkey
536         if (mPairingPasskey == passkey) {
537             if (mAutomaticPasskeyConfirmation) {
538                 if (DBG) {
539                     Log.d(TAG, "Passkeys match, accepting");
540                 }
541                 mRemotePairingDevice.setPairingConfirmation(true);
542             }
543         } else if (mPairingPasskey != INVALID) {
544             Log.w(TAG, "Passkeys don't match, rejecting");
545             mRemotePairingDevice.setPairingConfirmation(false);
546         }
547         return true;
548     }
549 
sendPairingResponse(int passkey)550     void sendPairingResponse(int passkey) {
551         if (!isConnected()) return;
552         if (DBG) {
553             Log.d(TAG, "sendPairingResponse + " + passkey);
554         }
555         // Send an encrypted response to the seeker with the Bluetooth passkey as required
556         byte[] decryptedResponse = new byte[16];
557         new Random().nextBytes(decryptedResponse);
558         ByteBuffer pairingPasskeyBytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(
559                 passkey);
560         decryptedResponse[0] = 0x3;
561         decryptedResponse[1] = pairingPasskeyBytes.get(1);
562         decryptedResponse[2] = pairingPasskeyBytes.get(2);
563         decryptedResponse[3] = pairingPasskeyBytes.get(3);
564 
565         mEncryptedResponse = encrypt(decryptedResponse);
566         if (mEncryptedResponse == null) {
567             return;
568         }
569         mPasskeyCharacteristic.setValue(mEncryptedResponse);
570         mBluetoothGattServer
571                 .notifyCharacteristicChanged(mRemoteGattDevice, mPasskeyCharacteristic, false);
572     }
573 
574     /**
575      * Initialize all of the GATT characteristics with appropriate default values and the required
576      * configurations.
577      */
setup()578     void setup() {
579         IntentFilter filter = new IntentFilter();
580         filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
581         mContext.registerReceiver(mPairingAttemptsReceiver, filter);
582         mModelIdCharacteristic = new BluetoothGattCharacteristic(FAST_PAIR_MODEL_ID_UUID.getUuid(),
583                 BluetoothGattCharacteristic.PROPERTY_READ,
584                 BluetoothGattCharacteristic.PERMISSION_READ);
585         mModelIdCharacteristic.setValue(mModelId);
586         mFastPairService.addCharacteristic(mModelIdCharacteristic);
587 
588         mKeyBasedPairingCharacteristic =
589                 new BluetoothGattCharacteristic(KEY_BASED_PAIRING_UUID.getUuid(),
590                         BluetoothGattCharacteristic.PROPERTY_WRITE
591                                 | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
592                         BluetoothGattCharacteristic.PERMISSION_WRITE);
593         mKeyBasedPairingCharacteristic.setValue(mModelId);
594         mKeyBasedPairingCharacteristic.addDescriptor(new BluetoothGattDescriptor(
595                 CLIENT_CHARACTERISTIC_CONFIG.getUuid(),
596                 BluetoothGattDescriptor.PERMISSION_READ
597                         | BluetoothGattDescriptor.PERMISSION_WRITE));
598         mFastPairService.addCharacteristic(mKeyBasedPairingCharacteristic);
599 
600         mPasskeyCharacteristic =
601                 new BluetoothGattCharacteristic(PASSKEY_UUID.getUuid(),
602                         BluetoothGattCharacteristic.PROPERTY_WRITE
603                                 | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
604                         BluetoothGattCharacteristic.PERMISSION_WRITE);
605         mPasskeyCharacteristic.setValue(mModelId);
606         mPasskeyCharacteristic.addDescriptor(new BluetoothGattDescriptor(
607                 CLIENT_CHARACTERISTIC_CONFIG.getUuid(),
608                 BluetoothGattDescriptor.PERMISSION_READ
609                         | BluetoothGattDescriptor.PERMISSION_WRITE));
610 
611         mFastPairService.addCharacteristic(mPasskeyCharacteristic);
612 
613         mAccountKeyCharacteristic =
614                 new BluetoothGattCharacteristic(ACCOUNT_KEY_UUID.getUuid(),
615                         BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
616                         BluetoothGattCharacteristic.PERMISSION_WRITE);
617         mFastPairService.addCharacteristic(mAccountKeyCharacteristic);
618 
619         mDeviceNameCharacteristic =
620                 new BluetoothGattCharacteristic(DEVICE_NAME_CHARACTERISTIC_CONFIG.getUuid(),
621                         BluetoothGattCharacteristic.PROPERTY_READ,
622                         BluetoothGattCharacteristic.PERMISSION_READ);
623         mDeviceNameCharacteristic.setValue(BluetoothAdapter.getDefaultAdapter().getName());
624         mFastPairService.addCharacteristic(mDeviceNameCharacteristic);
625     }
626 
start()627     void start() {
628         if (mBluetoothGattServer == null) {
629             return;
630         }
631         mBluetoothGattServer.addService(mFastPairService);
632     }
633 
stop()634     void stop() {
635         if (mBluetoothGattServer == null) {
636             return;
637         }
638         mBluetoothGattServer.removeService(mFastPairService);
639     }
640 
dump(IndentingPrintWriter writer)641     void dump(IndentingPrintWriter writer) {
642         writer.println("Currently connected to        : " + mRemoteGattDevice);
643         writer.println("Successful pairing attempts   : " + mSuccessCount);
644         writer.println("Unsuccessful pairing attempts : " + mFailureCount);
645     }
646 }
647