Version exchange for enrollment and unlock

- Implement version exchange
- reset encryption state when IHU is connected/disconnected/unlocked for
unlock

Bug: 132448677
Test: Manully tested on IHU with companion app built from cl/250791145
Change-Id: I68bf850b903e7ba415072029281081baf813321b
diff --git a/service/proto/ble_version_exchange.proto b/service/proto/ble_version_exchange.proto
index e6c47f1..28ef351 100644
--- a/service/proto/ble_version_exchange.proto
+++ b/service/proto/ble_version_exchange.proto
@@ -6,9 +6,16 @@
 option java_outer_classname = "VersionExchangeProto";
 
 message BLEVersionExchange {
-  // Required.
-  int32 minSupportedVersion = 1;
+  // Minimum supported protobuf version.
+  int32 minSupportedMessagingVersion = 1;
 
-  // Required.
-  int32 maxSupportedVersion = 2;
+  // Maximum supported protobuf version.
+  int32 maxSupportedMessagingVersion = 2;
+
+  // Minimum supported version of the encryption engine.
+  int32 minSupportedSecurityVersion = 3;
+
+  // Maximum supported version of the encryption engine.
+  int32 maxSupportedSecurityVersion = 4;
+
 }
diff --git a/service/src/com/android/car/trust/CarTrustAgentBleManager.java b/service/src/com/android/car/trust/CarTrustAgentBleManager.java
index 8517c98..718bd22 100644
--- a/service/src/com/android/car/trust/CarTrustAgentBleManager.java
+++ b/service/src/com/android/car/trust/CarTrustAgentBleManager.java
@@ -33,10 +33,12 @@
 
 import com.android.car.BLEStreamProtos.BLEMessageProto.BLEMessage;
 import com.android.car.BLEStreamProtos.BLEOperationProto.OperationType;
+import com.android.car.BLEStreamProtos.VersionExchangeProto.BLEVersionExchange;
 import com.android.car.CarLocalServices;
 import com.android.car.R;
 import com.android.car.Utils;
 
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
@@ -99,6 +101,10 @@
     private BluetoothGattService mUnlockGattService;
 
     private BLEMessagePayloadStream mBleMessagePayloadStream = new BLEMessagePayloadStream();
+    // This is a boolean because there's only one supported version.
+    private boolean mIsVersionExchanged;
+    private static final int MESSAGING_VERSION = 1;
+    private static final int SECURITY_VERSION = 1;
 
     CarTrustAgentBleManager(Context context) {
         super(context);
@@ -116,6 +122,11 @@
                 && device.getName() == null) {
             retrieveDeviceName(device);
         }
+
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Reset mIsVersionExchanged to false.");
+        }
+        mIsVersionExchanged = false;
         getTrustedDeviceService().onRemoteDeviceConnected(device);
     }
 
@@ -124,6 +135,10 @@
         if (getTrustedDeviceService() != null) {
             getTrustedDeviceService().onRemoteDeviceDisconnected(device);
         }
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Reset mIsVersionExchanged to false.");
+        }
+        mIsVersionExchanged = false;
     }
 
     @Override
@@ -146,6 +161,19 @@
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.d(TAG, "onCharacteristicWrite received uuid: " + uuid);
         }
+        if (!mIsVersionExchanged) {
+            if (uuid.equals(mEnrollmentClientWriteUuid)) {
+                resolveBLEVersion(device, value, mEnrollmentGattService
+                        .getCharacteristic(mEnrollmentServerWriteUuid));
+            } else if (uuid.equals(mUnlockClientWriteUuid)) {
+                resolveBLEVersion(device, value, mUnlockGattService
+                        .getCharacteristic(mUnlockServerWriteUuid));
+            } else {
+                Log.e(TAG, "Invalid UUID, disconnect remote device.");
+                disconnectRemoteDevice();
+            }
+            return;
+        }
         // This write operation is not thread safe individually, but is guarded by the callback
         // here.
         mBleMessagePayloadStream.write(value);
@@ -230,6 +258,44 @@
         return mRandomName;
     }
 
+    private void resolveBLEVersion(BluetoothDevice device, byte[] value,
+            BluetoothGattCharacteristic characteristic) {
+        BLEVersionExchange versionExchange;
+        try {
+            versionExchange = BLEVersionExchange.parseFrom(value);
+        } catch (IOException e) {
+            disconnectRemoteDevice();
+            Log.e(TAG, "Could not parse version exchange message", e);
+            return;
+        }
+        int minMessagingVersion = versionExchange.getMinSupportedMessagingVersion();
+        int minSecurityVersion = versionExchange.getMinSupportedSecurityVersion();
+        // The supported versions for the communication and security protocol.
+        // Only v1 is supported at this time.
+        // TODO:(b/134094617) get supported versions from BleMessageFactory
+        if (minMessagingVersion != MESSAGING_VERSION || minSecurityVersion != SECURITY_VERSION) {
+            Log.e(TAG, "No supported version (minMessagingVersion: " + minMessagingVersion
+                    + ", minSecurityVersion" + minSecurityVersion + ")");
+            disconnectRemoteDevice();
+            return;
+        }
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Resolved version to (minMessagingVersion: " + minMessagingVersion
+                    + ", minSecurityVersion" + minSecurityVersion + ")");
+        }
+        BLEVersionExchange headUnitVersion = BLEVersionExchange.newBuilder()
+                .setMinSupportedMessagingVersion(MESSAGING_VERSION)
+                .setMaxSupportedMessagingVersion(MESSAGING_VERSION)
+                .setMinSupportedSecurityVersion(SECURITY_VERSION)
+                .setMinSupportedSecurityVersion(SECURITY_VERSION)
+                .build();
+        setValueOnCharacteristicAndNotify(device, headUnitVersion.toByteArray(), characteristic);
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Sent supported versioned to the phone.");
+        }
+        mIsVersionExchanged = true;
+    }
+
     /**
      * Setup the BLE GATT server for Enrollment. The GATT server for Enrollment comprises of one
      * GATT Service and 2 characteristics - one for the phone to write to and one for the head unit
@@ -378,6 +444,7 @@
 
     /**
      * Sends the given message to the specified device and characteristic.
+     * The message will be splited into multiple messages wrapped in BLEMessage proto.
      *
      * @param device The device to send the message to.
      * @param characteristic The characteristic to write to.
@@ -401,11 +468,16 @@
 
         for (BLEMessage bleMessage : bleMessages) {
             // TODO(b/131719066) get acknowledgement from the phone then continue to send packets
-            characteristic.setValue(bleMessage.toByteArray());
-            notifyCharacteristicChanged(device, characteristic, false);
+            setValueOnCharacteristicAndNotify(device, bleMessage.toByteArray(), characteristic);
         }
     }
 
+    void setValueOnCharacteristicAndNotify(BluetoothDevice device, byte[] message,
+            BluetoothGattCharacteristic characteristic) {
+        characteristic.setValue(message);
+        notifyCharacteristicChanged(device, characteristic, false);
+    }
+
     private final AdvertiseCallback mEnrollmentAdvertisingCallback = new AdvertiseCallback() {
         @Override
         public void onStartSuccess(AdvertiseSettings settingsInEffect) {
diff --git a/service/src/com/android/car/trust/CarTrustAgentUnlockService.java b/service/src/com/android/car/trust/CarTrustAgentUnlockService.java
index 1151c4e..2e57b62 100644
--- a/service/src/com/android/car/trust/CarTrustAgentUnlockService.java
+++ b/service/src/com/android/car/trust/CarTrustAgentUnlockService.java
@@ -243,6 +243,7 @@
             queueMessageForLog("onRemoteDeviceConnected (addr:" + device.getAddress() + ")");
             mRemoteUnlockDevice = device;
         }
+        resetEncryptionState();
         mCurrentUnlockState = UNLOCK_STATE_WAITING_FOR_UNIQUE_ID;
     }
 
@@ -255,6 +256,7 @@
         synchronized (mDeviceLock) {
             mRemoteUnlockDevice = null;
         }
+        resetEncryptionState();
         mCurrentUnlockState = UNLOCK_STATE_WAITING_FOR_UNIQUE_ID;
     }
 
@@ -570,6 +572,7 @@
         synchronized (mHandleLock) {
             mUnlockHandle = null;
         }
+        resetEncryptionState();
     }
 
     void dump(PrintWriter writer) {