@@ -20,6 +20,7 @@ import expo.modules.kotlin.Promise
20
20
import expo.modules.kotlin.exception.CodedException
21
21
import expo.modules.core.errors.ModuleDestroyedException
22
22
import java.nio.ByteBuffer
23
+ import java.util.concurrent.ConcurrentHashMap
23
24
24
25
// Convert Android USBDevice to JS compatible WebUSBDevice
25
26
typealias WebUSBDevice = Map <String , Any ?>
@@ -71,6 +72,10 @@ class ReactNativeUsbModule : Module() {
71
72
return @AsyncFunction openDevice(deviceName)
72
73
}
73
74
75
+ AsyncFunction (" reset" ) { deviceName: String ->
76
+ return @AsyncFunction resetDevice(deviceName)
77
+ }
78
+
74
79
AsyncFunction (" close" ) { deviceName: String ->
75
80
return @AsyncFunction closeDevice(deviceName)
76
81
}
@@ -156,7 +161,7 @@ class ReactNativeUsbModule : Module() {
156
161
for (device in devicesList) {
157
162
if (usbManager.hasPermission(device)) {
158
163
Log .d(LOG_TAG , " Has permission, send event onDeviceConnected: $device " )
159
-
164
+
160
165
val webUsbDevice = if (hasOpenedConnection(device.deviceName)) {
161
166
Log .d(LOG_TAG , " Device already opened: $device " )
162
167
getWebUSBDevice(device)
@@ -220,7 +225,8 @@ class ReactNativeUsbModule : Module() {
220
225
get() = context.getSystemService(Context .USB_SERVICE ) as UsbManager
221
226
222
227
223
- private val openedConnections = mutableMapOf<String , UsbDeviceConnection >()
228
+ private val openedConnections = ConcurrentHashMap <String , UsbDeviceConnection >()
229
+ private val pendingRequests = ConcurrentHashMap <String , UsbRequest >()
224
230
225
231
// We need to store device metadata because we can't access them in detached event
226
232
private val devicesHistory = mutableMapOf<String , WebUSBDevice >()
@@ -248,6 +254,12 @@ class ReactNativeUsbModule : Module() {
248
254
return getWebUSBDevice(device)
249
255
}
250
256
257
+ private fun resetDevice (deviceName : String ) {
258
+ Log .d(LOG_TAG , " Resetting device $deviceName " )
259
+ val usbRequest = pendingRequests.remove(deviceName)
260
+ usbRequest?.cancel()
261
+ }
262
+
251
263
private fun closeDevice (deviceName : String ) {
252
264
Log .d(LOG_TAG , " Closing device $deviceName " )
253
265
getOpenedConnection(deviceName).close()
@@ -256,12 +268,10 @@ class ReactNativeUsbModule : Module() {
256
268
257
269
private fun closeAllOpenedDevices () {
258
270
Log .d(LOG_TAG , " Closing all devices" )
259
- with (openedConnections.iterator()) {
260
- forEach {
261
- it.value.close()
262
- remove()
263
- }
264
- }
271
+ pendingRequests.values.forEach { it.cancel() }
272
+ pendingRequests.clear()
273
+ openedConnections.values.forEach { it.close() }
274
+ openedConnections.clear()
265
275
}
266
276
267
277
private fun selectConfiguration (deviceName : String , configurationIndex : Int ) {
@@ -352,7 +362,19 @@ class ReactNativeUsbModule : Module() {
352
362
req.initialize(usbConnection, usbEndpoint)
353
363
req.queue(buffer)
354
364
355
- val result = usbConnection.requestWait()
365
+ if (pendingRequests.putIfAbsent(device.deviceName, req) != null ) {
366
+ req.cancel()
367
+ req.close()
368
+ Log .e(LOG_TAG , " Transfer already in progress for device ${device.deviceName} " )
369
+ throw Exception (" Transfer already in progress for device ${device.deviceName} " )
370
+ }
371
+
372
+ val result = try {
373
+ usbConnection.requestWait()
374
+ } finally {
375
+ pendingRequests.remove(device.deviceName)
376
+ req.close()
377
+ }
356
378
357
379
if (result == null ) {
358
380
Log .e(LOG_TAG , " Failed to transfer data from device ${device.deviceName} " )
@@ -378,7 +400,6 @@ class ReactNativeUsbModule : Module() {
378
400
return isConnectionOpened
379
401
}
380
402
381
-
382
403
private fun getDeviceByName (deviceName : String ): UsbDevice {
383
404
Log .d(LOG_TAG , " getDeviceByName: $deviceName " )
384
405
val devices = usbManager.deviceList.values.toList()
0 commit comments