Skip to content

Commit 68a40dc

Browse files
committed
Update USBPermissionManager.kt
1 parent 961015e commit 68a40dc

File tree

1 file changed

+30
-153
lines changed

1 file changed

+30
-153
lines changed

Transmission/src/main/java/org/operatorfoundation/transmission/USBPermissionManager.kt

Lines changed: 30 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -78,186 +78,63 @@ class USBPermissionManager(private val activityContext: Context)
7878

7979
/**
8080
* Requests permission for the specified USB device.
81-
* Returns a flow the emits the permission result.
81+
* Returns a flow that emits the permission result once and closes.
8282
*
8383
* @param device The USB device to request permission for.
84-
* @return Flow<PermissionResult> The permission request outcome.
84+
* @return Flow<PermissionResult> emitting a single result.
8585
*/
8686
fun requestPermissionFor(device: UsbDevice): Flow<PermissionResult> = callbackFlow {
8787
val deviceKey = getDeviceKey(device)
8888

89-
// Check if permission already exists.
89+
// Short-circuit if permission is already granted.
9090
if (hasPermission(device))
9191
{
9292
trySend(PermissionResult.Granted)
9393
close()
9494
return@callbackFlow
9595
}
9696

97-
// Create a broadcast receiver to handle the permission response.
9897
val permissionReceiver = object : BroadcastReceiver()
9998
{
10099
override fun onReceive(context: Context, intent: Intent)
101100
{
102-
Timber.d("=== BROADCAST RECEIVED ===")
103-
Timber.d("Action: ${intent.action}")
104-
Timber.d("Expected: $ACTION_USB_PERMISSION")
105-
Timber.d("Context: ${context.javaClass.simpleName}")
106-
Timber.d("Thread: ${Thread.currentThread().name}")
101+
if (intent.action != ACTION_USB_PERMISSION) return
107102

108-
if (ACTION_USB_PERMISSION == intent.action)
109-
{
110-
Timber.d("USB permission broadcast received!")
111-
112-
// Check what Android actually sends
113-
val androidDevice = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
114-
{
115-
intent.getParcelableExtra(UsbManager.EXTRA_DEVICE, UsbDevice::class.java)
116-
}
117-
else
118-
{
119-
@Suppress("DEPRECATION")
120-
intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
121-
}
122-
123-
Timber.d("Android provided device: ${androidDevice?.deviceName}")
124-
Timber.d("Android device key: ${androidDevice?.let { getDeviceKey(it) }}")
125-
Timber.d("Expected device key: $deviceKey")
126-
Timber.d("Permission granted flag: ${intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)}")
127-
128-
// Debug: Log all extras in the intent
129-
val extras = intent.extras
130-
if (extras != null)
131-
{
132-
for (key in extras.keySet())
133-
{
134-
Timber.d("Intent extra: $key = ${extras.get(key)}")
135-
}
136-
}
137-
else
138-
{
139-
Timber.d("No extras in intent")
140-
}
141-
142-
val receivedDevice = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
143-
{
144-
intent.getParcelableExtra(UsbManager.EXTRA_DEVICE, UsbDevice::class.java)
145-
}
146-
else
147-
{
148-
@Suppress("DEPRECATION")
149-
intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
150-
}
151-
152-
// Verify the response is for our device.
153-
if (receivedDevice != null && getDeviceKey(receivedDevice) == deviceKey)
154-
{
155-
val broadcastGrantedFlag = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
156-
Timber.d("Broadcast granted flag: $broadcastGrantedFlag")
157-
158-
// CRITICAL: Don't trust the broadcast flag - check actual permission state
159-
val actuallyHasPermission = usbManager.hasPermission(receivedDevice)
160-
Timber.d("Actual permission state from UsbManager: $actuallyHasPermission")
161-
162-
permissionCache[deviceKey] = actuallyHasPermission
163-
164-
val result = if (actuallyHasPermission)
165-
{
166-
Timber.d("🟢 Permission GRANTED - confirmed by UsbManager")
167-
PermissionResult.Granted
168-
}
169-
else
170-
{
171-
Timber.d("🔴 Permission DENIED - confirmed by UsbManager")
172-
PermissionResult.Denied
173-
}
174-
175-
trySend(result)
176-
close()
177-
}
178-
else
179-
{
180-
if (receivedDevice != null) {
181-
Timber.d(
182-
"Received permission for an unexpected device: ${
183-
getDeviceKey(
184-
receivedDevice
185-
)
186-
}"
187-
)
188-
}
189-
else
190-
{
191-
Timber.d("Received permission but the device is null.")
192-
}
193-
194-
trySend(PermissionResult.Error("Device mismatch in permission response"))
195-
close()
196-
}
197-
}
198-
else
199-
{
200-
Timber.d("Ignoring broadcast with different action")
201-
}
202-
}
203-
}
204-
205-
// Register receiver and request permission
206-
try
207-
{
208-
kotlinx.coroutines.MainScope().launch {
209-
val intentFilter = IntentFilter(ACTION_USB_PERMISSION)
210-
211-
Timber.d("=== REGISTERING RECEIVER ===")
212-
Timber.d("Context: ${activityContext.javaClass.simpleName}")
213-
Timber.d("Filter: ${intentFilter.actionsIterator().asSequence().toList()}")
103+
val receivedDevice =
104+
intent.getParcelableExtra(UsbManager.EXTRA_DEVICE, UsbDevice::class.java)
214105

215-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
106+
// Ignore broadcasts for other devices.
107+
if (receivedDevice == null || getDeviceKey(receivedDevice) != deviceKey)
216108
{
217-
activityContext.registerReceiver(
218-
permissionReceiver,
219-
intentFilter,
220-
Context.RECEIVER_NOT_EXPORTED
221-
)
222-
}
223-
else
224-
{
225-
activityContext.registerReceiver(permissionReceiver, intentFilter)
109+
Timber.w("Permission broadcast for unexpected device: ${receivedDevice?.deviceName}")
110+
return
226111
}
227112

228-
Timber.d("Receiver registered successfully")
229-
Timber.d("IntentFilter actions: ${intentFilter.actionsIterator().asSequence().toList()}")
230-
Timber.d("Current thread: ${Thread.currentThread().name}")
231-
Timber.d("Context: ${activityContext.javaClass.simpleName}")
232-
233-
// Create intent with the device as an extra so it comes back in the broadcast
234-
val permissionIntent = Intent(ACTION_USB_PERMISSION).apply {
235-
putExtra(UsbManager.EXTRA_DEVICE, device)
236-
}
113+
// EXTRA_PERMISSION_GRANTED is unreliable on some Android versions; query directly.
114+
val granted = usbManager.hasPermission(receivedDevice)
115+
Timber.d("Permission result for ${device.deviceName}: granted=$granted")
116+
permissionCache[deviceKey] = granted
237117

238-
val pendingIntent = PendingIntent.getBroadcast(
239-
activityContext,
240-
device.deviceId,
241-
permissionIntent,
242-
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
243-
)
118+
trySend(if (granted) PermissionResult.Granted else PermissionResult.Denied)
119+
close()
120+
}
121+
}
244122

245-
usbManager.requestPermission(device, pendingIntent)
123+
// Register before calling requestPermission() so we don't miss it
124+
val intentFilter = IntentFilter(ACTION_USB_PERMISSION)
125+
activityContext.registerReceiver(permissionReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
126+
Timber.d("Permission receiver registered for ${device.deviceName}")
246127

247-
Timber.d("Permission request sent for device: ${device.deviceName}")
248-
Timber.d("Using action: $ACTION_USB_PERMISSION")
249-
Timber.d("Device key: $deviceKey")
250-
}
128+
val pendingIntent = PendingIntent.getBroadcast(
129+
activityContext,
130+
device.deviceId,
131+
Intent(ACTION_USB_PERMISSION),
132+
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
133+
)
251134

252-
}
253-
catch (error: Exception)
254-
{
255-
Timber.e(error, "Failed to register receiver or request permission")
256-
trySend(PermissionResult.Error("Failed to request permission: ${error.message}"))
257-
close()
258-
}
135+
usbManager.requestPermission(device, pendingIntent)
136+
Timber.d("Permission request sent for ${device.deviceName}")
259137

260-
// Cleanup when flow is cancelled.
261138
awaitClose {
262139
try
263140
{

0 commit comments

Comments
 (0)