I’m trying to handle the event when a user presses “ok” or “cancel” on the automatic permission dialog presented when I connect a “known” USB device to the android phone.
I’m using the android.usb.host library and can send and receive between the android phone and the device. Futhermore do I handle the “USB_DEVICE_ATTACHED” and “USB_DEVICE_DETACHED” using a BroadcastReceiver without any problems.
I want to enable a sort of “autoconnect” feature and therefore I need to know when the user has pressed “ok” in the automatically displayed permission dialog, but I can’t find anything online at all. All I find is “bypass dialog”, but this is not what I want or need.
When I connect the usb device to the android phone, a permission dialog is automatically displayed because I use the “device_filter.xml” solution from androids documentation which can be seen here Android Usb Docs.
This is how I handle the USB_DEVICE_ATTATCHED and USB_DEVICE_DETACHED events:
public NativeUsbService(ReactApplicationContext reactContext) { ... // register device attached/detached event listeners IntentFilter filter = new IntentFilter(); filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); reactContext.registerReceiver(usbReceiver, filter); ... }
And then the Broadcast Receiver:
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) { synchronized (this) { UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if(device != null){ usbDevice = device; } else { Log.d(TAG, "onReceive: DEVICE WAS ATTACHED AND WAS NULL :("); } } } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) { Log.d(TAG, "onReceive: Device was detached!"); if(connection != null) { connection.releaseInterface(usbDeviceInterface); connection.close(); } connection = null; usbDevice = null; endpointIn = null; endpointOut = null; } } };
I have tried multiple different approaches, but nothing has worked.
I have tried getting the user response in from the intent, like with a manual permission request like below:
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) { synchronized (this) { UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if(device != null){ usbDevice = device; // THIS DOES NOT WORK ↓↓↓ if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { // The code never gets here... } } else { Log.d(TAG, "onReceive: DEVICE WAS ATTACHED AND WAS NULL :("); sendEvent("onDeviceAttached", false); } } } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) { if(connection != null) { connection.releaseInterface(usbDeviceInterface); connection.close(); } connection = null; usbDevice = null; endpointIn = null; endpointOut = null; } } };
I have also tried by adding a usb permission listener to the broadcast receiver by first adding the action name to my class variables:
private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
Then adding the action to my intent filter like so:
public NativeUsbService(ReactApplicationContext reactContext) { // register device attached/detached event listeners IntentFilter filter = new IntentFilter(); filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); filter.addAction(ACTION_USB_PERMISSION); // added action to my intent filter reactContext.registerReceiver(usbReceiver, filter); }
And finally reacting to the action like so:
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) { synchronized (this) { UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if(device != null){ usbDevice = device; } } } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) { Log.d(TAG, "onReceive: Device was detached!"); if(connection != null) { connection.releaseInterface(usbDeviceInterface); connection.close(); } connection = null; usbDevice = null; endpointIn = null; endpointOut = null; sendEvent("onDeviceDetached", true); } else if (action.equals(ACTION_USB_PERMISSION)) { Log.d(TAG, "onReceive: ACTION_USB_PERMISSION"); if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { Log.d(TAG, "onReceive: EXTRA_PERMISSION_GRANTED = true"); } else Log.d(TAG, "onReceive: EXTRA_PERMISSION_GRANTED = false"); } } };
Please make me aware of any missing information. Any help is greatly appreciated.
Advertisement
Answer
Answering my own question in case someone else is facing the same issue.
I though about manually requesting the permission again, after permission was granted, since it is possible to handle this manual permission request when user presses an option in the dialog. I discarded this idea, not because it wouldn’t work, but because I saw it as unecessary for the user to also have to press another dialog after the initial (automatic dialog).
I must add that I have not implemented this solution, so I do not know with certainty that it would prompt the user again, but I have had trouble with the manual permission request previously. If you want to try this approach the method belongs to the UsbManager class and is invoke like so usbManger.requestPermission(usbDevice)
.
I ended up with a solution where I start a thread which runs a loop calling usbManager.hasPermission(usbDevice)
until it has permission and then emits an event (emitting this event is my use case, implement it how you like).
The solution can be seen here:
import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; import com.facebook.react.bridge.ReactApplicationContext; ... private static volatile boolean permissionThreadShouldStop = false; private static Thread activePermissionThread = null; ... public static void usbPermissionEventEmitter(ReactApplicationContext reactContext, UsbManager usbManager, UsbDevice usbDevice) { if((activePermissionThread != null && activePermissionThread.isAlive())) { activePermissionThread.interrupt(); } permissionThreadShouldStop = false; activePermissionThread = new Thread(new Runnable() { @Override public void run() { while (!usbManager.hasPermission(usbDevice) && !permissionThreadShouldStop) { try { Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); break; } } if(usbManager.hasPermission(usbDevice)) { sendEvent(reactContext, "onUsbPermissionGranted", true); } } }); activePermissionThread.start(); }
The ReactApplicationContext
can be swapped with the normal android context. But this is for a react native module, so I use the reactContext.
I hope this will be helpful for someone, because i’m honestly really surpriced how scarse the android documentation is in regards to implementing Usb functionality using the android.hardware.usb library. Also in general when searching for information online, I have often found myself lost since there is very little information on this subject.