Native Crash in Android 4.3 when calling Picture.writeToStream()
Asked Answered
G

1

6

A regression has been introduced into Android 4.3. Code that used to work in previous versions of Android now causes a native crash which shuts down the process.

The crash occurs when drawing an image that is larger than 32 kb into a canvas which is being recorded by a Picture object which is in turn written to a stream via writeToStream().

The crash is occurring down in Skia when trying to write away a string (Which I believe is the Uri of the image object).

I/DEBUG(122):     #00  pc 0001e3bc  /system/lib/libc.so (strlen+72)    
I/DEBUG(122):     #01  pc 000d9858  /system/lib/libskia.so (SkWriter32::writeString(char const*, unsigned int)+256)    
I/DEBUG(122):     #02  pc 00113d68  /system/lib/libskia.so (SkImageRef_ashmem::flatten(SkFlattenableWriteBuffer&) const+44)

The following program shows how to reproduce this issue. All that is need is a layout with a button that has the id 'button'.

    public class MainActivity extends Activity {

    static final String IMAGE_FILE = Environment.getExternalStorageDirectory() + "/test.jpg";
    static final String SKIA_FILE = Environment.getExternalStorageDirectory() + "/test.skia";

    private static Bitmap loadBitmap(final String filename) {
        Bitmap bitmap = null;
        FileInputStream is;
        try {
            is = new FileInputStream(filename);
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inInputShareable = true;
            options.inPurgeable = true;
            bitmap = BitmapFactory.decodeFileDescriptor(is.getFD(), null, options);
            is.close();
        } catch (final FileNotFoundException e) {
            e.printStackTrace();
        } catch (final IOException ex) {
            ex.printStackTrace();
        }
        return bitmap;
    }

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(final View v) {

                final Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        // Create a Canvas and begin recording
                        final Picture picture = new Picture();
                        final Canvas canvas = picture.beginRecording(1024, 1024);
                        // De-compress an image from file
                        final Bitmap bitmap = loadBitmap(IMAGE_FILE);
                        // If present draw the image to the canvas and end
                        // recording
                        if (bitmap != null) {
                            canvas.drawBitmap(bitmap, new Matrix(), null);
                        }
                        picture.endRecording();

                        // Write out the Picture object to a Skia File.
                        FileOutputStream os;
                        try {
                            os = new FileOutputStream(SKIA_FILE);
                            picture.writeToStream(os);
                            os.close();
                        } catch (final FileNotFoundException e) {
                            e.printStackTrace();
                        } catch (final IOException ex) {
                            ex.printStackTrace();
                        }
                    }
                };
                new Thread(runnable).start();
            }
        });
    }
}

The two lines setting the BitmapFactory.Options are required to get the Skia flattening code to write out the image data (Otherwise images are emitted).

options.inInputShareable = true;
options.inPurgeable = true;

I am aware that the Picture methods writeToStream() and createFromStream() have been deprecated but I would not expect this to introduce a stability issue.

I need to write away the Picture object as I want to pass it from the main application to a service process. I cannot use the recommended workaround in the documentation which says to 'draw the picture into a bitmap' for the following reasons:

  1. The desired resolution of the Picture is not known at the time of writing away.
  2. The Picture object needs to be scaled up via a matrix after being restored.
  3. Saving to a very high resolution bitmap is inefficient in terms of memory and processing time.

Does anybody know a work around that allows the images to be written into the stream without causing this crash?

Georgeta answered 30/7, 2013 at 15:15 Comment(0)
P
4

I would have added this as a comment but I lack the reputation...

The breaking change in Skia appears to be the change to SkImageRef_ashmem.cpp:

https://code.google.com/p/skia/source/detail?r=4980

The flatten method used to check for a null URI and would write 0 to the output stream if the uri was null. Passing null to SkFlattenableWriteBuffer::writeString() results in the crash in strlen().

Polar answered 31/7, 2013 at 13:49 Comment(2)
Reported to Skia project as issue 1457: code.google.com/p/skia/issues/detail?id=1457Polar
This should be fixed in future version of Android now as the core Skia team have checked in a fix see code.google.com/p/android/issues/detail?id=58257Georgeta

© 2022 - 2024 — McMap. All rights reserved.