Android USB_DEVICE_ATTACHED persistent permission

In implementing AOA, there are two main ways to obtain device permission for USB data transfers.

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. This is the solution you've written. While functional and compliant, it prompts the user for permission every time a USB device is connected; 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:

<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). Here's a sample accessory_filter.xml file:

<?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.

This link provides more information: https://developer.android.com/guide/topics/connectivity/usb/accessory#manifest-example


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+).

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.

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

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

The android:directBootAware="true" hint comes from the link below and I am very thankful for it.

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

More details can be found here:

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

A full working project is here:

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.

These are the basic steps:

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

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/