feat: add BleRelayManager GATT callback (connect, UUID verify, notifications)
This commit is contained in:
parent
92585455c8
commit
a60f6cdc1c
1 changed files with 93 additions and 2 deletions
|
|
@ -11,6 +11,9 @@ import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
import javax.crypto.Mac
|
import javax.crypto.Mac
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
import android.bluetooth.BluetoothGattDescriptor
|
||||||
|
import android.bluetooth.BluetoothProfile
|
||||||
|
import android.util.Log
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.bluetooth.BluetoothAdapter
|
import android.bluetooth.BluetoothAdapter
|
||||||
|
|
@ -27,6 +30,7 @@ class BleRelayManager(
|
||||||
private val callbacks: BleCallbacks
|
private val callbacks: BleCallbacks
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val TAG = "BleRelayManager"
|
||||||
private val SERVICE_UUID: UUID = UUID.fromString("4fafc201-1fb5-459e-8fcc-c5c9c331914b")
|
private val SERVICE_UUID: UUID = UUID.fromString("4fafc201-1fb5-459e-8fcc-c5c9c331914b")
|
||||||
private val CHARACTERISTIC_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26d8")
|
private val CHARACTERISTIC_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26d8")
|
||||||
private val UUID_CHAR_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26d9")
|
private val UUID_CHAR_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26d9")
|
||||||
|
|
@ -55,7 +59,11 @@ class BleRelayManager(
|
||||||
override fun onScanResult(callbackType: Int, result: ScanResult) {
|
override fun onScanResult(callbackType: Int, result: ScanResult) {
|
||||||
val device = result.device ?: return
|
val device = result.device ?: return
|
||||||
val address = device.address ?: return
|
val address = device.address ?: return
|
||||||
if (triedAddresses.contains(address)) return
|
Log.d(TAG, "Found device: ${device.name ?: ""} ($address)")
|
||||||
|
if (triedAddresses.contains(address)) {
|
||||||
|
Log.d(TAG, "Skipping already-tried device $address")
|
||||||
|
return
|
||||||
|
}
|
||||||
stopScan()
|
stopScan()
|
||||||
connectToDevice(device)
|
connectToDevice(device)
|
||||||
}
|
}
|
||||||
|
|
@ -151,7 +159,89 @@ class BleRelayManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
private val gattCallback = object : android.bluetooth.BluetoothGattCallback() {
|
private val gattCallback = object : android.bluetooth.BluetoothGattCallback() {
|
||||||
// implemented in next task
|
@SuppressLint("MissingPermission")
|
||||||
|
override fun onConnectionStateChange(gatt: android.bluetooth.BluetoothGatt, status: Int, newState: Int) {
|
||||||
|
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
||||||
|
if (hmacKey == null) {
|
||||||
|
Log.w(TAG, "GATT connected but no key -- disconnecting")
|
||||||
|
callbacks.onStatusChanged(BleStatus.NoKey)
|
||||||
|
gatt.disconnect()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Log.d(TAG, "GATT connected")
|
||||||
|
bluetoothGatt = gatt
|
||||||
|
gatt.discoverServices()
|
||||||
|
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
||||||
|
Log.d(TAG, "GATT disconnected")
|
||||||
|
callbacks.onStatusChanged(BleStatus.Disconnected)
|
||||||
|
gatt.close()
|
||||||
|
bluetoothGatt = null
|
||||||
|
relayCharacteristic = null
|
||||||
|
seq = 1
|
||||||
|
handler.postDelayed(scanRunnable, RECONNECT_DELAY_MS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
override fun onServicesDiscovered(gatt: android.bluetooth.BluetoothGatt, status: Int) {
|
||||||
|
if (status != BluetoothGatt.GATT_SUCCESS) {
|
||||||
|
Log.w(TAG, "Service discovery failed (status=$status)")
|
||||||
|
callbacks.onStatusChanged(BleStatus.ServiceError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Services discovered, requesting MTU...")
|
||||||
|
gatt.requestMtu(256)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
override fun onMtuChanged(gatt: android.bluetooth.BluetoothGatt, mtu: Int, status: Int) {
|
||||||
|
Log.d(TAG, "MTU changed to $mtu")
|
||||||
|
val service = gatt.getService(SERVICE_UUID)
|
||||||
|
if (service != null) {
|
||||||
|
relayCharacteristic = service.getCharacteristic(CHARACTERISTIC_UUID)
|
||||||
|
val uuidChar = service.getCharacteristic(UUID_CHAR_UUID)
|
||||||
|
if (relayCharacteristic != null && uuidChar != null) {
|
||||||
|
Log.d(TAG, "Reading device UUID characteristic...")
|
||||||
|
gatt.readCharacteristic(uuidChar)
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Service or characteristics not found")
|
||||||
|
callbacks.onStatusChanged(BleStatus.ServiceNotFound)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Service not found")
|
||||||
|
callbacks.onStatusChanged(BleStatus.ServiceNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
override fun onCharacteristicRead(gatt: android.bluetooth.BluetoothGatt, characteristic: android.bluetooth.BluetoothGattCharacteristic, value: ByteArray, status: Int) {
|
||||||
|
if (characteristic.uuid == UUID_CHAR_UUID && status == BluetoothGatt.GATT_SUCCESS) {
|
||||||
|
val storedUuid = deviceUuid
|
||||||
|
if (storedUuid != null && value.contentEquals(storedUuid)) {
|
||||||
|
Log.d(TAG, "UUID verified, setting up relay")
|
||||||
|
relayCharacteristic?.let { rc ->
|
||||||
|
gatt.setCharacteristicNotification(rc, true)
|
||||||
|
val descriptor = rc.getDescriptor(CCCD_UUID)
|
||||||
|
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
|
||||||
|
gatt.writeDescriptor(descriptor)
|
||||||
|
}
|
||||||
|
callbacks.onStatusChanged(BleStatus.Connected)
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "UUID mismatch -- disconnecting")
|
||||||
|
triedAddresses.add(gatt.device.address)
|
||||||
|
callbacks.onStatusChanged(BleStatus.WrongDevice)
|
||||||
|
gatt.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
override fun onCharacteristicWrite(gatt: android.bluetooth.BluetoothGatt, characteristic: android.bluetooth.BluetoothGattCharacteristic, status: Int) {
|
||||||
|
if (status != BluetoothGatt.GATT_SUCCESS) {
|
||||||
|
Log.w(TAG, "Write failed (status=$status)")
|
||||||
|
callbacks.onStatusChanged(BleStatus.CommError)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadState() {
|
private fun loadState() {
|
||||||
|
|
@ -161,6 +251,7 @@ class BleRelayManager(
|
||||||
val hexUuid = prefs.getString(PrefsHelper.PREF_UUID, null)
|
val hexUuid = prefs.getString(PrefsHelper.PREF_UUID, null)
|
||||||
deviceUuid = hexUuid?.let { hexStringToBytes(it) }
|
deviceUuid = hexUuid?.let { hexStringToBytes(it) }
|
||||||
seq = prefs.getInt(PrefsHelper.PREF_SEQ, 1).coerceAtMost(PrefsHelper.SEQ_MAX)
|
seq = prefs.getInt(PrefsHelper.PREF_SEQ, 1).coerceAtMost(PrefsHelper.SEQ_MAX)
|
||||||
|
Log.d(TAG, "HMAC key loaded: ${hmacKey?.size ?: "none"} bytes, UUID: ${deviceUuid?.size ?: "none"} bytes, seq=$seq")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hexStringToBytes(hex: String): ByteArray? {
|
private fun hexStringToBytes(hex: String): ByteArray? {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue