diff --git a/.gitignore b/.gitignore index 0bd3eef1..fbc65d55 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,5 @@ local.properties *.DS_Store proguard/ jacoco.exec + +.idea \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 4ee2d22f..00000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -caches -codeStyles -libraries -workspace.xml -androidTestResultsUserPreferences.xml -appInsightsSettings.xml -deploymentTargetDropDown.xml -deploymentTargetSelector.xml -other.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index b86273d9..00000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index b96734e1..00000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 16660f1d..00000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddf..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java b/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java index df82d5dc..0a40f71c 100644 --- a/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java +++ b/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java @@ -361,9 +361,9 @@ public void prolificBaudRate() throws Exception { Assume.assumeTrue("only for Prolific", usb.serialDriver instanceof ProlificSerialDriver); int[] baudRates = { - 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400, 19200, - 28800, 38400, 57600, 115200, 128000, 134400, 161280, 201600, 230400, 268800, - 403200, 460800, 614400, 806400, 921600, 1228800, 2457600, 3000000, /*6000000*/ + 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400, 19200, + 28800, 38400, 57600, 115200, 128000, 134400, 161280, 201600, 230400, 268800, + 403200, 460800, 614400, 806400, 921600, 1228800, 2457600, 3000000, /*6000000*/ }; usb.open(); Assume.assumeFalse("only for non PL2303G*", ProlificSerialPortWrapper.isDeviceTypeHxn(usb.serialPort)); // HXN does not use divisor @@ -469,7 +469,7 @@ public void Ch34xBaudRate() throws Exception { usb.open(); int[] baudRates = { - 115200, 230400, 256000, 307200, 460800, 921600, 1000000, 1228800 + 115200, 230400, 256000, 307200, 460800, 921600, 1000000, 1228800 }; for (int baudRate : baudRates) { telnet.setParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE); @@ -647,8 +647,8 @@ public void dataBits() throws Exception { data = telnet.read(2); assertThat("19000/7N1", data, equalTo(new byte[]{(byte) 0x80, (byte) 0xff})); } catch (UnsupportedOperationException e) { - if(!usb.isCp21xxRestrictedPort) - throw e; + if(!usb.isCp21xxRestrictedPort) + throw e; } try { usb.setParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE); @@ -873,9 +873,9 @@ public void writeSizes() throws Exception { ((CommonUsbSerialPort)usb.serialPort).setWriteBufferSize(-1); ((CommonUsbSerialPort)usb.serialPort).setWriteBufferSize(-1); assertEquals(usb.serialPort.getWriteEndpoint().getMaxPacketSize(), - CommonUsbSerialPortWrapper.getWriteBuffer(usb.serialPort).length); + CommonUsbSerialPortWrapper.getWriteBuffer(usb.serialPort).length); assertEquals(usb.serialPort.getWriteEndpoint().getMaxPacketSize(), - usb.serialPort.getReadEndpoint().getMaxPacketSize()); + usb.serialPort.getReadEndpoint().getMaxPacketSize()); int baudRate = 300; if(usb.serialDriver instanceof Cp21xxSerialDriver && usb.serialPort.getPortNumber() > 0) @@ -945,7 +945,7 @@ public void writeTimeout() throws Exception { } catch(SerialTimeoutException ex) { assertTrue(ex.getMessage(), ex.getMessage().endsWith("rc=-1")); // timeout in bulkTransfer for(byte[] data = telnet.read(-1, readWait); data.length != 0; - data = telnet.read(-1, readWait)) { + data = telnet.read(-1, readWait)) { tbuf.testRead(data); } assertEquals(0, ex.bytesTransferred); @@ -960,7 +960,7 @@ public void writeTimeout() throws Exception { } catch(SerialTimeoutException ex) { assertTrue(ex.getMessage(), ex.getMessage().endsWith("rc=-1")); // timeout in bulkTransfer for(byte[] data = telnet.read(-1, readWait); data.length != 0; - data = telnet.read(-1, readWait)) { + data = telnet.read(-1, readWait)) { tbuf.testRead(data); } assertEquals(usb.writeBufferSize + usb.writePacketSize, ex.bytesTransferred); @@ -1117,7 +1117,7 @@ else if (usb.serialDriver instanceof ProlificSerialDriver) fail("IllegalArgumentException expected"); } catch (IllegalArgumentException ignored) {} } - + telnet.write("2aaa".getBytes()); data = usb.read(4, 8); assertThat(data, equalTo("2aaa".getBytes())); @@ -1209,13 +1209,13 @@ public void readSpeed() throws Exception { // Android is not a real time OS, so there is no guarantee that the USB thread is scheduled, or it might be blocked by Java garbage collection. // Using SERIAL_INPUT_OUTPUT_MANAGER_THREAD_PRIORITY=THREAD_PRIORITY_URGENT_AUDIO sometimes reduced errors by factor 10, sometimes not at all! // - int diffLen = readSpeedInt(5, -1, 0); + int diffLen = readSpeedInt(5, -1); if(usb.serialDriver instanceof Ch34xSerialDriver && diffLen == -1) - diffLen = 0; // todo: investigate last packet loss + diffLen = 0; // todo: investigate last packet loss assertEquals(0, diffLen); } - private int readSpeedInt(int writeSeconds, int readBufferSize, int readTimeout) throws Exception { + private int readSpeedInt(int writeSeconds, int readBufferSize) throws Exception { int baudrate = 115200; if(usb.serialDriver instanceof Ch34xSerialDriver) baudrate = 38400; @@ -1224,7 +1224,6 @@ private int readSpeedInt(int writeSeconds, int readBufferSize, int readTimeout) writeAhead = 50; usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_START)); - usb.ioManager.setReadTimeout(readTimeout); if(readBufferSize > 0) usb.ioManager.setReadBufferSize(readBufferSize); usb.ioManager.start(); @@ -1379,7 +1378,7 @@ public void purgeHwBuffers() throws Exception { assertThat(usb.read(1), equalTo("y".getBytes())); // cp2105/0 } else { assertThat(usb.read(2), anyOf(equalTo("xy".getBytes()), // cp2102 - equalTo("y".getBytes()))); // cp2102 + equalTo("y".getBytes()))); // cp2102 } } else { assertThat(usb.read(1), equalTo("y".getBytes())); @@ -1401,9 +1400,6 @@ public void IoManager() throws Exception { usb.ioManager = new SerialInputOutputManager(usb.serialPort, usb); assertEquals(usb, usb.ioManager.getListener()); - assertEquals(0, usb.ioManager.getReadTimeout()); - usb.ioManager.setReadTimeout(10); - assertEquals(10, usb.ioManager.getReadTimeout()); assertEquals(0, usb.ioManager.getWriteTimeout()); usb.ioManager.setWriteTimeout(11); assertEquals(11, usb.ioManager.getWriteTimeout()); @@ -1417,7 +1413,6 @@ public void IoManager() throws Exception { usb.ioManager.setReadBufferSize(usb.ioManager.getReadBufferSize()); usb.ioManager.setWriteBufferSize(usb.ioManager.getWriteBufferSize()); - usb.ioManager.setReadTimeout(usb.ioManager.getReadTimeout()); usb.ioManager.setWriteTimeout(usb.ioManager.getWriteTimeout()); usb.close(); @@ -1426,6 +1421,8 @@ public void IoManager() throws Exception { telnet.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); usb.ioManager.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); assertEquals(SerialInputOutputManager.State.STOPPED, usb.ioManager.getState()); + usb.ioManager.setReadBufferSize(22); + assertEquals(22, usb.ioManager.getReadBufferSize()); usb.ioManager.start(); usb.waitForIoManagerStarted(); assertEquals(SerialInputOutputManager.State.RUNNING, usb.ioManager.getState()); @@ -1439,35 +1436,17 @@ public void IoManager() throws Exception { usb.ioManager.setThreadPriority(Process.THREAD_PRIORITY_LOWEST); fail("setThreadPriority IllegalStateException expected"); } catch (IllegalStateException ignored) {} - try { - usb.ioManager.setReadTimeout(20); - fail("setReadTimeout IllegalStateException expected"); - } catch (IllegalStateException ignored) {} - assertEquals(0, usb.ioManager.getReadTimeout()); usb.ioManager.setWriteTimeout(21); assertEquals(21, usb.ioManager.getWriteTimeout()); - usb.ioManager.setReadBufferSize(22); - assertEquals(22, usb.ioManager.getReadBufferSize()); usb.ioManager.setWriteBufferSize(23); assertEquals(23, usb.ioManager.getWriteBufferSize()); - // readbuffer resize - telnet.write(new byte[1]); - usb.ioManager.setReadBufferSize(64); - Log.d(TAG, "setReadBufferSize(64)"); - telnet.write(new byte[1]); // still uses old buffer as infinite waiting step() holds reference to buffer - telnet.write(new byte[1]); // now uses 8 byte buffer - usb.read(3); - // small writebuffer try { usb.ioManager.writeAsync(new byte[8192]); fail("expected BufferOverflowException"); } catch (BufferOverflowException ignored) {} - // small readbuffer - usb.ioManager.setReadBufferSize(8); - Log.d(TAG, "setReadBufferSize(8)"); telnet.write("b".getBytes()); assertThat(usb.read(1), equalTo("b".getBytes())); // now new buffer is used @@ -1569,13 +1548,13 @@ public void readTimeout() throws Exception { int diffLen; usb.close(); // no issue with high transfer rate and long read timeout - diffLen = readSpeedInt(5, -1, longTimeout); + diffLen = readSpeedInt(5, -1); if(usb.serialDriver instanceof Ch34xSerialDriver && diffLen == -1) diffLen = 0; // todo: investigate last packet loss assertEquals(0, diffLen); usb.close(); // date loss with high transfer rate and short read timeout !!! - diffLen = readSpeedInt(5, -1, shortTimeout); + diffLen = readSpeedInt(5, -1); assertNotEquals("sporadic issue!", 0, diffLen); @@ -1615,7 +1594,7 @@ public void wrongDriver() throws Exception { } wrongSerialPort.close(); if(!(usb.serialDriver instanceof Ch34xSerialDriver | - usb.serialDriver instanceof ProlificSerialDriver)) + usb.serialDriver instanceof ProlificSerialDriver)) fail("error expected"); } catch (IOException ignored) { } @@ -1787,9 +1766,9 @@ public void controlLines() throws Exception { // control lines reset on initial open data = "none".getBytes(); assertEquals(usb.inputLinesConnected && !usb.inputLinesOnlyRtsCts - ? EnumSet.of(ControlLine.RI) - : EnumSet.noneOf(ControlLine.class), - usb.serialPort.getControlLines()); + ? EnumSet.of(ControlLine.RI) + : EnumSet.noneOf(ControlLine.class), + usb.serialPort.getControlLines()); assertThat(usb.getControlLine(usb.serialPort::getRTS), equalTo(Boolean.FALSE)); assertThat(usb.getControlLine(usb.serialPort::getCTS), equalTo(inputLineFalse)); assertThat(usb.getControlLine(usb.serialPort::getDTR), equalTo(Boolean.FALSE)); @@ -1810,9 +1789,9 @@ public void controlLines() throws Exception { usb.serialPort.setRTS(true); Thread.sleep(sleep); assertEquals(usb.inputLinesConnected - ? EnumSet.of(ControlLine.RTS, ControlLine.CTS) - : EnumSet.of(ControlLine.RTS), - usb.serialPort.getControlLines()); + ? EnumSet.of(ControlLine.RTS, ControlLine.CTS) + : EnumSet.of(ControlLine.RTS), + usb.serialPort.getControlLines()); assertThat(usb.getControlLine(usb.serialPort::getRTS), equalTo(Boolean.TRUE)); assertThat(usb.getControlLine(usb.serialPort::getCTS), equalTo(inputLineTrue)); assertThat(usb.getControlLine(usb.serialPort::getDTR), equalTo(Boolean.FALSE)); @@ -1830,9 +1809,9 @@ public void controlLines() throws Exception { assertEquals(usb.inputLinesOnlyRtsCts ? EnumSet.of(ControlLine.RTS, ControlLine.DTR, ControlLine.CTS) : usb.inputLinesConnected - ? EnumSet.of(ControlLine.RTS, ControlLine.DTR, ControlLine.CD) - : EnumSet.of(ControlLine.RTS, ControlLine.DTR), - usb.serialPort.getControlLines()); + ? EnumSet.of(ControlLine.RTS, ControlLine.DTR, ControlLine.CD) + : EnumSet.of(ControlLine.RTS, ControlLine.DTR), + usb.serialPort.getControlLines()); assertThat(usb.getControlLine(usb.serialPort::getRTS), equalTo(Boolean.TRUE)); assertThat(usb.getControlLine(usb.serialPort::getCTS), equalTo(usb.inputLinesOnlyRtsCts ? Boolean.TRUE : inputLineFalse)); assertThat(usb.getControlLine(usb.serialPort::getDTR), equalTo(Boolean.TRUE)); @@ -1848,9 +1827,9 @@ public void controlLines() throws Exception { usb.serialPort.setRTS(false); Thread.sleep(sleep); assertEquals(usb.inputLinesConnected && !usb.inputLinesOnlyRtsCts - ? EnumSet.of(ControlLine.DTR, ControlLine.DSR) - : EnumSet.of(ControlLine.DTR), - usb.serialPort.getControlLines()); + ? EnumSet.of(ControlLine.DTR, ControlLine.DSR) + : EnumSet.of(ControlLine.DTR), + usb.serialPort.getControlLines()); assertThat(usb.getControlLine(usb.serialPort::getRTS), equalTo(Boolean.FALSE)); assertThat(usb.getControlLine(usb.serialPort::getCTS), equalTo(inputLineFalse)); assertThat(usb.getControlLine(usb.serialPort::getDTR), equalTo(Boolean.TRUE)); @@ -1973,7 +1952,7 @@ public void flowControlXonXoff() throws Exception { Assume.assumeTrue("flow control not supported", false); } if (usb.serialPort.getSupportedFlowControl().contains(FlowControl.XON_XOFF_INLINE) && - usb.serialPort.getSupportedFlowControl().contains(FlowControl.XON_XOFF)) { + usb.serialPort.getSupportedFlowControl().contains(FlowControl.XON_XOFF)) { fail("only one of both XON_XOFF variants allowed"); } if (usb.serialPort.getSupportedFlowControl().contains(FlowControl.XON_XOFF_INLINE)) { diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java index 546f0ab9..23d14fa3 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java @@ -163,7 +163,8 @@ public void close() throws IOException { /** * use simple USB request supported by all devices to test if connection is still valid */ - protected void testConnection(boolean full) throws IOException { + @Override + public void testConnection(boolean full) throws IOException { testConnection(full, "USB get_status request failed"); } @@ -354,4 +355,9 @@ public void setFlowControl(FlowControl flowcontrol) throws IOException { @Override public void setBreak(boolean value) throws IOException { throw new UnsupportedOperationException(); } + @Override + public UsbDeviceConnection getConnection() { + return mConnection; + } + } diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialPort.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialPort.java index ebf96df5..93232e79 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialPort.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialPort.java @@ -321,4 +321,13 @@ enum FlowControl { NONE, RTS_CTS, DTR_DSR, XON_XOFF, XON_XOFF_INLINE } */ boolean isOpen(); + /** + * Returns the underlying {@link UsbDeviceConnection}. + */ + UsbDeviceConnection getConnection(); + + /** + * use simple USB request supported by all devices to test if connection is still valid + */ + void testConnection(boolean full) throws IOException; } diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/SerialInputOutputManager.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/SerialInputOutputManager.java index c22a2b8c..40ca47a8 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/SerialInputOutputManager.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/SerialInputOutputManager.java @@ -6,8 +6,10 @@ package com.hoho.android.usbserial.util; +import android.hardware.usb.UsbRequest; import android.os.Process; import android.util.Log; +import androidx.annotation.VisibleForTesting; import com.hoho.android.usbserial.driver.UsbSerialPort; @@ -35,13 +37,12 @@ public enum State { private static final String TAG = SerialInputOutputManager.class.getSimpleName(); private static final int BUFSIZ = 4096; - private int mReadTimeout = 0; private int mWriteTimeout = 0; - private final Object mReadBufferLock = new Object(); private final Object mWriteBufferLock = new Object(); - private ByteBuffer mReadBuffer; // default size = getReadEndpoint().getMaxPacketSize() + private int mReadBufferSize; // default size = getReadEndpoint().getMaxPacketSize() + private int mReadBufferCount = 4; private ByteBuffer mWriteBuffer = ByteBuffer.allocate(BUFSIZ); private int mThreadPriority = Process.THREAD_PRIORITY_URGENT_AUDIO; @@ -64,13 +65,13 @@ public interface Listener { public SerialInputOutputManager(UsbSerialPort serialPort) { mSerialPort = serialPort; - mReadBuffer = ByteBuffer.allocate(serialPort.getReadEndpoint().getMaxPacketSize()); + mReadBufferSize = serialPort.getReadEndpoint().getMaxPacketSize(); } public SerialInputOutputManager(UsbSerialPort serialPort, Listener listener) { mSerialPort = serialPort; mListener = listener; - mReadBuffer = ByteBuffer.allocate(serialPort.getReadEndpoint().getMaxPacketSize()); + mReadBufferSize = serialPort.getReadEndpoint().getMaxPacketSize(); } public synchronized void setListener(Listener listener) { @@ -94,17 +95,20 @@ public void setThreadPriority(int threadPriority) { } /** - * read/write timeout + * read buffer count */ - public void setReadTimeout(int timeout) { - // when set if already running, read already blocks and the new value will not become effective now - if(mReadTimeout == 0 && timeout != 0 && mState.get() != State.STOPPED) - throw new IllegalStateException("readTimeout only configurable before SerialInputOutputManager is started"); - mReadTimeout = timeout; + public int getReadBufferCount() { + return mReadBufferCount; } - public int getReadTimeout() { - return mReadTimeout; + /** + * read buffer count + */ + public void setReadBufferCount(int mReadBuffeCount) { + if (!mState.compareAndSet(State.STOPPED, State.STOPPED)) { + throw new IllegalStateException("ReadBufferCount only configurable before SerialInputOutputManager is started"); + } + this.mReadBufferCount = mReadBuffeCount; } public void setWriteTimeout(int timeout) { @@ -119,17 +123,19 @@ public int getWriteTimeout() { * read/write buffer size */ public void setReadBufferSize(int bufferSize) { - if (getReadBufferSize() == bufferSize) - return; - synchronized (mReadBufferLock) { - mReadBuffer = ByteBuffer.allocate(bufferSize); + if (getReadBufferSize() != bufferSize) { + if (!mState.compareAndSet(State.STOPPED, State.STOPPED)) { + throw new IllegalStateException("ReadBuffeCount only configurable before SerialInputOutputManager is started"); + } + mReadBufferSize = bufferSize; } } public int getReadBufferSize() { - return mReadBuffer.capacity(); + return mReadBufferSize; } + public void setWriteBufferSize(int bufferSize) { if(getWriteBufferSize() == bufferSize) return; @@ -227,28 +233,39 @@ private void setThreadPriority() { } } + @VisibleForTesting + protected UsbRequest getUsbRequest() { + return new UsbRequest(); + } + /** * Continuously services the read buffers until {@link #stop()} is called, or until a driver exception is * raised. */ - void runRead() { + public void runRead() { Log.i(TAG, "runRead running ..."); try { setThreadPriority(); mStartuplatch.countDown(); + // Initialize buffers and requests + for (int i = 0; i < mReadBufferCount; i++) { + ByteBuffer buffer = ByteBuffer.allocate(mReadBufferSize); + UsbRequest request = getUsbRequest(); + request.setClientData(buffer); + request.initialize(mSerialPort.getConnection(), mSerialPort.getReadEndpoint()); + request.queue(buffer, buffer.capacity()); + } do { stepRead(); } while (isStillRunning()); Log.i(TAG, "runRead: Stopping mState=" + getState()); } catch (Throwable e) { if (Thread.currentThread().isInterrupted()) { - Log.w(TAG, "runRead: interrupted"); - } else if(mSerialPort.isOpen()) { - Log.w(TAG, "runRead ending due to exception: " + e.getMessage(), e); + Log.w(TAG, "Thread interrupted, stopping runRead."); } else { - Log.i(TAG, "runRead: Socket closed"); + Log.w(TAG, "runRead ending due to exception: " + e.getMessage(), e); + notifyErrorListener(e); } - notifyErrorListener(e); } finally { if (!mState.compareAndSet(State.RUNNING, State.STOPPING)) { if (mState.compareAndSet(State.STOPPING, State.STOPPED)) { @@ -262,7 +279,7 @@ void runRead() { * Continuously services the write buffers until {@link #stop()} is called, or until a driver exception is * raised. */ - void runWrite() { + public void runWrite() { Log.i(TAG, "runWrite running ..."); try { setThreadPriority(); @@ -290,22 +307,29 @@ void runWrite() { } private void stepRead() throws IOException { - // Handle incoming data. - byte[] buffer; - synchronized (mReadBufferLock) { - buffer = mReadBuffer.array(); - } - int len = mSerialPort.read(buffer, mReadTimeout); - if (len > 0) { - if (DEBUG) { - Log.d(TAG, "Read data len=" + len); - } + // Wait for the request to complete + final UsbRequest completedRequest = mSerialPort.getConnection().requestWait(); + if (completedRequest != null) { + final ByteBuffer completedBuffer = (ByteBuffer) completedRequest.getClientData(); + completedBuffer.flip(); // Prepare for reading + final byte[] data = new byte[completedBuffer.remaining()]; + completedBuffer.get(data); final Listener listener = getListener(); - if (listener != null) { - final byte[] data = new byte[len]; - System.arraycopy(buffer, 0, data, 0, len); - listener.onNewData(data); + if ((listener != null) && (data.length > 0)) { + listener.onNewData(data); // Handle data + } + if (data.length == 0) { + mSerialPort.testConnection(true); } + completedBuffer.clear(); // Prepare for reuse + // Requeue the buffer and handle potential failures + if (!completedRequest.queue(completedBuffer, completedBuffer.capacity())) { + Log.e(TAG, "Failed to requeue the buffer"); + throw new IOException("Failed to requeue the buffer"); + } + } else { + Log.e(TAG, "Error waiting for request"); + throw new IOException("Error waiting for request"); } } diff --git a/usbSerialForAndroid/src/test/java/com/hoho/android/usbserial/util/SerialInputOutputManagerTest.java b/usbSerialForAndroid/src/test/java/com/hoho/android/usbserial/util/SerialInputOutputManagerTest.java index 63d9f209..d2e23c42 100644 --- a/usbSerialForAndroid/src/test/java/com/hoho/android/usbserial/util/SerialInputOutputManagerTest.java +++ b/usbSerialForAndroid/src/test/java/com/hoho/android/usbserial/util/SerialInputOutputManagerTest.java @@ -4,15 +4,39 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.nio.ByteBuffer; + +import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbRequest; import android.os.Process; import com.hoho.android.usbserial.driver.CommonUsbSerialPort; +import com.hoho.android.usbserial.driver.UsbSerialPort; import org.junit.Test; public class SerialInputOutputManagerTest { + private static class TestSerialInputOutputManager extends SerialInputOutputManager { + + private final UsbRequest mRequest; + + public TestSerialInputOutputManager(UsbSerialPort serialPort, UsbRequest mRequest) { + super(serialPort); + this.mRequest = mRequest; + } + + public TestSerialInputOutputManager(UsbSerialPort serialPort, Listener listener, UsbRequest mRequest) { + super(serialPort, listener); + this.mRequest = mRequest; + } + + @Override + protected UsbRequest getUsbRequest() { + return mRequest; + } + } // catch all Throwables in onNewData() and onRunError() @Test @@ -29,13 +53,16 @@ class ErrorListener implements SerialInputOutputManager.Listener { @Override public void onRunError(Exception e) { this.e = e; throw new UnknownError("error2");} } + UsbRequest request = mock(UsbRequest.class); UsbEndpoint readEndpoint = mock(UsbEndpoint.class); + UsbDeviceConnection connection = mock(UsbDeviceConnection.class); when(readEndpoint.getMaxPacketSize()).thenReturn(16); CommonUsbSerialPort port = mock(CommonUsbSerialPort.class); when(port.getReadEndpoint()).thenReturn(readEndpoint); - when(port.read(new byte[16], 0)).thenReturn(1); - when(port.isOpen()).thenReturn(true); - SerialInputOutputManager manager = new SerialInputOutputManager(port); + when(port.getConnection()).thenReturn(connection); + when(connection.requestWait()).thenReturn(request); + when(request.getClientData()).thenReturn(ByteBuffer.wrap(new byte[16]).put((byte) 0x00)); + SerialInputOutputManager manager = new TestSerialInputOutputManager(port, request); manager.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); ExceptionListener exceptionListener = new ExceptionListener();