Monday 28 September 2020

Better way to read data from Android USB Accessory

I am writing an Android app in Java that communicates with a USB accessory using 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:

  1. Communication should be done in a background thread, not on UI thread.
  2. 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:

  1. The read function can block indefinitely waiting for more data.
  2. 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.
  3. I also need to send data to the accessory, but reading and writing to the USB from two separate threads leads 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 an 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 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 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 accessory even if nothing was received from it, so a simple mutex will not work.



from Better way to read data from Android USB Accessory

No comments:

Post a Comment