Transfer InputStream to another Service (across process boundaries) with ParcelFileDescriptor.createPipe() failes with “EBADF (Bad file number)”

吃可爱长大的小学妹 提交于 2019-11-29 21:57:14

It seems like the cause was the ParcelFileDescriptor being an argument of the service method. If the service does return the ParcelFileDescriptor it works as expected.

Sending Service (Process A)

public sendInputStream() {
    InputStream is = ...; // that's the stream for process/service B
    ParcelFileDescriptor pfd = inputStreamService.inputStream();
    OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(pfd);

    int len;
    byte[] buf = new byte[1024];
    try {
        while ((len = is.read(buf)) > 0) {
            os.write(buf, 0, len);
    } catch (IOException e) {
    } finally {
        try { is.close(); } catch (IOException e1) {}
        try { os.close(); ] catch (IOException e1) {}
    }
}

Receiving Service Code (Process B)

The receiving service's .aidl:

package org.exmaple;
interface IInputStreamService {
    ParcelFileDescriptor inputStream();
}

The receiving service, called by Process A:

public class InputStreamService extends Service {

@Override
public IBinder onBind(Intent intent) {
    return mBinder;
}

private final IInputStreamService.Stub mBinder = new IInputStreamService.Stub() {

    @Override
    public void ParcelFileDescriptor inputStream() throws RemoteException {
                // one can read the contents of the Processes A's InputStream
                // from the following OutputStream
                OutputStream os = ...;
                ParcelFileDescriptor pfd = ParcelFileDescriptorUtil.pipeTo(os);
                return pfd;
    }
};

The ParcelFileDescriptorUtil is a helper class, with a classic java.io. stream-to-stream copy Thread. Now we have to use the pipeTo() method.

public class ParcelFileDescriptorUtil {

    public static ParcelFileDescriptor pipeTo(OutputStream outputStream) throws IOException {
        ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
        ParcelFileDescriptor readSide = pipe[0];
        ParcelFileDescriptor writeSide = pipe[1];

        // start the transfer thread
        new TransferThread(new ParcelFileDescriptor.AutoCloseInputStream(readSide), outputStream).start();

        return writeSide;
    }

    static class TransferThread extends Thread {
        final InputStream mIn;
        final OutputStream mOut;

        TransferThread(InputStream in, OutputStream out) {
            super("ParcelFileDescriptor Transfer Thread");
            mIn = in;
            mOut = out;
            setDaemon(true);
        }

        @Override
        public void run() {
            byte[] buf = new byte[1024];
            int len;

            try {
                while ((len = mIn.read(buf)) > 0) {
                    mOut.write(buf, 0, len);
                }
                mOut.flush(); // just to be safe
            } catch (IOException e) {
                LOG.e("TransferThread", e);
            }
            finally {
                try {
                    mIn.close();
                } catch (IOException e) {
                }
                try {
                    mOut.close();
                } catch (IOException e) {
                }
            }
        }
    }
}

This allows you to transfer InputStreams across process boundaries, one drawback is that there is some CPU time involved in the stream-to-stream copies.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!