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