简体   繁体   中英

Java.io.IOException, “bad file number” USB connection

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?

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);
    }
} 

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.

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 TextView s, etc.

Thread

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM