|
|
@@ -21,9 +21,12 @@ import com.iscs.bozzys.utils.byteArrayToHexString
|
|
|
import com.iscs.bozzys.utils.hexToByteArray
|
|
|
import kotlinx.coroutines.CancellableContinuation
|
|
|
import kotlinx.coroutines.CompletableDeferred
|
|
|
+import kotlinx.coroutines.CoroutineScope
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
-import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
|
+import kotlinx.coroutines.SupervisorJob
|
|
|
import kotlinx.coroutines.TimeoutCancellationException
|
|
|
+import kotlinx.coroutines.delay
|
|
|
+import kotlinx.coroutines.launch
|
|
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
|
import kotlinx.coroutines.withContext
|
|
|
import kotlinx.coroutines.withTimeout
|
|
|
@@ -64,6 +67,11 @@ class BleManager(
|
|
|
// 待响应式的请求数据接收池
|
|
|
private val receiverPool = ConcurrentHashMap<String, CompletableDeferred<BleFrame>>()
|
|
|
|
|
|
+ // 是否已经连接
|
|
|
+ private var isConnected = false
|
|
|
+
|
|
|
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
|
|
+
|
|
|
// 蓝牙扫描回调
|
|
|
private val scanCallback = object : ScanCallback() {
|
|
|
|
|
|
@@ -88,108 +96,6 @@ class BleManager(
|
|
|
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 蓝牙Gatt连接回调
|
|
|
- */
|
|
|
- @OptIn(ExperimentalCoroutinesApi::class)
|
|
|
- private val gattCallback = object : BluetoothGattCallback() {
|
|
|
-
|
|
|
- @SuppressLint("MissingPermission")
|
|
|
- override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
|
|
|
- if (newState == BluetoothProfile.STATE_CONNECTED) {
|
|
|
- LogUtil.i(TAG, "gattCallback onConnectionStateChange() gatt connected")
|
|
|
- gatt.discoverServices()
|
|
|
- } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
|
|
- LogUtil.i(TAG, "gattCallback onConnectionStateChange() gatt disconnected")
|
|
|
- if (!(this@BleManager.doneConnect?.isCompleted ?: false)) {
|
|
|
- this@BleManager.doneConnect?.resume(BleConnectResult(false), null)
|
|
|
- // 执行断开连接操作
|
|
|
- gatt.close()
|
|
|
- device = null
|
|
|
- this@BleManager.gatt = null
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @SuppressLint("MissingPermission")
|
|
|
- override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
|
|
|
- if (status == BluetoothGatt.GATT_SUCCESS) {
|
|
|
- LogUtil.i(TAG, "gattCallback onServicesDiscovered() find services success")
|
|
|
- // 执行设置mtu操作
|
|
|
- gatt.requestMtu(mtu)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
|
|
|
- super.onMtuChanged(gatt, mtu, status)
|
|
|
- LogUtil.i(TAG, "gattCallback onMtuChanged() mtu $mtu")
|
|
|
- Thread {
|
|
|
- val indicateOk = enableIndicate()
|
|
|
- SystemClock.sleep(1000)
|
|
|
- if (needIndicate) {
|
|
|
- if (!(this@BleManager.doneConnect?.isCompleted ?: false)) this@BleManager.doneConnect?.resume(BleConnectResult(indicateOk), null)
|
|
|
- } else {
|
|
|
- if (!(this@BleManager.doneConnect?.isCompleted ?: false)) this@BleManager.doneConnect?.resume(BleConnectResult(true), null)
|
|
|
- }
|
|
|
- }.start()
|
|
|
- }
|
|
|
-
|
|
|
- @SuppressLint("MissingPermission")
|
|
|
- override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray, status: Int) {
|
|
|
- super.onCharacteristicRead(gatt, characteristic, value, status)
|
|
|
- LogUtil.i(TAG, "gattCallback onCharacteristicRead()")
|
|
|
- }
|
|
|
-
|
|
|
- override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) {
|
|
|
- super.onDescriptorRead(gatt, descriptor, status, value)
|
|
|
- LogUtil.i(TAG, "gattCallback onDescriptorRead() data write success")
|
|
|
- }
|
|
|
-
|
|
|
- override fun onCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?) {
|
|
|
- super.onCharacteristicChanged(gatt, characteristic)
|
|
|
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) return
|
|
|
- LogUtil.w(TAG, "gattCallback <--- ${characteristic?.value?.byteArrayToHexString(" ")}")
|
|
|
- val data = characteristic?.value ?: byteArrayOf()
|
|
|
- var key = ""
|
|
|
- receiverPool.forEach { item ->
|
|
|
- if (data.byteArrayToHexString().startsWith(item.key.split("_")[1])) {
|
|
|
- // 找到指定响应体
|
|
|
- key = item.key
|
|
|
- return@forEach
|
|
|
- }
|
|
|
- }
|
|
|
- if (key.isNotEmpty()) {
|
|
|
- val deferred = receiverPool.remove(key)
|
|
|
- if (deferred != null && !deferred.isCompleted) {
|
|
|
- val spl = key.split("_")
|
|
|
- val rspCodeLen = spl[1].length / 2
|
|
|
- deferred.complete(BleFrame(spl[0].hexToByteArray(), data.copyOfRange(rspCodeLen, data.size), spl[1].hexToByteArray()))
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray) {
|
|
|
- super.onCharacteristicChanged(gatt, characteristic, value)
|
|
|
- LogUtil.w(TAG, "gattCallback <--- ${value.byteArrayToHexString(" ")}")
|
|
|
- var key = ""
|
|
|
- receiverPool.forEach { item ->
|
|
|
- if (value.byteArrayToHexString().startsWith(item.key.split("_")[1])) {
|
|
|
- // 找到指定响应体
|
|
|
- key = item.key
|
|
|
- return@forEach
|
|
|
- }
|
|
|
- }
|
|
|
- if (key.isNotEmpty()) {
|
|
|
- val deferred = receiverPool.remove(key)
|
|
|
- if (deferred != null && !deferred.isCompleted) {
|
|
|
- val spl = key.split("_")
|
|
|
- val rspCodeLen = spl[1].length / 2
|
|
|
- deferred.complete(BleFrame(spl[0].hexToByteArray(), value.copyOfRange(rspCodeLen, value.size), spl[1].hexToByteArray()))
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 协程方式的连接
|
|
|
*/
|
|
|
@@ -201,6 +107,23 @@ class BleManager(
|
|
|
} else {
|
|
|
this.device = device
|
|
|
innerConnect()
|
|
|
+ timeoutDisconnect()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 超时未连接处理
|
|
|
+ */
|
|
|
+ private fun timeoutDisconnect() {
|
|
|
+ scope.launch {
|
|
|
+ delay(5000)
|
|
|
+ // 未连接成功执行断开连接操作
|
|
|
+ if (!isConnected) {
|
|
|
+ if (!(this@BleManager.doneConnect?.isCompleted ?: false)) {
|
|
|
+ this@BleManager.doneConnect?.resume(BleConnectResult(false), null)
|
|
|
+ }
|
|
|
+ release()
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -241,10 +164,109 @@ class BleManager(
|
|
|
*/
|
|
|
@SuppressLint("MissingPermission")
|
|
|
private fun innerConnect() {
|
|
|
- // context -> app
|
|
|
- // autoConnect -> false 避免设备连接异常
|
|
|
try {
|
|
|
- gatt = device?.connectGatt(app, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
|
|
|
+ gatt = device?.connectGatt(app, false, object : BluetoothGattCallback() {
|
|
|
+
|
|
|
+ @SuppressLint("MissingPermission")
|
|
|
+ override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
|
|
|
+ if (newState == BluetoothProfile.STATE_CONNECTED) {
|
|
|
+ LogUtil.i(TAG, "$gatt gattCallback onConnectionStateChange() gatt connected")
|
|
|
+ gatt.discoverServices()
|
|
|
+ } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
|
|
+ LogUtil.i(TAG, "$gatt gattCallback onConnectionStateChange() gatt disconnected")
|
|
|
+ isConnected = false
|
|
|
+ if (!(this@BleManager.doneConnect?.isCompleted ?: false)) {
|
|
|
+ this@BleManager.doneConnect?.resume(BleConnectResult(false), null)
|
|
|
+ }
|
|
|
+ release()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressLint("MissingPermission")
|
|
|
+ override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
|
|
|
+ if (status == BluetoothGatt.GATT_SUCCESS) {
|
|
|
+ LogUtil.i(TAG, "gattCallback onServicesDiscovered() find services success")
|
|
|
+ // 执行设置mtu操作
|
|
|
+ gatt.requestMtu(mtu)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
|
|
|
+ super.onMtuChanged(gatt, mtu, status)
|
|
|
+ LogUtil.i(TAG, "gattCallback onMtuChanged() mtu $mtu")
|
|
|
+ Thread {
|
|
|
+ val indicateOk = enableIndicate()
|
|
|
+ SystemClock.sleep(1000)
|
|
|
+ if (needIndicate) {
|
|
|
+ isConnected = indicateOk
|
|
|
+ if (!(this@BleManager.doneConnect?.isCompleted ?: false)) {
|
|
|
+ this@BleManager.doneConnect?.resume(BleConnectResult(indicateOk), null)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ isConnected = false
|
|
|
+ if (!(this@BleManager.doneConnect?.isCompleted ?: false)) {
|
|
|
+ this@BleManager.doneConnect?.resume(BleConnectResult(true), null)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }.start()
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressLint("MissingPermission")
|
|
|
+ override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray, status: Int) {
|
|
|
+ super.onCharacteristicRead(gatt, characteristic, value, status)
|
|
|
+ LogUtil.i(TAG, "gattCallback onCharacteristicRead()")
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) {
|
|
|
+ super.onDescriptorRead(gatt, descriptor, status, value)
|
|
|
+ LogUtil.i(TAG, "gattCallback onDescriptorRead() data write success")
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?) {
|
|
|
+ super.onCharacteristicChanged(gatt, characteristic)
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) return
|
|
|
+ LogUtil.w(TAG, "gattCallback <--- ${characteristic?.value?.byteArrayToHexString(" ")}")
|
|
|
+ val data = characteristic?.value ?: byteArrayOf()
|
|
|
+ var key = ""
|
|
|
+ receiverPool.forEach { item ->
|
|
|
+ if (data.byteArrayToHexString().startsWith(item.key.split("_")[1])) {
|
|
|
+ // 找到指定响应体
|
|
|
+ key = item.key
|
|
|
+ return@forEach
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (key.isNotEmpty()) {
|
|
|
+ val deferred = receiverPool.remove(key)
|
|
|
+ if (deferred != null && !deferred.isCompleted) {
|
|
|
+ val spl = key.split("_")
|
|
|
+ val rspCodeLen = spl[1].length / 2
|
|
|
+ deferred.complete(BleFrame(spl[0].hexToByteArray(), data.copyOfRange(rspCodeLen, data.size), spl[1].hexToByteArray()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray) {
|
|
|
+ super.onCharacteristicChanged(gatt, characteristic, value)
|
|
|
+ LogUtil.w(TAG, "gattCallback <--- ${value.byteArrayToHexString(" ")}")
|
|
|
+ var key = ""
|
|
|
+ receiverPool.forEach { item ->
|
|
|
+ if (value.byteArrayToHexString().startsWith(item.key.split("_")[1])) {
|
|
|
+ // 找到指定响应体
|
|
|
+ key = item.key
|
|
|
+ return@forEach
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (key.isNotEmpty()) {
|
|
|
+ val deferred = receiverPool.remove(key)
|
|
|
+ if (deferred != null && !deferred.isCompleted) {
|
|
|
+ val spl = key.split("_")
|
|
|
+ val rspCodeLen = spl[1].length / 2
|
|
|
+ deferred.complete(BleFrame(spl[0].hexToByteArray(), value.copyOfRange(rspCodeLen, value.size), spl[1].hexToByteArray()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, BluetoothDevice.TRANSPORT_LE)
|
|
|
+ LogUtil.d(TAG, "$gatt gatt created")
|
|
|
} catch (e: Exception) {
|
|
|
LogUtil.e(TAG, "connect() connect to device ${device?.address} failed $e")
|
|
|
}
|
|
|
@@ -255,9 +277,20 @@ class BleManager(
|
|
|
*/
|
|
|
@SuppressLint("MissingPermission")
|
|
|
fun disconnect() {
|
|
|
- device?.let {
|
|
|
- gatt?.disconnect()
|
|
|
- }
|
|
|
+ device?.let { gatt?.disconnect() }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 资源释放
|
|
|
+ */
|
|
|
+ @SuppressLint("MissingPermission")
|
|
|
+ private fun release() {
|
|
|
+ // 执行断开连接操作
|
|
|
+ gatt?.close()
|
|
|
+ gatt = null
|
|
|
+ device = null
|
|
|
+ doneConnect = null
|
|
|
+ isConnected = false
|
|
|
}
|
|
|
|
|
|
/**
|