Skip to content

Commit 9911e14

Browse files
authored
01. Refactored SerialInputOutputManager (#615)
Used separate threads for reading and writing, enhancing concurrency and performance. Note: before was possible to start `SerialInputOutputManager` with `Executors.newSingleThreadExecutor().submit(ioManager)`. Now you have to use `ioManager.start()`
1 parent 2673407 commit 9911e14

File tree

5 files changed

+151
-152
lines changed

5 files changed

+151
-152
lines changed

usbSerialForAndroid/build.gradle

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ dependencies {
3434
implementation "androidx.annotation:annotation:1.8.0"
3535
testImplementation 'junit:junit:4.13.2'
3636
testImplementation 'org.mockito:mockito-core:5.12.0'
37+
androidTestImplementation 'androidx.appcompat:appcompat:1.6.1'
3738
androidTestImplementation 'androidx.test:core:1.5.0'
3839
androidTestImplementation 'androidx.test:runner:1.5.2'
39-
androidTestImplementation 'commons-net:commons-net:3.10.0'
40+
androidTestImplementation 'commons-net:commons-net:3.9.0' // later version fails on old Android devices with missing java.time.Duration class
4041
androidTestImplementation 'org.apache.commons:commons-lang3:3.14.0'
4142
}
4243

usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java

+11-68
Original file line numberDiff line numberDiff line change
@@ -1425,19 +1425,16 @@ public void IoManager() throws Exception {
14251425
usb.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
14261426
telnet.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
14271427
usb.ioManager.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
1428+
assertEquals(SerialInputOutputManager.State.STOPPED, usb.ioManager.getState());
14281429
usb.ioManager.start();
14291430
usb.waitForIoManagerStarted();
1430-
assertTrue("iomanager thread", usb.hasIoManagerThread());
1431+
assertEquals(SerialInputOutputManager.State.RUNNING, usb.ioManager.getState());
1432+
assertTrue("iomanager thread", usb.hasIoManagerThreads());
14311433
try {
14321434
usb.ioManager.start();
14331435
fail("already running error expected");
14341436
} catch (IllegalStateException ignored) {
14351437
}
1436-
try {
1437-
usb.ioManager.run();
1438-
fail("already running error expected");
1439-
} catch (IllegalStateException ignored) {
1440-
}
14411438
try {
14421439
usb.ioManager.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
14431440
fail("setThreadPriority IllegalStateException expected");
@@ -1462,23 +1459,12 @@ public void IoManager() throws Exception {
14621459
telnet.write(new byte[1]); // now uses 8 byte buffer
14631460
usb.read(3);
14641461

1465-
// writebuffer resize
1462+
// small writebuffer
14661463
try {
14671464
usb.ioManager.writeAsync(new byte[8192]);
14681465
fail("expected BufferOverflowException");
14691466
} catch (BufferOverflowException ignored) {}
14701467

1471-
usb.ioManager.setWriteBufferSize(16);
1472-
usb.ioManager.writeAsync("1234567890AB".getBytes());
1473-
try {
1474-
usb.ioManager.setWriteBufferSize(8);
1475-
fail("expected BufferOverflowException");
1476-
} catch (BufferOverflowException ignored) {}
1477-
usb.ioManager.setWriteBufferSize(24); // pending date copied to new buffer
1478-
telnet.write("a".getBytes());
1479-
assertThat(usb.read(1), equalTo("a".getBytes()));
1480-
assertThat(telnet.read(12), equalTo("1234567890AB".getBytes()));
1481-
14821468
// small readbuffer
14831469
usb.ioManager.setReadBufferSize(8);
14841470
Log.d(TAG, "setReadBufferSize(8)");
@@ -1490,74 +1476,31 @@ public void IoManager() throws Exception {
14901476
telnet.write("d".getBytes());
14911477
assertThat(usb.read(1), equalTo("d".getBytes()));
14921478

1479+
SerialInputOutputManager ioManager = usb.ioManager;
1480+
assertEquals(SerialInputOutputManager.State.RUNNING, usb.ioManager.getState());
14931481
usb.close();
1494-
for (int i = 0; i < 100 && usb.hasIoManagerThread(); i++) {
1482+
for (int i = 0; i < 100 && usb.hasIoManagerThreads(); i++) {
14951483
Thread.sleep(1);
14961484
}
1497-
assertFalse("iomanager thread", usb.hasIoManagerThread());
1485+
assertFalse("iomanager threads", usb.hasIoManagerThreads());
1486+
assertNull(usb.ioManager);
1487+
assertEquals(SerialInputOutputManager.State.STOPPED, ioManager.getState());
14981488
SerialInputOutputManager.DEBUG = false;
1499-
1500-
// legacy start
1501-
usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_START)); // creates new IoManager
1502-
usb.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
1503-
telnet.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
1504-
usb.ioManager.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
1505-
Executors.newSingleThreadExecutor().submit(usb.ioManager);
1506-
usb.waitForIoManagerStarted();
1507-
try {
1508-
usb.ioManager.start();
1509-
fail("already running error expected");
1510-
} catch (IllegalStateException ignored) {
1511-
}
15121489
}
15131490

15141491
@Test
15151492
public void writeAsync() throws Exception {
15161493
byte[] data, buf = new byte[]{1};
15171494

1518-
// w/o timeout: write delayed until something is read
1495+
// write immediately, without waiting for read
15191496
usb.open();
15201497
usb.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
15211498
telnet.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
15221499
usb.ioManager.writeAsync(buf);
15231500
usb.ioManager.writeAsync(buf);
1524-
data = telnet.read(1);
1525-
assertEquals(0, data.length);
1526-
telnet.write(buf);
1527-
data = usb.read(1);
1528-
assertEquals(1, data.length);
15291501
data = telnet.read(2);
15301502
assertEquals(2, data.length);
15311503
usb.close();
1532-
1533-
// with timeout: write after timeout
1534-
usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_START));
1535-
usb.ioManager.setReadTimeout(100);
1536-
usb.ioManager.start();
1537-
usb.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
1538-
telnet.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
1539-
usb.ioManager.writeAsync(buf);
1540-
usb.ioManager.writeAsync(buf);
1541-
data = telnet.read(2);
1542-
assertEquals(2, data.length);
1543-
usb.ioManager.setReadTimeout(200);
1544-
1545-
// with internal SerialTimeoutException
1546-
TestBuffer tbuf = new TestBuffer(usb.writeBufferSize + 2*usb.writePacketSize);
1547-
byte[] pbuf1 = new byte[tbuf.buf.length - 4];
1548-
byte[] pbuf2 = new byte[1];
1549-
System.arraycopy(tbuf.buf, 0,pbuf1, 0, pbuf1.length);
1550-
usb.ioManager.setWriteTimeout(20); // tbuf len >= 128, needs 133msec @ 9600 baud
1551-
usb.setParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE);
1552-
telnet.setParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE);
1553-
usb.ioManager.writeAsync(pbuf1);
1554-
for(int i = pbuf1.length; i < tbuf.buf.length; i++) {
1555-
Thread.sleep(20);
1556-
pbuf2[0] = tbuf.buf[i];
1557-
usb.ioManager.writeAsync(pbuf2);
1558-
}
1559-
while(!tbuf.testRead(telnet.read(-1)))
1560-
;
15611504
}
15621505

