Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9b8f9969 authored by Mark Harman's avatar Mark Harman
Browse files

Keep references to ParcelFileDescriptor to avoid problems with garbage collector.

parent 5e95ef4a
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -51,6 +51,8 @@
Version 1.48.2 (Work in progress)

FIXED   Manual focus and focus bracketing seekbars weren't being hidden when in immersive mode.
FIXED   Video subtitles would stop before end of video on some devices when using Storage Access
        Framework.
UPDATED Switched to AndroidX support library.
UPDATED Artist, Copyright exif tags option now supported for devices running Android 6 or earlier.
UPDATED Selecting remote device type for Bluetooth remote control no longer calls DeviceScanner
+32 −6
Original line number Diff line number Diff line
@@ -3195,12 +3195,36 @@ public class ImageSaver extends Thread {
        return bitmap;
    }

    /* In some cases we may create an ExifInterface with a FileDescriptor obtained from a
     * ParcelFileDescriptor (via getFileDescriptor()). It's important to keep a reference to the
     * ParcelFileDescriptor object for as long as the exif interface, otherwise there's a risk of
     * the ParcelFileDescriptor being garbage collected, invalidating the file descriptor still
     * being used by the ExifInterace!
     * This didn't cause any known bugs, but good practice to fix, similar to the issue reported in
     * https://sourceforge.net/p/opencamera/tickets/417/ .
     */
    private static class ExifInterfaceHolder {
        private final ParcelFileDescriptor pfd;
        private final ExifInterface exif;

        ExifInterfaceHolder(ParcelFileDescriptor pfd, ExifInterface exif) {
            this.pfd = pfd;
            this.exif = exif;
        }

        ExifInterface getExif() {
            return this.exif;
        }
    }

    /** Creates a new exif interface for reading and writing.
     *  If picFile==null, then saveUri must be non-null, and will be used instead to write the exif
     *  tags too.
     *  May return null if unable to create the exif interface.
     *  The returned ExifInterfaceHolder will always be non-null, but the contained getExif() may
     *  return null if this method was unable to create the exif interface.
     */
    private ExifInterface createExifInterface(File picFile, Uri saveUri) throws IOException {
    private ExifInterfaceHolder createExifInterface(File picFile, Uri saveUri) throws IOException {
        ParcelFileDescriptor parcelFileDescriptor = null;
        ExifInterface exif = null;
        if( picFile != null ) {
            if( MyDebug.LOG )
@@ -3210,7 +3234,7 @@ public class ImageSaver extends Thread {
        else {
            if( MyDebug.LOG )
                Log.d(TAG, "write direct to saveUri: " + saveUri);
            ParcelFileDescriptor parcelFileDescriptor = main_activity.getContentResolver().openFileDescriptor(saveUri, "rw");
            parcelFileDescriptor = main_activity.getContentResolver().openFileDescriptor(saveUri, "rw");
            if( parcelFileDescriptor != null ) {
                FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
                exif = new ExifInterface(fileDescriptor);
@@ -3219,7 +3243,7 @@ public class ImageSaver extends Thread {
                Log.e(TAG, "failed to create ParcelFileDescriptor for saveUri: " + saveUri);
            }
        }
        return exif;
        return new ExifInterfaceHolder(parcelFileDescriptor, exif);
    }

    /** Makes various modifications to the saved image file, according to the preferences in request.
@@ -3235,7 +3259,8 @@ public class ImageSaver extends Thread {
            if( MyDebug.LOG )
                Log.d(TAG, "add additional exif info");
            try {
                ExifInterface exif = createExifInterface(picFile, saveUri);
                ExifInterfaceHolder exif_holder = createExifInterface(picFile, saveUri);
                ExifInterface exif = exif_holder.getExif();
                if( exif != null ) {
                    modifyExif(exif, request.type == Request.Type.JPEG, request.using_camera2, request.current_date, request.store_location, request.store_geo_direction, request.geo_direction, request.custom_tag_artist, request.custom_tag_copyright, request.level_angle, request.pitch_angle, request.store_ypr);
                    exif.saveAttributes();
@@ -3254,7 +3279,8 @@ public class ImageSaver extends Thread {
            if( MyDebug.LOG )
                Log.d(TAG, "remove GPS timestamp hack");
            try {
                ExifInterface exif = createExifInterface(picFile, saveUri);
                ExifInterfaceHolder exif_holder = createExifInterface(picFile, saveUri);
                ExifInterface exif = exif_holder.getExif();
                if( exif != null ) {
                    fixGPSTimestamp(exif, request.current_date);
                    exif.saveAttributes();
+7 −3
Original line number Diff line number Diff line
@@ -1907,7 +1907,10 @@ public class MyApplicationInterface extends BasicApplicationInterface {
            final boolean store_location = getGeotaggingPref();
            final boolean store_geo_direction = getGeodirectionPref();
            class SubtitleVideoTimerTask extends TimerTask {
                OutputStreamWriter writer;
                // need to keep a reference to pfd_saf for as long as writer, to avoid getting garbage collected - see https://sourceforge.net/p/opencamera/tickets/417/
                @SuppressWarnings("FieldCanBeLocal")
                private ParcelFileDescriptor pfd_saf;
                private OutputStreamWriter writer;
                private int count = 1;
                private long min_video_time_from = 0;

@@ -2054,7 +2057,7 @@ public class MyApplicationInterface extends BasicApplicationInterface {
                                    String subtitle_filename = storageUtils.getFileName(last_video_file_saf);
                                    subtitle_filename = getSubtitleFilename(subtitle_filename);
                                    Uri subtitle_uri = storageUtils.createOutputFileSAF(subtitle_filename, ""); // don't set a mimetype, as we don't want it to append a new extension
                                    ParcelFileDescriptor pfd_saf = getContext().getContentResolver().openFileDescriptor(subtitle_uri, "w");
                                    pfd_saf = getContext().getContentResolver().openFileDescriptor(subtitle_uri, "w");
                                    writer = new FileWriter(pfd_saf.getFileDescriptor());
                                }
                            }
@@ -2174,6 +2177,7 @@ public class MyApplicationInterface extends BasicApplicationInterface {
            // create thumbnail
            long debug_time = System.currentTimeMillis();
            Bitmap thumbnail = null;
            ParcelFileDescriptor pfd_saf; // keep a reference to this as long as retriever, to avoid risk of pfd_saf being garbage collected
            MediaMetadataRetriever retriever = new MediaMetadataRetriever();
            try {
                if( video_method == VIDEOMETHOD_FILE ) {
@@ -2181,7 +2185,7 @@ public class MyApplicationInterface extends BasicApplicationInterface {
                    retriever.setDataSource(file.getPath());
                }
                else {
                    ParcelFileDescriptor pfd_saf = getContext().getContentResolver().openFileDescriptor(uri, "r");
                    pfd_saf = getContext().getContentResolver().openFileDescriptor(uri, "r");
                    retriever.setDataSource(pfd_saf.getFileDescriptor());
                }
                thumbnail = retriever.getFrameAtTime(-1);