FastPairProvider Passkey Response

Always send a Passkey Resopnse to the connected Seeker rather than only
upon receipt of one.

Test: atest BluetoothFastPairTest
Bug: 196195063
Merged-In: Ia0bb2d15a0673ac541ccfad80f02e2f8990f9e59
Change-Id: I263c3ab1dcaa4849855b8af7a5506edd225bc279
diff --git a/service/src/com/android/car/FastPairGattServer.java b/service/src/com/android/car/FastPairGattServer.java
index 6cbaae0..6fc537d 100644
--- a/service/src/com/android/car/FastPairGattServer.java
+++ b/service/src/com/android/car/FastPairGattServer.java
@@ -86,6 +86,7 @@
     private static final boolean DBG = FastPairUtils.DBG;
     private static final int MAX_KEY_COUNT = 10;
     private static final int KEY_LIFESPAN = 10_000;
+    private static final int INVALID = -1;
 
     private final boolean mAutomaticPasskeyConfirmation;
     private final byte[] mModelId;
@@ -95,7 +96,7 @@
     private ArrayList<AccountKey> mKeys = new ArrayList<>();
     private BluetoothGattServer mBluetoothGattServer;
     private BluetoothManager mBluetoothManager;
-    private int mPairingPasskey = -1;
+    private int mPairingPasskey = INVALID;
     private int mFailureCount = 0;
     private int mSuccessCount = 0;
     private BluetoothGattService mFastPairService = new BluetoothGattService(
@@ -149,7 +150,9 @@
         @Override
         public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
             super.onConnectionStateChange(device, status, newState);
-            Log.d(TAG, "onConnectionStateChange " + newState + "Device: " + device.toString());
+            if (DBG) {
+                Log.d(TAG, "onConnectionStateChange " + newState + "Device: " + device.toString());
+            }
             if (newState == 0) {
                 mPairingPasskey = -1;
                 mSharedSecretKey = null;
@@ -249,12 +252,13 @@
                 Log.d(TAG, intent.getAction());
             }
             if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction())) {
-                if (DBG) {
-                    Log.d(TAG, "PairingCode " + intent
-                                    .getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, -1));
-                }
                 mRemotePairingDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-                mPairingPasskey = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, -1);
+                mPairingPasskey = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, INVALID);
+                if (DBG) {
+                    Log.d(TAG, "DeviceAddress: " + mRemotePairingDevice
+                            + " PairingCode: " + mPairingPasskey);
+                }
+                sendPairingResponse(mPairingPasskey);
             }
         }
     };
@@ -309,6 +313,9 @@
         } catch (Exception e) {
             Log.e(TAG, e.toString());
         }
+        if (DBG) {
+            Log.w(TAG, "Encryption Failed, clear key");
+        }
         mHandler.removeCallbacks(mClearSharedSecretKey);
         mSharedSecretKey = null;
         return null;
@@ -439,7 +446,7 @@
 
         byte[] encryptedRequest = Arrays.copyOfRange(pairingRequest, 0, 16);
         if (DBG) {
-            Log.d(TAG, "Checking " + possibleKeys.size() + "Keys");
+            Log.d(TAG, "Checking " + possibleKeys.size() + " Keys");
         }
         // check all the keys for a valid pairing request
         for (SecretKeySpec key : possibleKeys) {
@@ -486,7 +493,7 @@
                     .getRemoteDevice(remoteAddressBytes);
             if (DBG) {
                 Log.d(TAG, "Local RPA = " + mLocalRpaDevice);
-                Log.d(TAG, "Decrypted, LocalMacAddress" + localAddress + "remoteAddress"
+                Log.d(TAG, "Decrypted, LocalMacAddress: " + localAddress + " remoteAddress: "
                         + reportedDevice.toString());
             }
             // Test that the received device address matches this devices address
@@ -494,7 +501,6 @@
                 if (DBG) {
                     Log.d(TAG, "SecretKey Validated");
                 }
-
                 // encrypt and respond to the seeker with the local public address
                 byte[] rawResponse = new byte[16];
                 new Random().nextBytes(rawResponse);
@@ -536,16 +542,23 @@
                 }
                 mRemotePairingDevice.setPairingConfirmation(true);
             }
-        } else {
+        } else if (mPairingPasskey != INVALID) {
             Log.w(TAG, "Passkeys don't match, rejecting");
             mRemotePairingDevice.setPairingConfirmation(false);
         }
+        return true;
+    }
 
+    void sendPairingResponse(int passkey) {
+        if (!isConnected()) return;
+        if (DBG) {
+            Log.d(TAG, "sendPairingResponse + " + passkey);
+        }
         // Send an encrypted response to the seeker with the Bluetooth passkey as required
         byte[] decryptedResponse = new byte[16];
         new Random().nextBytes(decryptedResponse);
         ByteBuffer pairingPasskeyBytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(
-                mPairingPasskey);
+                passkey);
         decryptedResponse[0] = 0x3;
         decryptedResponse[1] = pairingPasskeyBytes.get(1);
         decryptedResponse[2] = pairingPasskeyBytes.get(2);
@@ -553,11 +566,9 @@
 
         mEncryptedResponse = encrypt(decryptedResponse);
         if (mEncryptedResponse == null) {
-            return false;
+            return;
         }
         mPasskeyCharacteristic.setValue(mEncryptedResponse);
-        return true;
-
     }
 
     /**
diff --git a/tests/carservice_unit_test/src/com/android/car/BluetoothFastPairTest.java b/tests/carservice_unit_test/src/com/android/car/BluetoothFastPairTest.java
index d7a8f3b..25ffd78 100644
--- a/tests/carservice_unit_test/src/com/android/car/BluetoothFastPairTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/BluetoothFastPairTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
@@ -339,12 +340,27 @@
         assertThat(mTestGattServer.validatePairingRequest(encryptedRequest,
                 testKey.getKeySpec())).isTrue();
         //send Wrong Pairing Key
-        sendPairingKey(-1);
+        sendPairingKey(-2);
         mTestGattServer.processPairingKey(encryptedPairingKey);
         verify(mMockBluetoothDevice).setPairingConfirmation(false);
     }
 
     @Test
+    public void testNoPairingKey() {
+        FastPairUtils.AccountKey testKey = new FastPairUtils.AccountKey(TEST_SHARED_SECRET);
+        mTestGattServer.setSharedSecretKey(testKey.toBytes());
+        byte[] encryptedRequest = mTestGattServer.encrypt(TEST_PAIRING_REQUEST);
+
+        byte[] encryptedPairingKey = mTestGattServer.encrypt(TEST_PAIRING_KEY);
+
+        assertThat(mTestGattServer.validatePairingRequest(encryptedRequest,
+                testKey.getKeySpec())).isTrue();
+        mTestGattServer.processPairingKey(encryptedPairingKey);
+        verifyNoMoreInteractions(mMockBluetoothDevice);
+    }
+
+
+    @Test
     public void testValidPairingKeyAutoAccept() {
         FastPairUtils.AccountKey testKey = new FastPairUtils.AccountKey(TEST_SHARED_SECRET);
         mTestGattServer.setSharedSecretKey(testKey.toBytes());
@@ -445,5 +461,6 @@
         pairingRequest.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, pairingKey);
         pairingRequest.putExtra(BluetoothDevice.EXTRA_DEVICE, mMockBluetoothDevice);
         mTestGattServer.mPairingAttemptsReceiver.onReceive(mMockContext, pairingRequest);
+
     }
 }