15631506
@Test

usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/util/UsbWrapper.java

+10-5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import static org.junit.Assert.assertEquals;
3434
import static org.junit.Assert.assertTrue;
3535

36+
import androidx.core.content.ContextCompat;
37+
3638
public class UsbWrapper implements SerialInputOutputManager.Listener {
3739

3840
public final static int USB_READ_WAIT = 500;
@@ -92,7 +94,7 @@ public void onReceive(Context context, Intent intent) {
9294
intent.setPackage(context.getPackageName());
9395
PendingIntent permissionIntent = PendingIntent.getBroadcast(context, 0, intent, flags);
9496
IntentFilter filter = new IntentFilter("com.android.example.USB_PERMISSION");
95-
context.registerReceiver(usbReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
97+
ContextCompat.registerReceiver(context, usbReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
9698
usbManager.requestPermission(serialDriver.getDevice(), permissionIntent);
9799
for(int i=0; i<5000; i++) {
98100
if(granted[0] != null) break;
@@ -256,12 +258,15 @@ public void waitForIoManagerStarted() throws IOException {
256258
throw new IOException("IoManager not started");
257259
}
258260

259-
public boolean hasIoManagerThread() {
261+
public boolean hasIoManagerThreads() {
262+
int c = 0;
260263
for (Thread thread : Thread.getAllStackTraces().keySet()) {
261-
if (thread.getName().equals(SerialInputOutputManager.class.getSimpleName()))
262-
return true;
264+
if (thread.getName().equals(SerialInputOutputManager.class.getSimpleName() + "_read"))
265+
c += 1;
266+
if (thread.getName().equals(SerialInputOutputManager.class.getSimpleName() + "_write"))
267+
c += 1;
263268
}
264-
return false;
269+
return c == 2;
265270
}
266271

267272
// wait full time

0 commit comments

Comments
 (0)