diff --git a/omriusb/src/main/java/org/omri/radio/impl/UsbHelper.java b/omriusb/src/main/java/org/omri/radio/impl/UsbHelper.java index ec24cc4..1625996 100644 --- a/omriusb/src/main/java/org/omri/radio/impl/UsbHelper.java +++ b/omriusb/src/main/java/org/omri/radio/impl/UsbHelper.java @@ -83,13 +83,29 @@ private UsbHelper(Context context) { if(mContext != null) { mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE); - mUsbPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0); + // Android 12 (S, API 31) requires PendingIntent flags to explicitly declare mutability. + // UsbManager.requestPermission() needs to populate EXTRA_DEVICE and + // EXTRA_PERMISSION_GRANTED on the intent before broadcasting it back to us, which + // requires FLAG_MUTABLE. (FLAG_IMMUTABLE would deliver an empty broadcast and the + // permission flow would silently fail.) + int piFlags = PendingIntent.FLAG_UPDATE_CURRENT; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + piFlags |= PendingIntent.FLAG_MUTABLE; + } + mUsbPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), piFlags); IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); - mContext.registerReceiver(mUsbBroadcastReceiver, filter); + // Android 13 (TIRAMISU, API 33) requires runtime-registered receivers to specify + // export-ness. ACTION_USB_PERMISSION is delivered by the system to our own process, + // so RECEIVER_NOT_EXPORTED is correct. + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) { + mContext.registerReceiver(mUsbBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED); + } else { + mContext.registerReceiver(mUsbBroadcastReceiver, filter); + } created(); } } @@ -228,6 +244,20 @@ void removeDevice(UsbDevice remDev) { public void onReceive(Context context, Intent intent) { UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); String action = intent.getAction(); + if (action == null) { + if (DEBUG) Log.w(TAG, "Received broadcast with null action; ignoring"); + return; + } + if (device == null) { + // Some Android builds deliver a USB_PERMISSION broadcast with EXTRA_DEVICE missing + // (e.g. when the system retracts a pending request). Without a device handle there + // is nothing meaningful to do; clearing pending state avoids getting stuck. + if (DEBUG) Log.w(TAG, "Received " + action + " with null EXTRA_DEVICE; ignoring"); + if (ACTION_USB_PERMISSION.equals(action)) { + mPermissionPending = false; + } + return; + } synchronized (this) { if (ACTION_USB_PERMISSION.equals(action)) { if(DEBUG)Log.d(TAG, "Received Permission request: " + action);