I am writing an Android app in Java that communicates with a USB accessory using an Android accessory API.
The google documentation does not give an example of actually reading and writing data, only how to open the accessory for communication.
It also states 2 things:
- Communication should be done in a background thread, not on UI thread.
- Read buffer should be at least 16384 bytes long because this is the maximum length of message for Android Open Accessory protocol.
I implemented reading like this:
/** boiler plate from example docs: **/
fileDescriptor = usbManager.openAccessory(accessory);
if (fileDescriptor != null) {
FileDescriptor fd = fileDescriptor.getFileDescriptor();
inputStream = new FileInputStream(fd);
readBuffer = new byte[16384];
// ... some more code that is not relevant for reading data ... //
}
/** my code in separate thread: **/
int read;
while ((read = inputStream.read(readBuffer)) >= 0) {
// ... parse message and do stuff with it ... //
}
This code works, and I get correct data from the accessory, but it creates problems for my application logic:
- The read function can block indefinitely waiting for more data.
- It sometimes returns more than one message in the buffer, back to back, so I have no idea when or how it decides the accessory is done sending and should return the accumulated bytes.
- I also need to send data to the accessory, but reading and writing to the USB from two separate threads lead to errors.
I have no control over the code running on the accessory, it is a closed product, but I do know its protocol: it sends messages prefixed with 2 bytes indicating the size of the message.
So, I tried:
read = inputStream.read(readBuffer, 0, 2);
if (read == 2) {
int size = (readBuffer[0] & 0xff) + ((readBuffer[1] & 0xff) << 8);
read = inputStream.read(readBuffer, 2, size);
}
The first read returns the expected message size, but the second read always throws java.io.IOException: read failed: EIO (I/O error)
I also tried using available()
method before calling read
to try and find out how many bytes can be read, but that throws IllegalArgumentException
exception.
So, my question is:
Is there any other way to read data from Android Accessory besides blindly giving the read
function maximum buffer and waiting for it to return?
If not, is there at least a way to make the read
function time out if no bytes were received in the specified time frame?
Is there a way to work directly with bulk transfer API on Android Accessory, the way Android USB Host API allows?
If all that is not possible, what is the correct logic to make sure reads and writes to the accessory don't interfere with each other?
The protocol I am working with does not guarantee one to one back and forward send and receive. The accessory can take user input and send it to the Android device at any time, and the Android device needs to send data to the accessory even if nothing was received from it, so a simple mutex will not work.
InputStream
twice, hence the error on the second call. Either place it in aBufferedInputStream
or have a way to reset the stream (or completely load the stream, but I guess that's not what you wanted to do) – HolloInputStream
twice. You could try to change that second read to start at 0 as well and end at -2, however I don't really like this approach as it can cause errors if the input changes. – Holloavailable()
method doesn't work and for some reason you can't write to the descriptor while you are reading. If you found how to do it please update your post! – Tarolibusb_bulk_transfer
reading from Android, hangs and waiting for read without data.. should I add\0
from Android message ? thanks. – Tillich