1

I'm attempting to build a capability that allows a ContentProvider to extract a thumbnail from a raw image.

Here is the implementation:

@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException
{
    [Uri handling stuff]
    return openPipeHelper(image, "unused", null, null, this);
}

@Override
public void writeDataToPipe(@NonNull ParcelFileDescriptor output, @NonNull Uri uri, @NonNull String mimeType, @Nullable Bundle opts, @Nullable Void args)
{
    final String where = Meta.URI_SELECTION;
    final String[]  whereArgs = new String[]{uri.toString()};
    Meta.ImageType imageType = null;
    try(Cursor metaCursor = query(
            Meta.META_URI, new String[] { Meta.TYPE },
            where, whereArgs, null, null))
    {
        if (metaCursor != null && metaCursor.moveToFirst()) 
            imageType = Meta.ImageType.fromInt(metaCursor.getInt(metaCursor.getColumnIndex(Meta.TYPE)));
    }

    byte[] thumbData;
    //noinspection ConstantConditions
    try(AssetFileDescriptor fd = getContext().getContentResolver().openAssetFileDescriptor(uri, "r"))
    {
        if (imageType == null || imageType == Meta.ImageType.UNPROCESSED)
        {
            imageType = ImageUtil.getImageType(getContext(), uri);

            // Update the image type
            ContentValues value = new ContentValues(1);
            value.put(Meta.TYPE, imageType.getValue());
            update(Meta.META_URI, value, where, whereArgs);
        }

        switch(imageType)
        {
            case TIFF:
                thumbData = ImageUtil.getTiffImage(fd.getParcelFileDescriptor().getFd());
                break;
            default:
                thumbData = ImageUtil.getRawThumb(fd.getParcelFileDescriptor().getFd());
                break;
        }
    }
    catch (IOException e)
    {
        Log.e(TAG, ": Failed to open: " + uri, e);
        return;
    }

    if (thumbData == null)
    {
        Log.e(TAG, ": Failed to extract: " + uri);
        return;
    }

    try (FileOutputStream fout = new FileOutputStream(output.getFileDescriptor()))
    {
        fout.write(thumbData, 0, thumbData.length);  // FAILS HERE
        Log.d(TAG, "--End openFile: " + uri);
    }
    catch (IOException e)
    {
        Log.e(TAG, "*Fail openFile: " + uri, e);
    }
}

The code used a PipeDataWriter to place a byte[] from a native (jni) decoded thumbnail ImageUtil.getRawThumb into the read side of a pipe fout.write(thumbData, 0, thumbData.length) that will feed openFile.

However, when I load a few hundred images I'm finding 5-10% of images fail with errors similar to:

03-24 23:34:49.022 26890-28130/com.anthonymandra.rawdroidpro E/MetaProvider: *Fail openFile: content://com.android.externalstorage.documents/tree/0000-0000%3A_largeSet/document/0000-0000%3A_largeSet%2FKyoto-255.CR2 java.io.IOException: write failed: EPIPE (Broken pipe) at libcore.io.IoBridge.write(IoBridge.java:498) at java.io.FileOutputStream.write(FileOutputStream.java:186) at com.anthonymandra.content.MetaProvider.writeDataToPipe(MetaProvider.java:270) at com.anthonymandra.content.MetaProvider.writeDataToPipe(MetaProvider.java:40) at android.content.ContentProvider$1.doInBackground(ContentProvider.java:1712) at android.os.AsyncTask$2.call(AsyncTask.java:295) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) at java.lang.Thread.run(Thread.java:818) Caused by: android.system.ErrnoException: write failed: EPIPE (Broken pipe) at libcore.io.Posix.writeBytes(Native Method) at libcore.io.Posix.write(Posix.java:271) at libcore.io.BlockGuardOs.write(BlockGuardOs.java:313) at libcore.io.IoBridge.write(IoBridge.java:493) at java.io.FileOutputStream.write(FileOutputStream.java:186)  at com.anthonymandra.content.MetaProvider.writeDataToPipe(MetaProvider.java:270)  at com.anthonymandra.content.MetaProvider.writeDataToPipe(MetaProvider.java:40)  at android.content.ContentProvider$1.doInBackground(ContentProvider.java:1712)  at android.os.AsyncTask$2.call(AsyncTask.java:295)  at java.util.concurrent.FutureTask.run(FutureTask.java:237)  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)  at java.lang.Thread.run(Thread.java:818)

ContentResolver.openInputStream is being called by Glide, which is using up to 'core-count' threads to load images.

Previously I just loaded the thumbnail via a custom loader for Glide without issue. I'm guessing there's some concurrency limitation to the underlying "pipe" system. Most questions on this topic don't get a satisfying answer. Is there anything obviously wrong here or am I exceeding the capabilities of the system?

Anthony
  • 7,638
  • 3
  • 38
  • 71

0 Answers0