Android USB Accessory Mode触发IOError问题求助
Android AOA v2 USB交互故障排查与修复
问题1:脚本无法启动Accessory模式(多数情况)
核心原因
permissionIntent初始化顺序错误:requestUsbPermissions()在permissionIntent赋值前调用,导致权限请求传入null,无法触发系统授权弹窗。- 未监听
UsbManager.ACTION_USB_ACCESSORY_ATTACHED广播:插拔USB后系统发送该广播,但App未注册接收器,无法自动触发权限请求。 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错误
核心原因
- Python端硬编码端点地址:切换到Accessory模式后,设备接口和端点会变化,直接使用
write(0x02)可能指向无效端点。 - Android端
BufferedInputStream导致流阻塞:缓冲区预读会破坏底层流的状态一致性。 - 资源未完全释放:线程退出时仅关闭输入流,未释放输出流和文件描述符,导致设备资源泄漏。
修复
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)
核心原因
- 流状态不一致:Python端写入后设备流状态变化,但Android端持续读取已失效的流。
- 资源泄漏:未完全释放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
相关产品推荐
相关产品推荐

