You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android TV无法识别安卓APP通过蓝牙Socket发送的命令

问题:蓝牙Socket发送命令至Android TV无响应

我已通过蓝牙Socket实现Android TV与安卓手机APP的连接,完成以下步骤:

  • 搜索设备——完成
  • 使用createBond配对设备——完成
  • 在通道4获取到Android TV的UUID(用于RF_COMM)

但尝试通过bluetoothsocket?.outputStream?.write("VolumeUp".toByteArray())发送音量加命令时,Android TV无任何反应。

附上相关代码:

if (
    ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) !=
        PackageManager.PERMISSION_GRANTED
) {
    return
}

Toast.makeText(this, deviceToPair.name, Toast.LENGTH_LONG).show()

bluetoothAdapter.cancelDiscovery()

try {
    val paringRequired = deviceToPair.createBond()
    val value = if (paringRequired) "Haa" else "Na"
    Log.d("Log", "Device name " + deviceToPair.name)
    Log.d("Log", "Device deviceAddress " + deviceToPair.address)
    Log.d("Log", "Device deviceClass " + deviceToPair.bluetoothClass)
    Log.d("Log", "Device bondState " + deviceToPair.bondState)
    Log.d("Log", "Device deviceType " + deviceToPair.type)

    Log.d("Log", deviceToPair.name + " ke Paring ke jarurat hai ho? " + value)
} catch (e: Exception) {
    Log.d("Log", "Exception while using socket connection : " + e.message)
}

try {
    val uuid = getWorkingUUIDFromDevice(deviceToPair)
    if (uuid != null) {
        val socket = deviceToPair.createRfcommSocketToServiceRecord(UUID.fromString(uuid))
        val thread = Thread {
            Thread.sleep(5000) // 5 seconds delay
            socket.connect()
            Log.d("Log", "Socket connect done : ")
            // socket?.outputStream?.write(KeyEvent.KEYCODE_HOME)
            val commandByte = AnymoteCommand.VOLUME_UP.commandName.toByteArray(Charsets.UTF_8)
            Log.d("Log", "Volume up sent : ")

            socket?.outputStream?.write("powerOff".toByteArray())
            Log.d("Log", "Data sending to tv : ")
            val inputStream = socket?.inputStream
            socket?.outputStream?.flush()

            Log.d("Log", "Got data from tv " + inputStream?.read())

            socket.close()
        }
        thread.start()
}

可能的原因及解决办法

1. 命令格式不匹配

Android TV的蓝牙控制服务通常不识别自定义字符串命令(如"VolumeUp"、"powerOff"),需遵循对应协议的编码规则:

  • 若使用HID协议(多数电视蓝牙控制基于此),需发送标准HID报告格式的按键数据,而非直接转字符串字节。比如音量加对应特定的HID报告数组,还要发送按键释放的报告。
  • 若代码中用到AnymoteCommand,说明目标服务基于Anymote协议,该协议使用Protocol Buffers序列化命令,不能直接将命令名转UTF-8字节发送,需按PB规范构建数据包。

2. 连接时机不可靠

代码中用固定5秒延迟等待配对完成,容易出现配对未完成就发起连接的情况:

  • 移除Thread.sleep(5000),改为注册BroadcastReceiver监听BluetoothDevice.ACTION_BOND_STATE_CHANGED事件,当设备状态变为BOND_BONDED时再发起Socket连接。

3. 权限与状态检查

  • 在Socket连接、流读写操作中捕获IOException,打印详细错误日志,排查是否存在连接失败、流写入异常的问题。
  • 确认getWorkingUUIDFromDevice获取的UUID对应电视上的蓝牙控制服务,比如HID服务的标准UUID为00001124-0000-1000-8000-00805f9b34fb。

4. 流操作逻辑问题

  • 避免连续发送多个命令,先单独测试一个命令(如仅发送音量加),确认有效性后再扩展。
  • 发送命令后需确保flush()调用,但部分服务要求特定的数据包结束符,需按协议规范补充。

修正后的关键代码示例

// 注册广播接收器监听配对状态
private val bondStateReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        intent ?: return
        if (BluetoothDevice.ACTION_BOND_STATE_CHANGED == intent.action) {
            val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
            if (device?.address == deviceToPair.address) {
                if (device.bondState == BluetoothDevice.BOND_BONDED) {
                    // 配对完成,发起连接
                    connectToTv(device)
                }
            }
        }
    }
}

// 连接电视并发送HID格式的音量加命令
private fun connectToTv(device: BluetoothDevice) {
    try {
        val hidUuid = UUID.fromString("00001124-0000-1000-8000-00805f9b34fb")
        val socket = device.createRfcommSocketToServiceRecord(hidUuid)
        Thread {
            try {
                socket.connect()
                Log.d("Log", "Socket连接成功")

                // 发送音量按下的HID报告
                val pressReport = byteArrayOf(0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00)
                socket.outputStream.write(pressReport)
                socket.outputStream.flush()

                // 延迟100ms后发送按键释放报告
                Thread.sleep(100)
                val releaseReport = byteArrayOf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
                socket.outputStream.write(releaseReport)
                socket.outputStream.flush()

                socket.close()
            } catch (e: IOException) {
                Log.e("Log", "Socket操作异常: ${e.message}")
            }
        }.start()
    } catch (e: Exception) {
        Log.e("Log", "连接初始化异常: ${e.message}")
    }
}

内容的提问来源于stack exchange,提问作者Honey Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 20:58:30