USB permission not automatically granted for filtered devices in Android manifest, ACTION_USB_PERMISSION not triggered

I’m developing an Android application that manages USB devices using an external library. The MainActivity instantiates this library, which handles USB device events (connect/disconnect) and permissions via a BroadcastReceiver. My goal is to automatically grant permission to devices specified in an XML filter file upon connection, without explicitly calling requestPermission.

Here’s what I’ve set up:

In AndroidManifest.xml, I added a <intent-filter> for USB_DEVICE_ATTACHED and specified a device_filter.xml file to match specific devices:

<uses-feature android:name="android.hardware.usb.host" />

<application ... >
    <activity android:name=".MainActivity" android:exported="true">
        <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>

My device_filter.xml contains vendor and product IDs for devices I want to handle:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <usb-device vendor-id="2965" product-id="6032" />
    <usb-device vendor-id="10374" product-id="32807" />
</resources>

In my UsbCdc class, which handles the USB logic, I initialize a BroadcastReceiver that listens for ACTION_USB_PERMISSION and USB_DEVICE_ATTACHED:

public UsbCdc(Context context) {
    mContext = context;
    mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);

    PendingIntent permissionIntent = PendingIntent.getBroadcast(mContext, 0,
            new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);
    IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
    filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
    filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
    ContextCompat.registerReceiver(mContext, mUsbReceiver, filter, ContextCompat.RECEIVER_EXPORTED);
}

When I connect devices that match device_filter.xml, hasPermission(device) still returns false. In mUsbReceiver, if a device lacks permissions, I call mUsbManager.requestPermission(device, permissionIntent). However, ACTION_USB_PERMISSION is not triggered afterward, even though I have manually granted permissions.

Is automatic permission granting for devices in device_filter.xml dependent on the Android version or specific device settings? Why might ACTION_USB_PERMISSION not be triggered after calling requestPermission in the BroadcastReceiver?

Automatic permission granting for USB devices specified in an XML filter file is generally not possible in Android due to the security restrictions on USB device access. Even with device filtering in place, the system still requires explicit user consent for each device connection. However, I can address a few factors that may influence why ACTION_USB_PERMISSION isn’t being triggered as expected after calling requestPermission:

1. Android Version Limitations

Android’s USB permission behavior can vary across versions. Starting with Android 10 (API level 29), the behavior around USB permissions became stricter. Generally:

  • Automatic permissions are not granted to USB devices that match device_filter.xml – user consent is required each time unless explicitly requested by the user in the device’s “USB devices” settings.
  • System prompts will occur for user consent even if devices match the filter file, as Android doesn’t permit automatic permission granting based on a metadata file.

2. App Permissions and Device Settings

Ensure that your application has been granted the necessary permissions and that the USB device is recognized by the Android OS:

  • Confirm device compatibility: Not all USB devices support automatic permissions in Android.
  • Grant “Always allow” (if possible): On some devices, you may be able to set a device to “Always allow” in the device’s USB settings. However, this setting is device-dependent and may not be available on all Android devices.

3. Ensuring Broadcast Receiver Registration and PendingIntent Setup

To ensure consistent permission handling, double-check the PendingIntent and BroadcastReceiver registration setup:

  • Confirm PendingIntent setup is correct. Starting from Android 12, it’s recommended to set a PendingIntent.FLAG_MUTABLE if your app targets Android 12 (API level 31) or above.
  • BroadcastReceiver registration: Register the BroadcastReceiver in your code, rather than in the manifest, using ContextCompat.registerReceiver, as you did, to ensure it is ready to handle any ACTION_USB_PERMISSION intents.
  • Double-check your receiver registration in UsbCdc to ensure it’s using the correct permissions and that the receiver stays active during the permission process.

4. Check USB Manager and System Behavior

  • Check the result of requestPermission: Even if requestPermission is called, ACTION_USB_PERMISSION might not trigger if the user has already granted permission manually via the system prompt. Ensure that the permission state changes as expected by calling hasPermission(device) after requesting permission to verify it’s set.
  • Debug device connection events: Use logs to check the sequence of events after plugging in the USB device. Verify whether USB_DEVICE_ATTACHED or ACTION_USB_PERMISSION triggers, as there could be unexpected handling differences due to Android version or device-specific quirks.

Example Adjusted Code with Logging and Compatibility Check

public UsbCdc(Context context) {
    mContext = context;
    mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);

    // Ensure FLAG_MUTABLE for Android 12+ if targeting Android 12+
    PendingIntent permissionIntent = PendingIntent.getBroadcast(
        mContext, 0, new Intent(ACTION_USB_PERMISSION),
        PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_MUTABLE
    );

    IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
    filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
    filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);

    ContextCompat.registerReceiver(mContext, mUsbReceiver, filter, ContextCompat.RECEIVER_EXPORTED);
}

// BroadcastReceiver that listens for permission and device connection events
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
        
        if (ACTION_USB_PERMISSION.equals(action)) {
            if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                Log.d("UsbCdc", "Permission granted for device " + device);
                // Handle permission granted logic
            } else {
                Log.d("UsbCdc", "Permission denied for device " + device);
            }
        } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
            if (device != null && !mUsbManager.hasPermission(device)) {
                Log.d("UsbCdc", "Requesting permission for device " + device);
                mUsbManager.requestPermission(device, permissionIntent);
            }
        }
    }
};

Important Notes

  1. Logging Sequence: Logging is essential for tracking if ACTION_USB_PERMISSION is triggered or not after calling requestPermission. If ACTION_USB_PERMISSION isn’t triggered at all, the issue could be related to the system’s handling of USB permissions rather than your code.
  2. Testing Across Devices: Since USB permission handling can vary across Android versions and device manufacturers, test on multiple devices or use an emulator if possible. Some devices require explicit user consent, regardless of permissions specified in the device_filter.xml file.
  3. Consider Root Permissions or ADB for Development Testing: For development, using adb commands or testing on rooted devices can sometimes help bypass restrictions temporarily, allowing you to verify if the logic works under ideal circumstances.

These adjustments should provide more consistent behavior, although fully automated USB permission granting is limited by Android’s security model, so user interaction may be necessary for permission approval in most cases.