I have the following code:
public final static @NonNull String ACTION_USB_PERMISSION = "blah blah";
public final @NonNull Object permissionLock = new Object();
public final @NonNull AtomicBoolean didReceiverReceive = new AtomicBoolean(false);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
didReceiverReceive.set(true);
synchronized (permissionLock) {
permissionLock.notifyAll();
}
}
}, new IntentFilter(ACTION_USB_PERMISSION));
final @NonNull UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
final @NonNull UsbDevice usbDevice = usbManager.getDeviceList().values().stream().findFirst().get();
// For the sake of requesting permission, assume
// usbDevice != null; (and unwrapping the Optional<UsbDevice> was successful)
// usbManager.hasPermission(usbDevice) == false;
// We need to request permission!
synchronized (permissionLock) {
usbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE));
/* CASE 1: assume UsbManager::requestPermission is asynchronous
* THIS CAUSES DEADLOCK! THIS IS BAD
try {
permissionLock.wait();
} catch (InterruptedException ie) {
// this never happens btw
throw new RuntimeException(ie);
}
*/
/* CASE 2: assume UsbManager::requestPermission is synchronous
* THIS THROWS AN ASSERTION ERROR! THIS IS BAD
assert didReceiverReceive.get();
*/
}
// TODO how do I safely block execution of the Thread that calls onCreate until the USB-permission broadcast receiver receives the broadcast intent?
}
I tried two things (named CASE 1 and CASE 2), which are in the onCreate method in the location in which they were invoked.
I was expecting that UsbManager::requestPermission was either asynchronous or synchronous. Pretty reasonable, no? It turns out it is semi-synchronous for lack of a better word. What happens is that the UsbManager seems to schedule the broadcast/permission request to be executed at some time in the future, but not necessarily on a different thread. This thread that it uses to schedule the broadcast/permission request is the same that calls onCreate, regardless of whether or not I put usbManager.requestPermission(...) in a new Thread. Consequently, I simply cannot create a scenario in which the USB-permission broadcast receiver's onReceive method is invoked appropriately before the onCreate method terminates.