Better way to read data from Android USB Accessory
Asked Answered
E

0

8

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:

  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 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.

Economically answered 25/9, 2020 at 14:51 Comment(7)
You can't read from the same InputStream twice, hence the error on the second call. Either place it in a BufferedInputStream or have a way to reset the stream (or completely load the stream, but I guess that's not what you wanted to do)Hollo
@Fullslack.dev what do you mean I can't read from it twice? Why not? Do you have some reference to documentation? Reading in a loop works with no other actions on the stream in my code, as long as I provide a big enough buffer for each read. I can't reload this stream since it is backed by a hardware device - USB, not a file on disk. Resetting or reloading it would mean closing and reopening the USB connection, and that's not possible.Economically
santhoshreddymandadi.com/java/… and https://mcmap.net/q/149084/-read-input-stream-twice are some examples. I know reading in a loop works, however you also mention a error after reading the first 2 bytes to get the size of the stream. That is basically reading the InputStream 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.Hollo
@Fullslack.dev I see what you mean now, but I think you misunderstood my code: I am not trying to read the same bytes again. In the examples you linked, they are trying to re-read data without resetting the stream, and that will obviously fail, but I am trying to read part of the available data in one read operation and then the rest of the data I have not read yet in a separate operation. If this stream was pointing to a regular file this would have worked perfectly.Economically
@Fullslack.dev (part 2, comment too long): Note that the offset parameter in the second read specifies where in the byte buffer to put the result, not where in the stream to read. Thanks for trying to help, but it looks like my problem is elsewhere.Economically
I'm in the same situation. It seems that the available() 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!Taro
Hello, I'm stuck in libusb_bulk_transfer reading from Android, hangs and waiting for read without data.. should I add \0 from Android message ? thanks.Tillich

© 2022 - 2024 — McMap. All rights reserved.