11

I'm setting up a USB accessory connection between my Android phone and another device. Just sending bytes back and forth for now to test. I get some definite communication going at first, but it always ends up dying with Java.io.IOException: write failed: EBADF (Bad file number)" after a second or so. Sometimes the reading stays alive but the writing dies; others both die.

I'm not doing anything super fancy, reading and writing just like the Google documentation:

Initial connection (inside a broadcast receiver, I know this part works at least initially):

if (action.equals(ACTION_USB_PERMISSION))
{
    ParcelFileDescriptor pfd = manager.openAccessory(accessory);
    if (pfd != null) {
        FileDescriptor fd = pfd.getFileDescriptor();
        mIn = new FileInputStream(fd);
        mOut = new FileOutputStream(fd);
    }
}

Reading:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        byte[] buf = new byte[BUF_SIZE];
        while (true)
        {
            try {
                int recvd = mIn.read(buf);
                if (recvd > 0) {
                    byte[] b = new byte[recvd];
                    System.arraycopy(buf, 0, b, 0, recvd);
                    //Parse message
                }
            }
            catch (IOException e) {
                Log.e("read error", "failed to read from stream");
                e.printStackTrace();
            }
        }
    }
});
thread.start();

Writing:

synchronized(mWriteLock) {
    if (mOut !=null && byteArray.length>0) {
        try {
            //mOut.flush();
            mOut.write(byteArray, 0, byteArray.length);
        }
        catch (IOException e) {
            Log.e("error", "error writing");
            e.printStackTrace();
            return false;
        }
    }
    else {
        Log.e(TAG, "Can't send data, serial stream is null");
        return false;
    }
}

Error stacktrace:

java.io.IOException: write failed: EBADF (Bad file number)
W/System.err(14028):     at libcore.io.IoBridge.write(IoBridge.java:452)
W/System.err(14028):     at java.io.FileOutputStream.write(FileOutputStream.java:187)
W/System.err(14028):     at com.my.android.transport.MyUSBService$5.send(MyUSBService.java:468)
W/System.err(14028):     at com.my.android.transport.MyUSBService$3.onReceive(MyUSBService.java:164)
W/System.err(14028):     at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:781)
W/System.err(14028):     at android.os.Handler.handleCallback(Handler.java:608)
W/System.err(14028):     at android.os.Handler.dispatchMessage(Handler.java:92)
W/System.err(14028):     at android.os.Looper.loop(Looper.java:156)
W/System.err(14028):     at android.app.ActivityThread.main(ActivityThread.java:5045)
W/System.err(14028):     at java.lang.reflect.Method.invokeNative(Native Method)
W/System.err(14028):     at java.lang.reflect.Method.invoke(Method.java:511)
W/System.err(14028):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
W/System.err(14028):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
W/System.err(14028):     at dalvik.system.NativeStart.main(Native Method)
W/System.err(14028): Caused by: libcore.io.ErrnoException: write failed: EBADF (Bad file number)
W/System.err(14028):     at libcore.io.Posix.writeBytes(Native Method)
W/System.err(14028):     at libcore.io.Posix.write(Posix.java:178)
W/System.err(14028):     at libcore.io.BlockGuardOs.write(BlockGuardOs.java:191)
W/System.err(14028):     at libcore.io.IoBridge.write(IoBridge.java:447)
W/System.err(14028):     ... 13 more

I have logging all over the place and thus I know it's not anything too obvious, such as another permission request being received (and thus the file streams being reinitialized mid-read). The streams aren't closing either, because I never have that happen anywhere in my code (for now). I'm not getting any detached or attached events either (I log that if it happens). Nothing seems too out of the ordinary; it just dies.

I thought maybe it was a concurrency issue, so I played with locks and sleeps, nothing worked that I tried. I don't think it's a throughput issue either because it still happens when I sleep every read (on both ends), and read one single packet at a time (super slow bitrate). Is there a chance the buffer is being overrun on the other end somehow? How would I go about clearing this? I do have access to the other end's code, it is an Android device as well, using Host mode. In case that it matters, I can post that code too - standard bulk transfers.

Does the phone just have lackluster support for Android Accessory Mode? I've tried two phones and they both fail similarly, so I doubt it's that.

I'm wondering what causes this error in general when writing or reading from USB on Android?

6
  • What version of Android are you using? Also, what are you trying to communicate with? I believe that with Accessory Mode (are you using Android Open Accessory Mode?) you need a host device that is compatible like the arduino or FT311D. Commented Apr 12, 2013 at 1:36
  • Android 4.0.4. I can get the initial connection just fine, and there's no such thing as a "compatible" device - if you send the right control requests, the phone goes in accessory mode if it supports it. A dialog pops up on my phone stating that it sees an accessory - just having problems with keeping the connection. Commented Apr 12, 2013 at 2:20
  • Oh ok, wait it sees an accessory, meaning your phone is in host mode then, not accessory mode right? Which device is providing the power, phone to device or device providing to the phone? Commented Apr 12, 2013 at 2:26
  • No, the Android documentation is actually a little confusing with the terminology. It sees an accessory, meaning it goes into accessory mode. When you are in accessory mode on a phone, you can iterate through UsbAccessory objects - you are seeing them in accessory mode. The device is kind of like an Android tablet (also 4.0.4 I think); it is providing power to my phone. It is communicating via Host mode. This all works fine initially, just this "bad file number" comes up eventually. Commented Apr 12, 2013 at 2:35
  • Ok, I have worked with Open Accessory mode mostly so I am just trying to figure out more specifics to see how much I may be able to help you... So it sounds like you have two android devices talking via USB between each other, one host, one accessory? Is that correct? And you are getting the "bad file number" on the accessory android device? Commented Apr 12, 2013 at 2:58

3 Answers 3

8

I got same problem in my code, and I found that it happens because FileDescriptor object was GCed.

I fixed this issue by adding ParcelFileDescriptor field in Activity(or Service).

I checked your first code snippet and the code you based on, and latter has ParcelFileDescriptor field in Thread.

I think if you edit your code like below, it works well.

ParcelFileDescriptor mPfd;
...

if (action.equals(ACTION_USB_PERMISSION))
{
    mPfd = manager.openAccessory(accessory);
    if (mPfd != null) {
        FileDescriptor fd = mPfd.getFileDescriptor();
        mIn = new FileInputStream(fd);
        mOut = new FileOutputStream(fd);
    }
} 
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for contributing to this. I haven't done this in years now, but hopefully people find it helpful.
This answer definitely deserves more upvotes, the fact ParcelFileDescriptor is GC'ed isn't obvious at all, I had a method returning new FileOutputStream(mPfd.getFileDescriptor()) and was seeing errors like that after couple of transfers. I spent many hours on this, thanks mate!
7

It ended up being a threading issue. I needed to more properly segregate even writing as well, instead of just reading.

I ended up using this code as a basis.

1 Comment

Hi, I have same issue and I have seen above link but not worked for me. please share what was the threading issue.
1

OK, a few things I noticed that just seemed different from what I do for Open Accessory Mode, which I followed the documentation for USB accessory mostly, so it should be very similar, is that your mIn.read(buf); should be mIn.read(buf, 0, 64); as far as I know.

Also, you should declare in your class declarations thread myThread;. Then within your BroadcastReceiver after creating the new FileInput/OutputStream, have myThread = new thread(myHandler, myInputStream); followed my myThread.start();.

Now I noticed that you are communicating directly with the UI from your thread. You should use a handler instead that the thread will communicate to and then that will communicate back to your UI, at least from what I had read.

Here is an example of my handler and thread:

final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg){

    }
};

private class USB_Thread extends Thread {
    Handler thisHandler;
    FileInputStream thisInputStream;

    USB_Thread(Handler handler, FileInputStream instream){
        thisHandler = handler;
        thisInputStream = instream;
    }
    @Override
    public void run(){
        while(true) {
            try{
                if((thisInputStream != null) && (dataReceived == false)) {
                    Message msg = thisHandler.obtainMessage();
                    int bytesRead = thisInputStream.read(USB_Data_In, 0, 63);
                    if (bytesRead > 0){
                        dataReceived = true;
                        thisHandler.sendMessage(msg);
                    }
                }
            }
            catch(IOException e){

            }
        }
    }
}

Also, there are some demo open accessory application here. They may help with your understanding of accessory mode.

And also there are known issues with an application not receiving the BroadcastReceiver for ACTION_USB_ACCESSORY/DEVICE_ATTACHED programmatically. It will only receive it via the manifest file. You can find more on this here and here.

I actually didn't test putting the dataReceived variable in the handler and only recently changed that part of my code. I tested it and it didn't work, so trying to remember what it was I had read, I think it was not about variables communicating within the threads, but trying to use something like .setText(). I have updated my code to include the dataReceived=true in the thread. The handler would then be used for updating items on the UI, such as TextViews, etc.

Thread

FileDescriptor fd = mFileDescriptor.getFileDescriptor();
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
usbThread = new USB_Thread(mHandler, mInputStream);
usbThread.start();

6 Comments

Thanks for the tips, but I'm not communicating directly with the UI; where did you get that from? The mRead overload was good advice, I should always specify a maximum, but it didn't end up changing much. I'm thinking this may be a hardware side issue....so hard to say.
Yea, the UI was a misread. It could be a possible hardware issue too. I have tested Open Accessory code on three different tablets, an off brand one that didn't work at all, the Nexus 7 which worked well except when powering up from an off state and then a Galaxy Tab 2 7" which works like the Nexus but doesn't have the startup problem. Although where are you starting your read thread?
I have editted my answer. Try something like that for your thread right after your input/output stream declaration. Maybe that will work...
Another thing I just noticed, try moving the synchronized() command to before your ParcelFileDescriptor in your BroadcastReceiver.
Again thanks for the tips, I tried some of your suggestions as well as other things but still failing. However I dug in further and the only thing that I can see looks out of place is a StrictMode$InstanceCountViolation, which apparently you can turn off strict mode. Tried that, no luck either. I'm gonna try explicitly declaring permissions for the phone I'm using within the hardware and see if that helps matters.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.