From 3b56247b66f182ced14cfe6ce6fcc4f7ea7fe11b Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Thu, 27 Nov 2025 15:46:25 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=BF=9E=E6=8E=A5=E9=80=9A?= =?UTF-8?q?=E9=81=93=EF=BC=8Cphy=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yunqiinnovation/ble_service/BleService.kt | 146 +++++++++++++----- 1 file changed, 104 insertions(+), 42 deletions(-) diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt index 02f2c6cc6..1fc62da1b 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt @@ -134,6 +134,12 @@ object BleService { private var maxMtu = 517 // 最大MTU值 (512 + 5字节头部) private var isWaitingForWriteCallback = false // 是否正在等待写入回调 + // 通知注册串行化相关变量 + private val notificationSetupQueue: ArrayDeque = ArrayDeque() + private var isDescriptorWriteInProgress = false + private var notificationTargetsTotal = 0 + private var notificationTargetsSucceeded = 0 + // 初始化状态 private var isInitialized = false @@ -743,33 +749,6 @@ object BleService { updateConnectionState(BleConst.STATE_CONNECTED) Log.d(TAG, "成功连接Ble蓝牙设备:${g.device.name}") companionManager?.unregisterBackgroundScan() - // 设置PHY值为2M以提高传输速度 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - try { - // 首先尝试读取当前PHY状态 - val readResult = g.readPhy() - Log.d(TAG, "读取PHY状态请求结果: $readResult") - - // 延迟一段时间后再设置PHY,确保readPhy完成 - Handler(Looper.getMainLooper()).postDelayed({ - try { - val setResult = g.setPreferredPhy( - BluetoothDevice.PHY_LE_2M_MASK, // TX PHY: 2M - BluetoothDevice.PHY_LE_2M_MASK, // RX PHY: 2M - BluetoothDevice.PHY_OPTION_NO_PREFERRED - ) - Log.d(TAG, "设置PHY为2M请求结果: $setResult") - } catch (e: Exception) { - Log.w(TAG, "设置PHY失败: ${e.message}") - } - }, 500) // 延迟500ms - } catch (e: Exception) { - Log.w(TAG, "读取PHY失败: ${e.message},将使用默认1M PHY") - } - } else { - Log.w(TAG, "当前Android版本不支持PHY设置(需要API 26+),使用默认1M PHY") - } - g.discoverServices() } @@ -861,20 +840,23 @@ object BleService { startBytesStatistics() writeChar?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE callWriteChar?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE - //要先设置音频服务的通知,否则接收不到 - // 设置音频服务的通知(如果存在) + // 串行注册通知:等待上一个通道成功后再注册下一个 + notificationSetupQueue.clear() + isDescriptorWriteInProgress = false + notificationTargetsTotal = 0 + notificationTargetsSucceeded = 0 if (callAudioChar != null && audioChar != null) { - setupNotifications(g, callAudioChar) - setupNotifications(g, audioChar) - - - Log.i(TAG, "通话音频服务特征找到并设置通知") - + notificationSetupQueue.add(callAudioChar!!) + notificationSetupQueue.add(audioChar!!) + Log.i(TAG, "通话音频服务特征找到,准备串行开启通知") } else { - Log.w(TAG, "通话音频服务特征未找到") + Log.w(TAG, "通话音频服务特征未完整找到") } - - setupNotifications(g, notifyChar) + // 将命令通知通道也纳入串行队列,避免并发写入描述符 + notifyChar?.let { notificationSetupQueue.add(it) } + notificationTargetsTotal = notificationSetupQueue.size + // 启动处理队列 + processNextNotificationDescriptor(g) // 在服务发现完成后,请求最大MTU //requestMaxMtu(g) @@ -951,8 +933,27 @@ object BleService { override fun onDescriptorWrite( g: BluetoothGatt, d: BluetoothGattDescriptor, status: Int ) { - if (status == BluetoothGatt.GATT_SUCCESS) - Log.i(TAG, "通知描述符写入成功: ${d.characteristic.uuid}") + if (d.uuid == BleConst.CLIENT_CHAR_CONFIG_UUID) { + val charUuid = d.characteristic.uuid + if (status == BluetoothGatt.GATT_SUCCESS) { + Log.i(TAG, "通知描述符写入成功: ${charUuid}") + notificationTargetsSucceeded++ + } else { + Log.e(TAG, "通知描述符写入失败: ${charUuid}, status=${status}") + } + + // 当前一次写入完成,移除队首并继续处理下一个 + if (notificationSetupQueue.isNotEmpty() && notificationSetupQueue.first().uuid == charUuid) { + notificationSetupQueue.removeFirst() + } + isDescriptorWriteInProgress = false + // 如果所有通知已处理完毕,且全部成功,则设置PHY + if (notificationSetupQueue.isEmpty()) { + onAllNotificationsProcessed(g) + } else { + processNextNotificationDescriptor(g) + } + } } /** @@ -1010,6 +1011,66 @@ object BleService { } return true } + + /** + * 串行处理通知描述符写入队列 + * 功能:遵循Android BLE单操作约束,确保按顺序开启各通道通知 + * @param g BluetoothGatt实例 + */ + private fun processNextNotificationDescriptor(g: BluetoothGatt) { + if (isDescriptorWriteInProgress) return + val next = notificationSetupQueue.firstOrNull() ?: run { + onAllNotificationsProcessed(g) + return + } + val started = setupNotifications(g, next) + if (started) { + isDescriptorWriteInProgress = true + } else { + // 当前特征无法启动通知,跳过并尝试下一个 + notificationSetupQueue.removeFirst() + processNextNotificationDescriptor(g) + } + } + + /** + * 所有通知通道处理完成后的回调:在全部成功时设置PHY + */ + private fun onAllNotificationsProcessed(g: BluetoothGatt) { + if (notificationTargetsTotal > 0 && notificationTargetsSucceeded == notificationTargetsTotal) { + applyPreferredPhy(g) + } else { + Log.w(TAG, "通知通道未全部注册成功(${notificationTargetsSucceeded}/${notificationTargetsTotal}),跳过PHY设置") + } + } + + /** + * 读取并设置首选PHY为2M(仅API 26+) + */ + private fun applyPreferredPhy(g: BluetoothGatt) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + try { + val readResult = g.readPhy() + Log.d(TAG, "读取PHY状态请求结果: $readResult") + Handler(Looper.getMainLooper()).postDelayed({ + try { + val setResult = g.setPreferredPhy( + BluetoothDevice.PHY_LE_2M_MASK, + BluetoothDevice.PHY_LE_2M_MASK, + BluetoothDevice.PHY_OPTION_NO_PREFERRED + ) + Log.d(TAG, "设置PHY为2M请求结果: $setResult") + } catch (e: Exception) { + Log.w(TAG, "设置PHY失败: ${e.message}") + } + }, 500) + } catch (e: Exception) { + Log.w(TAG, "读取PHY失败: ${e.message},将使用默认1M PHY") + } + } else { + Log.w(TAG, "当前Android版本不支持PHY设置(需要API 26+),使用默认1M PHY") + } + } /** * 请求最大MTU值 @@ -1033,6 +1094,7 @@ object BleService { * @param data 音频数据 (Opus编码) */ private fun processAudioData(data: ByteArray) { + recordfile?.saveAudioDataToWav(data) // 委托给Opus音频管理器处理 opusAudioManager.processAudioData(data) } @@ -1344,6 +1406,8 @@ object BleService { */ fun closeCodec(): Boolean { Log.i(TAG, "关闭编解码...") + recordfile?.closeFile() + recordfile1?.closeFile() val cmdData = byteArrayOf( BleConst.CODEC_CONTROL_CLOSE.toByte(), BleConst.AUDIO_CHANNEL_STEREO.toByte() @@ -1688,5 +1752,3 @@ object BleService { } } - -