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

Android USB Accessory Mode触发IOError问题求助

Android AOA v2 USB交互故障排查与修复

问题1:脚本无法启动Accessory模式(多数情况)

核心原因

  1. permissionIntent初始化顺序错误:requestUsbPermissions()在permissionIntent赋值前调用,导致权限请求传入null,无法触发系统授权弹窗。
  2. 未监听UsbManager.ACTION_USB_ACCESSORY_ATTACHED广播:插拔USB后系统发送该广播,但App未注册接收器,无法自动触发权限请求。
  3. accessoryList检查时机过早:App启动时USB设备可能未完成枚举,导致设备列表为空。

修复(MainActivity.kt)

调整初始化顺序,新增插拔广播监听,增加枚举延迟:

import android.os.Handler
import android.os.Looper

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    Log.i(TAG_USB, "MainActivity onCreate")
    mUsbManager = getSystemService(Context.USB_SERVICE) as UsbManager
    setContent {
        AMX2Theme {
            Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
                Greeting("Android")
            }
        }
    }

    // 先初始化permissionIntent,确保非空
    permissionIntent = PendingIntent.getBroadcast(this, 0, Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE)
    val filter = IntentFilter(ACTION_USB_PERMISSION)
    filter.addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED) // 新增设备插拔监听
    registerReceiver(usbReceiver, filter)

    // 延迟100ms请求权限,等待设备完成枚举
    Handler(Looper.getMainLooper()).postDelayed({
        requestUsbPermissions()
    }, 100)
}

// 更新广播接收器,处理插拔事件
private val usbReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        when(intent.action) {
            ACTION_USB_PERMISSION -> {
                synchronized(this) {
                    val accessory: UsbAccessory? = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY)
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        Log.i(TAG_USB, "Permission granted, opening device")
                        openDevice()
                    } else {
                        Log.d(TAG_USB, "permission denied for accessory $accessory")
                        mUsbManager?.requestPermission(accessory, permissionIntent)
                    }
                }
            }
            UsbManager.ACTION_USB_ACCESSORY_ATTACHED -> {
                Log.i(TAG_USB, "Accessory attached, requesting permissions")
                requestUsbPermissions()
            }
        }
    }
}

问题2:连接后首次写入成功,后续触发[Errno 5] IO错误

核心原因

  1. Python端硬编码端点地址:切换到Accessory模式后,设备接口和端点会变化,直接使用write(0x02)可能指向无效端点。
  2. Android端BufferedInputStream导致流阻塞:缓冲区预读会破坏底层流的状态一致性。
  3. 资源未完全释放:线程退出时仅关闭输入流,未释放输出流和文件描述符,导致设备资源泄漏。

修复

Python端(client.py)

动态获取Accessory模式下的有效端点:

def start_accessory_mode():
    dev = get_accessory()
    if not dev:
        print('Android accessory not found')
        print('Try to start accessory mode')
        dev = get_android_device()
        set_protocol(dev)
        set_strings(dev)
        set_accessory_mode(dev)
        time.sleep(2)  # 延长等待时间,确保设备完成模式切换
        dev = get_accessory()
        if not dev:
            sys.exit('Unable to start accessory mode')
    
    # 获取Accessory模式下的接口和端点
    dev.set_configuration()
    cfg = dev.get_active_configuration()
    intf = cfg[(0,0)]
    # 匹配输出端点(主机→设备)
    ep_out = usb.util.find_descriptor(
        intf,
        custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT
    )
    # 匹配输入端点(设备→主机)
    ep_in = usb.util.find_descriptor(
        intf,
        custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN
    )
    if not ep_out or not ep_in:
        sys.exit('Cannot find accessory endpoints')
    return dev, ep_out, ep_in

# 修改写入逻辑,使用正确的端点
def wait_for_command(ldev: usb.core.Device, ep_out, ep_in):
    while True:
        try:
            msg = "beans"
            ret = ep_out.write(msg, 1000)
            if ret == len(msg):
                print(' - Write OK')
            time.sleep(0.2)
        except usb.core.USBError as e:
            print("USB write error", e)
            if e.errno == 5:
                print("Device disconnected, exiting")
                break
        except KeyboardInterrupt:
            print("Bye!")
            break

def main():
    dev, ep_out, ep_in = start_accessory_mode()
    wait_for_command(dev, ep_out, ep_in)

Android端(MainActivity.kt)

替换缓冲流,完善资源释放逻辑:

private var mListenerTask: Runnable = object : Runnable {
    override fun run() {
        Log.i(TAG_USB, "Thread running")
        val buffer = ByteArray(16384)
        while(true) {
            try {
                Log.i(TAG_USB, "About to read")
                val ret: Int = mInputStream?.read(buffer) ?: -1
                Log.i(TAG_USB, "Read $ret bytes")
                if (ret > 0) {
                    val msg = String(buffer, 0, ret) // 仅转换实际读取的字节
                    Log.v(TAG_USB, "Got msg: $msg")
                } else if (ret == -1) {
                    Log.e(TAG_USB, "Stream closed by peer")
                    break
                } else {
                    Log.e(TAG_USB, "Read error $ret")
                }
            } catch (e: IOException) {
                Log.e(TAG_USB, "Read failed: ${e.message}")
                break
            }
            try {
                Thread.sleep(1000)
            } catch (e: InterruptedException) {
                break
            }
        }
        // 退出时释放所有USB资源
        cleanupResources()
    }
}

private fun cleanupResources() {
    try {
        mInputStream?.close()
        mOutputStream?.close()
        mFileDescriptor?.close()
    } catch (e: IOException) {
        Log.e(TAG_USB, "Error closing resources: ${e.message}")
    } finally {
        mInputStream = null
        mOutputStream = null
        mFileDescriptor = null
        bStarted = false
        mThread = null
    }
}

// 在Activity销毁时也执行资源清理
override fun onDestroy() {
    cleanupResources()
    unregisterReceiver(usbReceiver)
    super.onDestroy()
}

问题3:Android端read抛出EIO (I/O error)

核心原因

  1. 流状态不一致:Python端写入后设备流状态变化,但Android端持续读取已失效的流。
  2. 资源泄漏:未完全释放USB资源,导致设备处于异常状态。

修复

上述代码修改已覆盖该问题:

  • 确保所有USB资源在退出时被正确释放。
  • 读取时检查流有效性,避免读取已关闭的流。
  • 使用动态匹配的端点进行通信,避免无效传输。

额外配置:确保AndroidManifest.xml添加AOA相关声明

<uses-feature android:name="android.hardware.usb.accessory" />
<uses-permission android:name="android.permission.USB_PERMISSION" />

<activity
    android:name=".MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
    </intent-filter>
    <meta-data
        android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
        android:resource="@xml/accessory_filter" />
</activity>

创建res/xml/accessory_filter.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <usb-accessory
        manufacturer="Segment 6"
        model="AMX2"
        version="0.1.0-beta" />
</resources>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 00:39:54