简体   繁体   English

Android USB_DEVICE_ATTACHED 持久权限

[英]Android USB_DEVICE_ATTACHED persistent permission

How can I make Android not request for permission each time I reconnect a USB device?每次重新连接 USB 设备时,如何让 Android 不请求许可? I want to make it to remember "Use by default" checkmark for the USB devices so that I don't have to give permission every time to the same device.我想让它记住 USB 设备的“默认使用”复选标记,这样我就不必每次都向同一设备授予权限。

I programatically detect when USB devices (android phones) are attached to my host device (android phone) so that I can switch them to AOA mode and use them as accessories.我以编程方式检测 USB 设备(安卓手机)何时连接到我的主机设备(安卓手机),以便我可以将它们切换到 AOA 模式并将它们用作配件。 Basically I have two android phones and an OTG cable and I want them to communicate between eachother.基本上我有两部安卓手机和一条 OTG 电缆,我希望它们能够相互通信。

I have a thread which constantly enumerates the attached USB devices:我有一个线程不断枚举连接的 USB 设备:

UsbManager manager = (UsbManager) 
                   context.getSystemService(Context.USB_SERVICE);
while (!m_stopRequested) {
  boolean shouldNotify = false;
  HashMap<String, UsbDevice> deviceMap = m_usbManager.getDeviceList();
  for (Entry<String, UsbDevice> entry : deviceMap) {
    UsbDevice device = entry.getValue();
    if (m_usbManager.hasPermission(device)) {
      int pid = device.getProductId();
      if (device.getVendorId() == VID_GOOGLE(0x18D1) && (pid == ACCESSORY_PID(0x2D01) || pid == ACCESSORY_PID_ALT(0x2D00))) {
        switchDeviceToAOAMode(device);
      }
    } else {
      m_usbManager.requestPermission(device);
    }
  }
  Thread.sleep(1000);
}

I also have a BroadcastReceiver registered to receive USB_PERMISSION intents:我还注册了一个 BroadcastReceiver 来接收 USB_PERMISSION 意图:

private final class USBReceiver extends BroadcastReceiver {

    public void onReceive(Context context, Intent intent) {
        MCSLogger.log(TAG, "Received permission result!");

        String action = intent.getAction();
        UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

        if (ACTION_USB_PERMISSION.equals(action)) {
            boolean res = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
            MCSLogger.log(TAG, "permission action for dev=" + device + " received " + res);
            int pid = device.getProductId();
            if (res && device.getVendorId() == VID_GOOGLE(0x18D1) && (pid == ACCESSORY_PID(0x2D01) || pid == ACCESSORY_PID_ALT(0x2D00))) {
              connectAccessory()
            }
        }
    }
};

This is how I switch to AOA mode:这是我切换到 AOA 模式的方式:

  private boolean switchDeviceToAOAMode(UsbDeviceConnection connection) {
        byte ioBuffer[] = new byte[2];
        int devVersion;
        int response;
    enter code here
        response = connection.controlTransfer(0xC0, 51, 0, 0, ioBuffer, 2, 0);

        if (response < 0) {
            MCSLogger.log(TAG, "Error starting transfer control " + response);
            return false;
        }

        devVersion = ioBuffer[1] << 8 | ioBuffer[0];

        // sometimes hangs on the next transfer :( //WIN32 libusb only
        // SystemClock.sleep(1000);

        byte manufacturer[] = m_manufacturer.getBytes();
        response = connection.controlTransfer(0x40, 52, 0, 0, manufacturer, manufacturer.length, 0);
        if (response < 0) {
            MCSLogger.log(TAG, "Error transfering manufacturer " + response);
            return false;
        }
        byte modelName[] = m_modelName.getBytes();
        response = connection.controlTransfer(0x40, 52, 0, 1, modelName, modelName.length, 0);
        if (response < 0) {
            MCSLogger.log(TAG, "Error transfering modelName " + response);
            return false;
        }
        byte description[] = m_description.getBytes();
        response = connection.controlTransfer(0x40, 52, 0, 2, description, description.length, 0);
        if (response < 0) {
            MCSLogger.log(TAG, "Error transfering description " + response);
            return false;
        }
        byte version[] = m_version.getBytes();
        response = connection.controlTransfer(0x40, 52, 0, 3, version, version.length, 0);
        if (response < 0) {
            MCSLogger.log(TAG, "Error transfering version " + response);
            return false;
        }
        byte uri[] = m_uri.getBytes();
        response = connection.controlTransfer(0x40, 52, 0, 4, uri, uri.length, 0);
        if (response < 0) {
            MCSLogger.log(TAG, "Error transfering uri " + response);
            return false;
        }
        byte serialNumber[] = m_serialNumber.getBytes();
        response = connection.controlTransfer(0x40, 52, 0, 5, serialNumber, serialNumber.length, 0);
        if (response < 0) {
            MCSLogger.log(TAG, "Error transfering serialNumber " + response);
            return false;
        }

        MCSLogger.log(TAG, "Accessory Identification sent " + devVersion);

        response = connection.controlTransfer(0x40, 53, 0, 0, null, 0, 0);
        if (response < 0) {
            MCSLogger.log(TAG, "Error ending transfer control " + response);
            return false;
        }
        return true;
    }

The answer provided by @Ender is correct, but there is one more thing you need to do on later versions of the Android Platform (7+). @Ender 提供的答案是正确的,但在更高版本的 Android 平台 (7+) 上,您还需要做一件事。

You need to make sure that you have android:directBootAware="true" added to the activity tag that is responsible for responding to the USB_ACCESSORY_ATTACHED / USB_DEVICE_ATTACHED permissions.您需要确保将android:directBootAware="true"添加到负责响应 USB_ACCESSORY_ATTACHED / USB_DEVICE_ATTACHED 权限的活动标签中。

