Android 14下USB串口通信权限申请失败问题求助
Android 14下USB权限申请异常问题
我开发了一款连接带重量传感器Arduino的Android应用,在Android 11上运行正常,但在Android 14环境测试时出现异常:要么不弹出USB权限申请弹窗直接自动拒绝,要么弹出权限申请后点击同意仍被拒绝。
已确认设备支持OTG,且使用“Serial USB Terminal”应用可正常与Arduino通信,说明硬件通信无问题。我并非编程新手,但刚接触Kotlin且久未编码。
以下是相关代码与日志:
USB通信处理类代码
class IOPesa(private val context: Context) { private var serialPort: UsbSerialDevice? = null private var responseData: String? = null private val usbManager: UsbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager private val responseLatch = CountDownLatch(1) companion object { private const val ACTION_USB_PERMISSION = "com.example.testapp.USB_PERMISSION" private const val TAG = "IOPesa" } private val usbPermissionReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { synchronized(this) { if (ACTION_USB_PERMISSION == intent.action) { val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) Log.d(TAG, "Broadcast received. Device: $device") if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false) && device != null) { Log.d(TAG, "Permission granted for device: ${device.deviceName}") connectToDevice(device) } else { Log.e(TAG, "Permission denied for the device ${device?.deviceName}") } } } } } init { val filter = IntentFilter(ACTION_USB_PERMISSION) context.registerReceiver(usbPermissionReceiver, filter, Context.RECEIVER_EXPORTED) discoverDeviceAndRequestPermission() } private fun discoverDeviceAndRequestPermission() { val deviceList = usbManager.deviceList val device = deviceList.values.firstOrNull() if (device != null) { Log.d(TAG, "Device found: ${device.deviceName}, requesting permission...") requestPermission(device) } else { Log.e(TAG, "No USB devices connected.") } } private fun requestPermission(device: UsbDevice) { Log.d(TAG, "Requesting permission for device: ${device.deviceName}") val permissionIntent = PendingIntent.getBroadcast(context, 0, Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) usbManager.requestPermission(device, permissionIntent) Log.d(TAG, "Permission intent sent. Device: $device, PermissionIntent: $permissionIntent") } private fun connectToDevice(device: UsbDevice) { val connection = usbManager.openDevice(device) serialPort = UsbSerialDevice.createUsbSerialDevice(device, connection) if (serialPort != null && serialPort!!.open()) { Log.d(TAG, "Connection opened, configuring serial port...") configureSerialConnection() } else { Log.e(TAG, "Could not open the device connection or create serial device.") } } private fun configureSerialConnection() { serialPort?.apply { setBaudRate(9600) setDataBits(UsbSerialInterface.DATA_BITS_8) setStopBits(UsbSerialInterface.STOP_BITS_1) setParity(UsbSerialInterface.PARITY_NONE) setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF) read { data -> responseData = String(data) Log.d(TAG, "Received data: $responseData") responseLatch.countDown() } } Log.d(TAG, "Serial port configured.") } fun enviar(message: String): String { Log.d(TAG, "Sending message: $message") responseData = null // Resetear los datos antes de enviar responseLatch.await(0, TimeUnit.SECONDS) // Restablecer el latch serialPort?.write(message.toByteArray()) responseLatch.await(2, TimeUnit.SECONDS) // Esperar hasta 2 segundos para la respuesta return responseData ?: "No response received" } fun disconnect() { serialPort?.close() context.unregisterReceiver(usbPermissionReceiver) Log.d(TAG, "Disconnected and receiver unregistered.") } }
Manifest配置
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.example.testapp"> <uses-feature android:name="android.hardware.usb.host" /> <uses-feature android:name="android.hardware.camera" android:required="false" /> <uses-permission android:name="android.permission.USB_PERMISSION" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.USB_HOST" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.TestApp" tools:targetApi="31"> <activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name" android:theme="@style/Theme.TestApp" android:screenOrientation="landscape"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
异常Logcat日志
设备发现
2024-06-22 23:49:05.360 30020-30020 IOPesa com.example.testapp D Device found: /dev/bus/usb/001/002, requesting permission...
请求权限
2024-06-22 23:49:05.360 30020-30020 IOPesa com.example.testapp D Requesting permission for device: /dev/bus/usb/001/002
请求已发送
2024-06-22 23:49:05.377 30020-30020 IOPesa com.example.testapp D Permission intent sent. Device: UsbDevice[mName=/dev/bus/usb/001/002,mVendorId=6790,mProductId=29987,mClass=255,mSubclass=0,mProtocol=0,mManufacturerName=null,mProductName=USB Serial,mVersion=2.64,mSerialNumberReader=android.hardware.usb.IUsbSerialReader$Stub$Proxy@7e3e8bc, mHasAudioPlayback=false, mHasAudioCapture=false, mHasMidi=false, mHasVideoCapture=false, mHasVideoPlayback=false, mConfigurations=[ UsbConfiguration[mId=1,mName=null,mAttributes=128,mMaxPower=49,mInterfaces=[ UsbInterface[mId=0,mAlternateSetting=0,mName=null,mClass=255,mSubclass=1,mProtocol=2,mEndpoints=[ UsbEndpoint[mAddress=130,mAttributes=2,mMaxPacketSize=32,mInterval=0] UsbEndpoint[mAddress=2,mAttributes=2,mMaxPacketSize=32,mInterval=0] UsbEndpoint[mAddress=129,mAttributes=3,mMaxPacketSize=8,mInterval=1]]]], PermissionIntent: PendingIntent{a5b9045: android.os.BinderProxy@94fa39a}
权限被拒绝
无论是否弹出权限申请弹窗,点击同意后均收到该日志:
2024-06-22 23:49:07.736 30020-30020 IOPesa com.example.testapp E Permission denied for the device null
问题修复方案
1. 适配Android 14的Parcelable获取方式
日志中出现device null是因为API 33+推荐使用类型安全的Parcelable获取方法,修改广播接收器中的设备获取代码:
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE, UsbDevice::class.java)
2. 优化广播接收器与权限声明
在Manifest中添加自定义权限,防止其他应用恶意触发权限广播:
<permission android:name="com.example.testapp.USB_PERMISSION" android:protectionLevel="signature" />
同时修改广播接收器的注册逻辑,添加权限校验:
context.registerReceiver( usbPermissionReceiver, filter, "com.example.testapp.USB_PERMISSION", null, Context.RECEIVER_EXPORTED )
3. 调整权限申请时机
Android 14限制后台发起权限请求,将discoverDeviceAndRequestPermission()从init块移出,改为在用户交互事件(如按钮点击)中调用:
// 移除init块中的discoverDeviceAndRequestPermission()调用 // 在MainActivity中添加触发逻辑 buttonConnect.setOnClickListener { ioPesa.discoverDeviceAndRequestPermission() }
4. 添加USB设备过滤规则
在Manifest的MainActivity中添加设备连接的intent-filter与元数据:
<activity ...> ... <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" /> </activity>
在res/xml目录创建usb_device_filter.xml,填入你的Arduino设备ID:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="6790" product-id="29987" /> </resources>
5. 检查PendingIntent的兼容性
虽然你已使用FLAG_IMMUTABLE,但确保你的UsbSerial库适配Android 14,若仍存在问题,可尝试临时改用FLAG_MUTABLE(仅测试使用,正式版推荐保持FLAG_IMMUTABLE):
val permissionIntent = PendingIntent.getBroadcast( context, 0, Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE )
内容的提问来源于stack exchange,提问作者pointbraker
相关产品推荐
相关产品推荐

