Skip to content

Commit e019f50

Browse files
committed
macOS: check that device has not been disconnected
1 parent db81a6b commit e019f50

File tree

4 files changed

+55
-20
lines changed

4 files changed

+55
-20
lines changed

java-does-usb/src/main/java/net/codecrete/usb/UsbDevice.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,18 @@ public interface UsbDevice {
152152
*/
153153
void attachStandardDrivers();
154154

155+
/**
156+
* Indicates if the device is connected.
157+
* <p>
158+
* When a {@link UsbDevice} instance is initially returned by {@link Usb#getDevices()} and related methods,
159+
* it is connected. When the user unplugs the device, the application can still hold on to instance of
160+
* {@link UsbDevice} even though the actual USB device is gone. This method can be used to check if the
161+
* device is still connected.
162+
* </p>
163+
* @return {@code true} if the device is connected, {@code false} if it is no longer connected
164+
*/
165+
boolean isConnected();
166+
155167
/**
156168
* Opens the device for communication.
157169
*/

java-does-usb/src/main/java/net/codecrete/usb/common/UsbDeviceImpl.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public abstract class UsbDeviceImpl implements UsbDevice {
5555
protected int deviceProtocol;
5656
protected Version versionUsb;
5757
protected Version versionDevice;
58+
protected boolean connected;
5859

5960
/**
6061
* Creates a new instance.
@@ -70,6 +71,7 @@ protected UsbDeviceImpl(Object id, int vendorId, int productId) {
7071
uniqueDeviceId = id;
7172
vid = vendorId;
7273
pid = productId;
74+
connected = true;
7375
}
7476

7577
@Override
@@ -157,6 +159,11 @@ public Object getUniqueId() {
157159
return uniqueDeviceId;
158160
}
159161

162+
@Override
163+
public boolean isConnected() {
164+
return connected;
165+
}
166+
160167
/**
161168
* Sets the class codes and version for the device descriptor.
162169
*

java-does-usb/src/main/java/net/codecrete/usb/macos/MacosUsbDevice.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,16 @@ public class MacosUsbDevice extends UsbDeviceImpl {
7878
}
7979

8080
@Override
81-
public void detachStandardDrivers() {
82-
if (isOpened())
83-
throwException("detachStandardDrivers() must not be called while the device is open");
81+
public synchronized void detachStandardDrivers() {
82+
checkIsClosed("detachStandardDrivers() must not be called while the device is open");
8483
var ret = IoKitUsb.USBDeviceReEnumerate(device, IOKit.kUSBReEnumerateCaptureDeviceMask());
8584
if (ret != 0)
8685
throwException(ret, "detaching standard drivers failed");
8786
}
8887

8988
@Override
90-
public void attachStandardDrivers() {
91-
if (isOpened())
92-
throwException("attachStandardDrivers() must not be called while the device is open");
89+
public synchronized void attachStandardDrivers() {
90+
checkIsClosed("attachStandardDrivers() must not be called while the device is open");
9391
var ret = IoKitUsb.USBDeviceReEnumerate(device, IOKit.kUSBReEnumerateReleaseDeviceMask());
9492
if (ret != 0)
9593
throwException(ret, "attaching standard drivers failed");
@@ -100,11 +98,17 @@ public boolean isOpened() {
10098
return claimedInterfaces != null;
10199
}
102100

101+
private void checkIsClosed(String message) {
102+
if (!connected)
103+
throwException("device has been disconnected");
104+
if (isOpened())
105+
throwException(message);
106+
}
107+
103108
@SuppressWarnings("java:S2276")
104109
@Override
105110
public synchronized void open() {
106-
if (isOpened())
107-
throwException("device is already open");
111+
checkIsClosed("device is already open");
108112

109113
// open device (several retries if device has just been connected/discovered)
110114
var duration = System.currentTimeMillis() - discoveryTime;
@@ -161,6 +165,7 @@ public synchronized void close() {
161165
}
162166

163167
synchronized void closeFully() {
168+
connected = false;
164169
close();
165170
IoKitUsb.Release(device);
166171
device = null;
@@ -580,7 +585,7 @@ protected Transfer createTransfer() {
580585
}
581586

582587
@Override
583-
public void abortTransfers(UsbDirection direction, int endpointNumber) {
588+
public synchronized void abortTransfers(UsbDirection direction, int endpointNumber) {
584589
var epInfo = getEndpointInfo(endpointNumber, direction, UsbTransferType.BULK,
585590
UsbTransferType.INTERRUPT);
586591

@@ -590,7 +595,7 @@ public void abortTransfers(UsbDirection direction, int endpointNumber) {
590595
}
591596

592597
@Override
593-
public void clearHalt(UsbDirection direction, int endpointNumber) {
598+
public synchronized void clearHalt(UsbDirection direction, int endpointNumber) {
594599
var epInfo = getEndpointInfo(endpointNumber, direction, UsbTransferType.BULK,
595600
UsbTransferType.INTERRUPT);
596601

java-does-usb/src/test/java/net/codecrete/usb/special/Unplug.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,27 @@ private static void onUnpluggedDevice(UsbDevice device) {
5757
var worker = activeDevices.remove(device);
5858
worker.setDisconnectTime(System.currentTimeMillis());
5959
worker.join();
60+
61+
// test handling of disconnected devices
62+
new Thread(() -> {
63+
sleep(2000);
64+
try {
65+
device.open();
66+
System.err.println("Device should not be openable after disconnect");
67+
} catch (UsbException e) {
68+
// expected
69+
}
70+
}).start();
71+
}
72+
73+
@SuppressWarnings({"SameParameterValue", "java:S2925"})
74+
private static void sleep(long millis) {
75+
try {
76+
Thread.sleep(millis);
77+
78+
} catch (InterruptedException e) {
79+
Thread.currentThread().interrupt();
80+
}
6081
}
6182

6283
static class DeviceWorker {
@@ -241,16 +262,6 @@ private void receiveEcho() {
241262
logWork(1);
242263
}
243264
}
244-
245-
@SuppressWarnings({"SameParameterValue", "java:S2925"})
246-
private static void sleep(long millis) {
247-
try {
248-
Thread.sleep(millis);
249-
250-
} catch (InterruptedException e) {
251-
Thread.currentThread().interrupt();
252-
}
253-
}
254265
}
255266

256267
static class Work {

0 commit comments

Comments
 (0)