无法获取请求 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 商店。