无法获取请求 UVC 设备权限的对话框
Fail to get dialog box to request permission for UVC device
我正在尝试开发一个应用程序来连接 phone 与特定的 UVC 相机。
当我用 USB 数据线插入设备并能够枚举不同的接口时,我成功检测到该设备。
我的问题是,当我尝试请求访问此设备的权限以打开相机界面时,没有对话框出现在用户面前,我的接收器的 onReceive 回调被立即调用。
这是我添加 USB 和相机权限的清单:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.experiment"
android:versionCode="1"
android:versionName="alpha">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USB_PERMISSION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-feature android:name="android.hardware.usb.host" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Experiment">
<activity android:name=".MainActivity">
<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_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
</application>
</manifest>
这是我的 MainActivity.kt :
package com.example.experiment
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.usb.*
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"
private const val USB_SUBCLASS_CONTROL = 1
private const val USB_SUBCLASS_STREAMING = 2
private const val TAG = "EXPERIMENT"
class MainActivity : AppCompatActivity() {
private lateinit var usbManager: UsbManager
private lateinit var textLabel: TextView
private lateinit var permissionIntent: PendingIntent
private lateinit var device: UsbDevice
private lateinit var devIf: UsbInterface
private lateinit var conn: UsbDeviceConnection
private lateinit var bytes: ByteArray
private var open = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "**************************************************************")
usbManager = getSystemService(Context.USB_SERVICE) as UsbManager
permissionIntent = PendingIntent.getBroadcast(this, 0, Intent(ACTION_USB_PERMISSION), 0)
val filter = IntentFilter(ACTION_USB_PERMISSION)
registerReceiver(usbReceiver, filter)
setContentView(R.layout.activity_main)
textLabel = findViewById<TextView>(R.id.textlabel)
val myButton: Button = findViewById(R.id.button_to_press)
myButton.setOnClickListener {
detectUSB()
}
}
private val usbReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d(TAG,"onReceive called")
if (ACTION_USB_PERMISSION == intent.action) {
Log.d(TAG, "USB permission action")
synchronized(this) {
device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
Log.d(TAG, "got device")
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
device.apply {
//call method to set up device communication
Log.d(TAG, "trying to open device")
conn = usbManager.openDevice(device)
open = true
}
} else {
Log.d(TAG, "permission denied for device $device")
}
}
}
}
}
private fun detectUSB() {
val deviceList: HashMap<String, UsbDevice> = usbManager.deviceList
val deviceIterator: Iterator<UsbDevice> = deviceList.values.iterator()
var i = ""
while (deviceIterator.hasNext()) {
val device = deviceIterator.next()
if (device.vendorId == 0xABCD && device.productId == 0x0123) {
if (!usbManager.hasPermission(device)) {
Log.d(TAG, "request sent")
usbManager.requestPermission(device, permissionIntent)
} else {
Log.d(TAG, "got permission to use device")
open = true
}
if (!open) {
i += "waiting for device permission"
} else {
if (!conn.releaseInterface(devIf))
{
i += """releaseInterface failed""".trimIndent()
}
conn.close()
i += """
device closed""".trimIndent()
open = false
}
}
i += """
DeviceID: ${device.deviceId}
DeviceName: ${device.deviceName}
DeviceClass: ${device.deviceClass} - ${translateDeviceClass(device.deviceClass)}
DeviceSubClass: ${device.deviceSubclass}
VendorID: ${device.vendorId}
ProductID: ${device.productId}
InterfaceCount: ${device.interfaceCount}
"""
}
textLabel.text = i
}
private fun translateDeviceClass(deviceClass: Int): String {
return when (deviceClass) {
UsbConstants.USB_CLASS_APP_SPEC -> "Application specific USB class"
UsbConstants.USB_CLASS_AUDIO -> "USB class for audio devices"
UsbConstants.USB_CLASS_CDC_DATA -> "USB class for CDC devices (communications device class)"
UsbConstants.USB_CLASS_COMM -> "USB class for communication devices"
UsbConstants.USB_CLASS_CONTENT_SEC -> "USB class for content security devices"
UsbConstants.USB_CLASS_CSCID -> "USB class for content smart card devices"
UsbConstants.USB_CLASS_HID -> "USB class for human interface devices (for example, mice and keyboards)"
UsbConstants.USB_CLASS_HUB -> "USB class for USB hubs"
UsbConstants.USB_CLASS_MASS_STORAGE -> "USB class for mass storage devices"
UsbConstants.USB_CLASS_MISC -> "USB class for wireless miscellaneous devices"
UsbConstants.USB_CLASS_PER_INTERFACE -> "USB class indicating that the class is determined on a per-interface basis"
UsbConstants.USB_CLASS_PHYSICA -> "USB class for physical devices"
UsbConstants.USB_CLASS_PRINTER -> "USB class for printers"
UsbConstants.USB_CLASS_STILL_IMAGE -> "USB class for still image devices (digital cameras)"
UsbConstants.USB_CLASS_VENDOR_SPEC -> "Vendor specific USB class"
UsbConstants.USB_CLASS_VIDEO -> "USB class for video devices"
UsbConstants.USB_CLASS_WIRELESS_CONTROLLER -> "USB class for wireless controller devices"
else -> "Unknown USB class!"
}
}
}
我在 https://developer.android.com/reference/android/hardware/usb/UsbManager#requestPermission(android.hardware.usb.UsbDevice,%20android.app.PendingIntent) 上读到,对于 UVC 设备,我需要获得我添加的摄像头许可,但我仍然没有获得许可对话框并且无法打开我的设备。我在这里错过了什么?我怎样才能获得访问我的设备的权限?
如果我对任何其他未标记为 UVC 的 USB 设备使用相同的代码,我每次都会收到对话框。
感谢您的帮助!
我找到了问题的原因:如here、Android 10 在开发过程中破坏了 UVC 支持,无法访问此类设备。这已在 Pixels phone 上修复,但在大多数设备上未修复,例如我的设备(华为 P20 pro)。
要解决此问题,需要将目标 sdk 版本更改为版本 27。
在 Android Studio 中,这可以通过打开项目的模块设置并更改默认配置选项卡中的 目标 SDK 版本 来完成。您不需要更改 编译 Sdk 版本。请注意,选择此类 SDK 版本将阻止您将应用发布到 android 商店。
我正在尝试开发一个应用程序来连接 phone 与特定的 UVC 相机。 当我用 USB 数据线插入设备并能够枚举不同的接口时,我成功检测到该设备。 我的问题是,当我尝试请求访问此设备的权限以打开相机界面时,没有对话框出现在用户面前,我的接收器的 onReceive 回调被立即调用。
这是我添加 USB 和相机权限的清单:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.experiment"
android:versionCode="1"
android:versionName="alpha">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USB_PERMISSION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-feature android:name="android.hardware.usb.host" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Experiment">
<activity android:name=".MainActivity">
<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_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
</application>
</manifest>
这是我的 MainActivity.kt :
package com.example.experiment
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.usb.*
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"
private const val USB_SUBCLASS_CONTROL = 1
private const val USB_SUBCLASS_STREAMING = 2
private const val TAG = "EXPERIMENT"
class MainActivity : AppCompatActivity() {
private lateinit var usbManager: UsbManager
private lateinit var textLabel: TextView
private lateinit var permissionIntent: PendingIntent
private lateinit var device: UsbDevice
private lateinit var devIf: UsbInterface
private lateinit var conn: UsbDeviceConnection
private lateinit var bytes: ByteArray
private var open = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "**************************************************************")
usbManager = getSystemService(Context.USB_SERVICE) as UsbManager
permissionIntent = PendingIntent.getBroadcast(this, 0, Intent(ACTION_USB_PERMISSION), 0)
val filter = IntentFilter(ACTION_USB_PERMISSION)
registerReceiver(usbReceiver, filter)
setContentView(R.layout.activity_main)
textLabel = findViewById<TextView>(R.id.textlabel)
val myButton: Button = findViewById(R.id.button_to_press)
myButton.setOnClickListener {
detectUSB()
}
}
private val usbReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d(TAG,"onReceive called")
if (ACTION_USB_PERMISSION == intent.action) {
Log.d(TAG, "USB permission action")
synchronized(this) {
device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
Log.d(TAG, "got device")
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
device.apply {
//call method to set up device communication
Log.d(TAG, "trying to open device")
conn = usbManager.openDevice(device)
open = true
}
} else {
Log.d(TAG, "permission denied for device $device")
}
}
}
}
}
private fun detectUSB() {
val deviceList: HashMap<String, UsbDevice> = usbManager.deviceList
val deviceIterator: Iterator<UsbDevice> = deviceList.values.iterator()
var i = ""
while (deviceIterator.hasNext()) {
val device = deviceIterator.next()
if (device.vendorId == 0xABCD && device.productId == 0x0123) {
if (!usbManager.hasPermission(device)) {
Log.d(TAG, "request sent")
usbManager.requestPermission(device, permissionIntent)
} else {
Log.d(TAG, "got permission to use device")
open = true
}
if (!open) {
i += "waiting for device permission"
} else {
if (!conn.releaseInterface(devIf))
{
i += """releaseInterface failed""".trimIndent()
}
conn.close()
i += """
device closed""".trimIndent()
open = false
}
}
i += """
DeviceID: ${device.deviceId}
DeviceName: ${device.deviceName}
DeviceClass: ${device.deviceClass} - ${translateDeviceClass(device.deviceClass)}
DeviceSubClass: ${device.deviceSubclass}
VendorID: ${device.vendorId}
ProductID: ${device.productId}
InterfaceCount: ${device.interfaceCount}
"""
}
textLabel.text = i
}
private fun translateDeviceClass(deviceClass: Int): String {
return when (deviceClass) {
UsbConstants.USB_CLASS_APP_SPEC -> "Application specific USB class"
UsbConstants.USB_CLASS_AUDIO -> "USB class for audio devices"
UsbConstants.USB_CLASS_CDC_DATA -> "USB class for CDC devices (communications device class)"
UsbConstants.USB_CLASS_COMM -> "USB class for communication devices"
UsbConstants.USB_CLASS_CONTENT_SEC -> "USB class for content security devices"
UsbConstants.USB_CLASS_CSCID -> "USB class for content smart card devices"
UsbConstants.USB_CLASS_HID -> "USB class for human interface devices (for example, mice and keyboards)"
UsbConstants.USB_CLASS_HUB -> "USB class for USB hubs"
UsbConstants.USB_CLASS_MASS_STORAGE -> "USB class for mass storage devices"
UsbConstants.USB_CLASS_MISC -> "USB class for wireless miscellaneous devices"
UsbConstants.USB_CLASS_PER_INTERFACE -> "USB class indicating that the class is determined on a per-interface basis"
UsbConstants.USB_CLASS_PHYSICA -> "USB class for physical devices"
UsbConstants.USB_CLASS_PRINTER -> "USB class for printers"
UsbConstants.USB_CLASS_STILL_IMAGE -> "USB class for still image devices (digital cameras)"
UsbConstants.USB_CLASS_VENDOR_SPEC -> "Vendor specific USB class"
UsbConstants.USB_CLASS_VIDEO -> "USB class for video devices"
UsbConstants.USB_CLASS_WIRELESS_CONTROLLER -> "USB class for wireless controller devices"
else -> "Unknown USB class!"
}
}
}
我在 https://developer.android.com/reference/android/hardware/usb/UsbManager#requestPermission(android.hardware.usb.UsbDevice,%20android.app.PendingIntent) 上读到,对于 UVC 设备,我需要获得我添加的摄像头许可,但我仍然没有获得许可对话框并且无法打开我的设备。我在这里错过了什么?我怎样才能获得访问我的设备的权限? 如果我对任何其他未标记为 UVC 的 USB 设备使用相同的代码,我每次都会收到对话框。
感谢您的帮助!
我找到了问题的原因:如here、Android 10 在开发过程中破坏了 UVC 支持,无法访问此类设备。这已在 Pixels phone 上修复,但在大多数设备上未修复,例如我的设备(华为 P20 pro)。
要解决此问题,需要将目标 sdk 版本更改为版本 27。 在 Android Studio 中,这可以通过打开项目的模块设置并更改默认配置选项卡中的 目标 SDK 版本 来完成。您不需要更改 编译 Sdk 版本。请注意,选择此类 SDK 版本将阻止您将应用发布到 android 商店。