Nick Felker
Nick Felker

Reputation: 11968

How to return byte data with Android's ParcelFileDescriptor and Documents Provider

I'm building a Documents Provider in order to access data that my application has, which I am able to represent as a byte array. I have gotten the querying working, but I have become stuck on the opening as I do not fully understand how the ParcelFileDescriptor works and how I am able to pass data through it.

When my document provider's openDocument is called, I get a byte array representing my file data.

private ParcelFileDescriptor getFileDescriptor(byte[] data) throws IOException {
    ParcelFileDescriptor[] pipes = ParcelFileDescriptor.createReliablePipe();
    ParcelFileDescriptor readPipe = pipes[0];
    ParcelFileDescriptor writePipe = pipes[1];

    OutputStream stream = new ParcelFileDescriptor.AutoCloseOutputStream(writePipe);
    stream.write(data);
    return writePipe;
}

Then, in my activity, I receive the uri and to open I run:

try {
    InputStream inputStream = getContentResolver().openInputStream(uri);
    InputStreamReader reader = new InputStreamReader(inputStream);
    StringBuilder stringBuilder = new StringBuilder();
    int c;
    while ((c = reader.read()) != -1) {
        stringBuilder.append(c);
    }
    reader.close();
    String fileData = stringBuilder.toString();
    textView.setText(uri.toString() + "\n\n" + fileData);
} catch (IOException e) {
    textView.setText(uri.toString() + "\n\nError: " + e.getMessage());
    e.printStackTrace();
}

I believe this should allow me to pull out that data, but I am getting the error java.io.IOException: read failed: EBADF (Bad file descriptor)

If I try returning the readPipe in my method, the app appears to get in a bad state and stops working. So, I think returning the writePipe is what I want, but I don't know why it can't be read. I haven't had luck finding a good example of using a ParcelFileDescriptor in this way.

Thanks.

Upvotes: 1

Views: 4018

Answers (1)

Nick Felker
Nick Felker

Reputation: 11968

After looking at the example that came from @CommonsWare, I was able to determine how to manage the pipes in order to process my byte array and then return that to the calling activity.

Here's how the code ultimately turned out.

private ParcelFileDescriptor getFileDescriptor(byte[] fileData) throws IOException {
    Log.d(TAG, "Found " + fileData.length + " bytes of data");
    ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();

    // Stream the file data to our ParcelFileDescriptor output stream
    InputStream inputStream = new ByteArrayInputStream(fileData);
    AutoCloseOutputStream outputStream = new AutoCloseOutputStream(pipe[1]);
    int len;
    while ((len = inputStream.read()) >= 0) {
        outputStream.write(len);
    }
    inputStream.close();
    outputStream.flush();
    outputStream.close();

    // Return the ParcelFileDescriptor input stream to the calling activity in order to read
    // the file data.
    return pipe[0];
}

Upvotes: 3

Related Questions