Android 13(API33)读取USB设备遇EACCES权限拒绝问题求助
问题分析与解决方案
核心错误原因
- 你直接通过
FileInputStream访问/dev/bus/usb/xxx路径是完全错误的操作方式。Android的USB Host API设计是通过UsbManager、UsbDeviceConnection和端点(Endpoint)完成设备通信,而非直接操作/dev下的系统设备节点文件——这些文件属于系统级资源,普通应用即便拿到USB权限也无法直接打开。 - 你申请的
READ_EXTERNAL_STORAGE、READ_MEDIA_*权限是针对外部存储(如SD卡)的,和USB设备通信完全无关,这些权限解决不了当前问题。
正确的USB设备数据读取流程
1. 确认USB权限已成功获取
你当前的requestUSBPermission方法只是发起权限请求,还需要通过广播接收器确认用户是否授予权限:
private val usbPermissionReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (ACTION_USB_PERMISSION == intent?.action) { synchronized(this) { val device = intent.getParcelableExtra<UsbDevice>(UsbManager.EXTRA_DEVICE) if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { device?.let { // 权限已授予,在这里启动设备通信逻辑 readUsbDeviceData(it) } } else { Toast.makeText(context, "USB权限被拒绝", Toast.LENGTH_SHORT).show() } } } } } // 在Activity的onCreate中注册广播 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ...其他初始化代码 val filter = IntentFilter(ACTION_USB_PERMISSION) registerReceiver(usbPermissionReceiver, filter) } // 在onDestroy中注销广播,避免内存泄漏 override fun onDestroy() { super.onDestroy() unregisterReceiver(usbPermissionReceiver) }
2. 通过USB Host API读取设备数据
使用官方API通过端点传输数据,而非直接访问文件,示例代码如下:
fun readUsbDeviceData(device: UsbDevice) { val manager = getSystemService(USB_SERVICE) as UsbManager val connection = manager.openDevice(device) ?: run { Toast.makeText(this, "无法打开USB设备", Toast.LENGTH_SHORT).show() return } // 声明要使用的USB接口(通常为接口0,需根据你的设备实际情况调整) val usbInterface = device.getInterface(0) if (!connection.claimInterface(usbInterface, true)) { Toast.makeText(this, "无法声明USB接口", Toast.LENGTH_SHORT).show() connection.close() return } // 找到用于接收数据的批量传输端点(USB_DIR_IN方向) var inputEndpoint: UsbEndpoint? = null for (i in 0 until usbInterface.endpointCount) { val endpoint = usbInterface.getEndpoint(i) if (endpoint.type == UsbConstants.USB_ENDPOINT_XFER_BULK && endpoint.direction == UsbConstants.USB_DIR_IN) { inputEndpoint = endpoint break } } inputEndpoint?.let { endpoint -> // 准备缓冲区,大小需匹配设备端点的最大包长 val buffer = ByteArray(endpoint.maxPacketSize) try { // 从端点读取数据,超时时间设为5000ms val bytesRead = connection.bulkTransfer(endpoint, buffer, buffer.size, 5000) if (bytesRead > 0) { // 处理读取到的数据,示例:保存到应用私有目录 val outputFile = File(filesDir, "received_data.mp4") FileOutputStream(outputFile).use { outputStream -> outputStream.write(buffer, 0, bytesRead) } Toast.makeText(this, "数据读取成功", Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, "未读取到数据", Toast.LENGTH_SHORT).show() } } catch (e: Exception) { e.printStackTrace() Toast.makeText(this, "读取失败: ${e.message}", Toast.LENGTH_SHORT).show() } finally { // 释放接口并关闭连接 connection.releaseInterface(usbInterface) connection.close() } } ?: run { Toast.makeText(this, "未找到可用的输入端点", Toast.LENGTH_SHORT).show() connection.releaseInterface(usbInterface) connection.close() } }
3. 额外注意事项
- 如果是U盘这类存储设备,不需要用USB Host API直接读取端点,建议使用**Storage Access Framework(SAF)**让用户选择文件,操作更简单且无需处理复杂的USB协议:
private const val REQUEST_CODE_OPEN_DOCUMENT = 1001 // 启动文件选择器 fun openUsbFilePicker() { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) type = "video/mp4" // 匹配你要读取的文件类型 } startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT) } // 处理选择结果 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_CODE_OPEN_DOCUMENT && resultCode == RESULT_OK) { data?.data?.let { uri -> contentResolver.openInputStream(uri)?.use { inputStream -> // 读取文件内容逻辑 } } } } - 若为自定义USB设备,需在Manifest中添加设备过滤,确保系统能识别你的设备:
然后在<uses-feature android:name="android.hardware.usb.host" android:required="true" /> <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/usb_device_filter" />res/xml/usb_device_filter.xml中添加设备的VID和PID:<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="你的设备VID" product-id="你的设备PID" /> </resources>
内容的提问来源于stack exchange,提问作者Djim Oomes
相关产品推荐
相关产品推荐