Here is a valid manifest section for the activity:以下是该活动的有效清单部分:

    <activity android:name=".MainActivity"
              android:directBootAware="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_DEVICE_ATTACHED"
                    android:resource="@xml/usb_device_filter" />
        </intent-filter>

    </activity>

Source:来源:

https://github.com/dazza5000/USBPermissionTest/blob/master/app/src/main/AndroidManifest.xml https://github.com/dazza5000/USBPermissionTest/blob/master/app/src/main/AndroidManifest.xml

usb_device_filter.xml usb_device_filter.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>
    <usb-device vendor-id="2049" product-id="25"/>
</resources>

Source:来源:

https://github.com/dazza5000/USBPermissionTest/blob/master/app/src/main/res/xml/usb_device_filter.xml https://github.com/dazza5000/USBPermissionTest/blob/master/app/src/main/res/xml/usb_device_filter.xml

The android:directBootAware="true" hint comes from the link below and I am very thankful for it. android:directBootAware="true"提示来自下面的链接,我非常感谢它。

https://www.sdgsystems.com/post/android-usb-permissions https://www.sdgsystems.com/post/android-usb-permissions

More details can be found here:可以在此处找到更多详细信息:

https://issuetracker.google.com/issues/77658221 https://issuetracker.google.com/issues/77658221

A full working project is here:一个完整的工作项目在这里:

https://github.com/dazza5000/USBPermissionTest https://github.com/dazza5000/USBPermissionTest

Root Access根访问

If you have root access, you can create the file and write it to disk and then reboot the device so that the default permission is read and set.如果您有 root 访问权限,您可以创建文件并将其写入磁盘,然后重新启动设备,以便读取和设置默认权限。

These are the basic steps:这些是基本步骤:

private void grantUSBPermission() { UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); private void grantUSBPermission() { UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);

HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();

for (UsbDevice usbDevice : deviceList.values()) {
    if (usbDevice.getManufacturerName() != null && usbDevice.getManufacturerName().equalsIgnoreCase(MANUFACTURER)) {
        Boolean hasPermission = usbManager.hasPermission(usbDevice);
        // Log if USB manager explicitly reports no permission.
        if (!hasPermission) {
            Log.i("DARRAN", "USB Manager reporting no permission to reader.");
            DeviceFilter deviceFilter = new DeviceFilter(usbDevice);
            writeSettingsFile(deviceFilter);
        }
    }
}

} }

private void writeSettingsFile(DeviceFilter deviceFilter) {
    PermissionUtil.writeSettingsLocked(getApplicationContext(), deviceFilter);
    RootUtil.executeAsRoot(COMMAND_COPY_USB_FILE);
    RootUtil.executeAsRoot(COMMAND_CHOWN_USB_FILE);
    RootUtil.executeAsRoot("reboot");
}

Commands:命令:

public static final String COMMAND_COPY_USB_FILE = "cp /sdcard/Android/data/com.whereisdarran.setusbdefault/files/usb_device_manager.xml /data/system/users/0/usb_device_manager.xml";
public static final String COMMAND_CHOWN_USB_FILE = "chown system:system /data/system/users/0/usb_device_manager.xml";

A full working project can be found here:一个完整的工作项目可以在这里找到:

https://github.com/dazza5000/set-usb-default https://github.com/dazza5000/set-usb-default

Also, a blog article with a little more context:此外,还有一篇博客文章有更多的上下文:

http://whereisdarran.com/2019/12/wip-how-to-programmatically-set-your-app-as-the-default-app-for-a-usb-device-on-android-root-required/ http://whereisdarran.com/2019/12/wip-how-to-programmatically-set-your-app-as-the-default-app-for-a-usb-device-on-android-root-required/

In implementing AOA, there are two main ways to obtain device permission for USB data transfers.在实现 AOA 时,主要有两种方式来获取 USB 数据传输的设备许可。

One approach involves manually enumerating all connected devices, finding the desired device, directly requesting permission via the UsbManager.requestPermission(Device device) method, and handling the resulting broadcast with a BroadcastReceiver.一种方法涉及手动枚举所有连接的设备,找到所需的设备,通过 UsbManager.requestPermission(Device device) 方法直接请求许可,并使用 BroadcastReceiver 处理结果广播。 This is the solution you've written.这是您编写的解决方案。 While functional and compliant, it prompts the user for permission every time a USB device is connected;在功能性和合规性的同时,它会在每次连接 USB 设备时提示用户进行许可; a potential source of annoyance for the user.用户的潜在烦恼来源。

The other approach is far simpler and allows for use-by-default functionality.另一种方法要简单得多,并且允许使用默认功能。 It requires that an intent filter be defined in AndroidManifest.xml like so:它要求在 AndroidManifest.xml 中定义一个意图过滤器,如下所示:

<activity ...>
...
<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" />

Along with an xml file named "accessory_filter"(just a suggestion, you can name it whatever you want).连同一个名为“accessory_filter”的 xml 文件(只是一个建议,您可以随意命名)。 Here's a sample accessory_filter.xml file:这是一个示例 Accessories_filter.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" /></resources>

The intent filter will automatically fire up the application in the event of a device connection and presents the user with the option to use your app as the default application for the specific device you are working with. Intent 过滤器将在设备连接时自动启动应用程序,并为用户提供使用您的应用程序作为您正在使用的特定设备的默认应用程序的选项。

This link provides more information: https://developer.android.com/guide/topics/connectivity/usb/accessory#manifest-example此链接提供了更多信息: https : //developer.android.com/guide/topics/connectivity/usb/accessory#manifest-example

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM