1
0
mirror of https://github.com/mik3y/usb-serial-for-android synced 2025-06-08 00:16:13 +00:00

improved error handling for read() with concurrent close() (#569)

- isOpen() returns false during concurrent close()
- less tracing in SerialInputOutputManager
This commit is contained in:
Kai Morich 2024-04-24 21:24:24 +02:00
parent 1245293888
commit 8b9ad7efdf
6 changed files with 26 additions and 15 deletions

View File

@ -13,7 +13,7 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments = [ // Raspi Windows LinuxVM ... testInstrumentationRunnerArguments = [ // Raspi Windows LinuxVM ...
'rfc2217_server_host': '192.168.0.147', 'rfc2217_server_host': '192.168.0.78',
'rfc2217_server_nonstandard_baudrates': 'true', // true false false 'rfc2217_server_nonstandard_baudrates': 'true', // true false false
] ]
} }

View File

@ -320,8 +320,10 @@ public class DeviceTest {
try { try {
closer.wait = false; closer.wait = false;
usb.serialPort.read(new byte[256], 2000); usb.serialPort.read(new byte[256], 2000);
fail("closed expected"); fail("closed expected");
} catch(IOException ex) { } catch(IOException ex) {
assertFalse(usb.serialPort.isOpen());
assertEquals("Connection closed", ex.getMessage()); assertEquals("Connection closed", ex.getMessage());
} }
th.join(); th.join();
@ -333,6 +335,7 @@ public class DeviceTest {
usb.serialPort.read(new byte[256], 0); usb.serialPort.read(new byte[256], 0);
fail("closed expected"); fail("closed expected");
} catch(IOException ex) { } catch(IOException ex) {
assertFalse(usb.serialPort.isOpen());
assertEquals("Connection closed", ex.getMessage()); assertEquals("Connection closed", ex.getMessage());
} }
th.join(); th.join();

View File

@ -139,6 +139,8 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
if (mConnection == null) { if (mConnection == null) {
throw new IOException("Already closed"); throw new IOException("Already closed");
} }
UsbDeviceConnection connection = mConnection;
mConnection = null;
try { try {
mUsbRequest.cancel(); mUsbRequest.cancel();
} catch(Exception ignored) {} } catch(Exception ignored) {}
@ -147,9 +149,8 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
closeInt(); closeInt();
} catch(Exception ignored) {} } catch(Exception ignored) {}
try { try {
mConnection.close(); connection.close();
} catch(Exception ignored) {} } catch(Exception ignored) {}
mConnection = null;
} }
protected abstract void closeInt(); protected abstract void closeInt();
@ -157,10 +158,13 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
/** /**
* use simple USB request supported by all devices to test if connection is still valid * use simple USB request supported by all devices to test if connection is still valid
*/ */
protected void testConnection() throws IOException { protected void testConnection(boolean full) throws IOException {
if(mConnection == null || mUsbRequest == null) { if(mConnection == null) {
throw new IOException("Connection closed"); throw new IOException("Connection closed");
} }
if(!full) {
return;
}
byte[] buf = new byte[2]; byte[] buf = new byte[2];
int len = mConnection.controlTransfer(0x80 /*DEVICE*/, 0 /*GET_STATUS*/, 0, 0, buf, buf.length, 200); int len = mConnection.controlTransfer(0x80 /*DEVICE*/, 0 /*GET_STATUS*/, 0, 0, buf, buf.length, 200);
if(len < 0) if(len < 0)
@ -169,7 +173,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
@Override @Override
public int read(final byte[] dest, final int timeout) throws IOException { public int read(final byte[] dest, final int timeout) throws IOException {
if(dest.length <= 0) { if(dest.length == 0) {
throw new IllegalArgumentException("Read buffer too small"); throw new IllegalArgumentException("Read buffer too small");
} }
return read(dest, dest.length, timeout); return read(dest, dest.length, timeout);
@ -179,7 +183,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
public int read(final byte[] dest, final int length, final int timeout) throws IOException {return read(dest, length, timeout, true);} public int read(final byte[] dest, final int length, final int timeout) throws IOException {return read(dest, length, timeout, true);}
protected int read(final byte[] dest, int length, final int timeout, boolean testConnection) throws IOException { protected int read(final byte[] dest, int length, final int timeout, boolean testConnection) throws IOException {
if(mConnection == null || mUsbRequest == null) { if(mConnection == null) {
throw new IOException("Connection closed"); throw new IOException("Connection closed");
} }
if(length <= 0) { if(length <= 0) {
@ -201,8 +205,8 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
nread = mConnection.bulkTransfer(mReadEndpoint, dest, readMax, timeout); nread = mConnection.bulkTransfer(mReadEndpoint, dest, readMax, timeout);
// Android error propagation is improvable: // Android error propagation is improvable:
// nread == -1 can be: timeout, connection lost, buffer to small, ??? // nread == -1 can be: timeout, connection lost, buffer to small, ???
if(nread == -1 && testConnection && MonotonicClock.millis() < endTime) if(nread == -1 && testConnection)
testConnection(); testConnection(MonotonicClock.millis() < endTime);
} else { } else {
final ByteBuffer buf = ByteBuffer.wrap(dest, 0, length); final ByteBuffer buf = ByteBuffer.wrap(dest, 0, length);
@ -217,7 +221,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
// Android error propagation is improvable: // Android error propagation is improvable:
// response != null & nread == 0 can be: connection lost, buffer to small, ??? // response != null & nread == 0 can be: connection lost, buffer to small, ???
if(nread == 0) { if(nread == 0) {
testConnection(); testConnection(true);
} }
} }
return Math.max(nread, 0); return Math.max(nread, 0);

View File

@ -165,8 +165,8 @@ public class FtdiSerialDriver implements UsbSerialDriver {
do { do {
nread = super.read(dest, length, Math.max(1, (int)(endTime - MonotonicClock.millis())), false); nread = super.read(dest, length, Math.max(1, (int)(endTime - MonotonicClock.millis())), false);
} while (nread == READ_HEADER_LENGTH && MonotonicClock.millis() < endTime); } while (nread == READ_HEADER_LENGTH && MonotonicClock.millis() < endTime);
if(nread <= 0 && MonotonicClock.millis() < endTime) if(nread <= 0)
testConnection(); testConnection(MonotonicClock.millis() < endTime);
} else { } else {
do { do {
nread = super.read(dest, length, timeout); nread = super.read(dest, length, timeout);

View File

@ -205,8 +205,8 @@ public class ProlificSerialDriver implements UsbSerialDriver {
byte[] buffer = new byte[STATUS_BUFFER_SIZE]; byte[] buffer = new byte[STATUS_BUFFER_SIZE];
long endTime = MonotonicClock.millis() + 500; long endTime = MonotonicClock.millis() + 500;
int readBytesCount = mConnection.bulkTransfer(mInterruptEndpoint, buffer, STATUS_BUFFER_SIZE, 500); int readBytesCount = mConnection.bulkTransfer(mInterruptEndpoint, buffer, STATUS_BUFFER_SIZE, 500);
if(readBytesCount == -1 && MonotonicClock.millis() < endTime) if(readBytesCount == -1)
testConnection(); testConnection(MonotonicClock.millis() < endTime);
if (readBytesCount > 0) { if (readBytesCount > 0) {
if (readBytesCount != STATUS_BUFFER_SIZE) { if (readBytesCount != STATUS_BUFFER_SIZE) {
throw new IOException("Invalid status notification, expected " + STATUS_BUFFER_SIZE + " bytes, got " + readBytesCount); throw new IOException("Invalid status notification, expected " + STATUS_BUFFER_SIZE + " bytes, got " + readBytesCount);

View File

@ -203,7 +203,11 @@ public class SerialInputOutputManager implements Runnable {
step(); step();
} }
} catch (Exception e) { } catch (Exception e) {
if(mSerialPort.isOpen()) {
Log.w(TAG, "Run ending due to exception: " + e.getMessage(), e); Log.w(TAG, "Run ending due to exception: " + e.getMessage(), e);
} else {
Log.i(TAG, "Socket closed");
}
final Listener listener = getListener(); final Listener listener = getListener();
if (listener != null) { if (listener != null) {
listener.onRunError(e); listener.onRunError(e);