OSDN Git Service

ExifInterface: close the resource after reading EXIF data explicitly
authorJaesung Chung <jaesung@google.com>
Tue, 15 Mar 2016 08:25:31 +0000 (17:25 +0900)
committerJaesung Chung <jaesung@google.com>
Wed, 23 Mar 2016 02:20:29 +0000 (19:20 -0700)
- In case of giving an argument of the image file name or descriptor,
 we created a InputStream at the inside of ExifInterface, close them
 properly.
- Developers should take care of closing input streams when they use
 input streams since they made those input streams.

Bug: 27636804
Change-Id: I9a3a75b1b55dcb2718a60e14814a04cad939d22c

media/java/android/media/ExifInterface.java

index dbedf34..3193c40 100644 (file)
@@ -651,6 +651,7 @@ public class ExifInterface {
     private final String mFilename;
     private final FileDescriptor mSeekableFileDescriptor;
     private final AssetManager.AssetInputStream mAssetInputStream;
+    private final boolean mIsInputStream;
     private boolean mIsRaw;
     private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
     private boolean mHasThumbnail;
@@ -669,20 +670,26 @@ public class ExifInterface {
         if (filename == null) {
             throw new IllegalArgumentException("filename cannot be null");
         }
-        FileInputStream in = new FileInputStream(filename);
+        FileInputStream in = null;
         mAssetInputStream = null;
         mFilename = filename;
-        if (isSeekableFD(in.getFD())) {
-            mSeekableFileDescriptor = in.getFD();
-        } else {
-            mSeekableFileDescriptor = null;
+        mIsInputStream = false;
+        try {
+            in = new FileInputStream(filename);
+            if (isSeekableFD(in.getFD())) {
+                mSeekableFileDescriptor = in.getFD();
+            } else {
+                mSeekableFileDescriptor = null;
+            }
+            loadAttributes(in);
+        } finally {
+            IoUtils.closeQuietly(in);
         }
-        loadAttributes(in);
     }
 
     /**
      * Reads Exif tags from the specified image file descriptor. Attribute mutation is supported
-     * for seekable file descriptors only.
+     * for writable and seekable file descriptors only.
      */
     public ExifInterface(FileDescriptor fileDescriptor) throws IOException {
         if (fileDescriptor == null) {
@@ -692,10 +699,25 @@ public class ExifInterface {
         mFilename = null;
         if (isSeekableFD(fileDescriptor)) {
             mSeekableFileDescriptor = fileDescriptor;
+            // Keep the original file descriptor in order to save attributes when it's seekable.
+            // Otherwise, just close the given file descriptor after reading it because the save
+            // feature won't be working.
+            try {
+                fileDescriptor = Os.dup(fileDescriptor);
+            } catch (ErrnoException e) {
+                e.rethrowAsIOException();
+            }
         } else {
             mSeekableFileDescriptor = null;
         }
-        loadAttributes(new FileInputStream(fileDescriptor));
+        mIsInputStream = false;
+        FileInputStream in = null;
+        try {
+            in = new FileInputStream(fileDescriptor);
+            loadAttributes(in);
+        } finally {
+            IoUtils.closeQuietly(in);
+        }
     }
 
     /**
@@ -718,6 +740,7 @@ public class ExifInterface {
             mAssetInputStream = null;
             mSeekableFileDescriptor = null;
         }
+        mIsInputStream = true;
         loadAttributes(inputStream);
     }
 
@@ -800,32 +823,32 @@ public class ExifInterface {
      * determine whether the image data format is JPEG or not.
      */
     private void loadAttributes(@NonNull InputStream in) throws IOException {
-        // Initialize mAttributes.
-        for (int i = 0; i < EXIF_TAGS.length; ++i) {
-            mAttributes[i] = new HashMap();
-        }
-
-        // Process RAW input stream
-        if (mAssetInputStream != null) {
-            long asset = mAssetInputStream.getNativeAsset();
-            if (handleRawResult(nativeGetRawAttributesFromAsset(asset))) {
-                return;
-            }
-        } else if (mSeekableFileDescriptor != null) {
-            if (handleRawResult(nativeGetRawAttributesFromFileDescriptor(
-                    mSeekableFileDescriptor))) {
-                return;
+        try {
+            // Initialize mAttributes.
+            for (int i = 0; i < EXIF_TAGS.length; ++i) {
+                mAttributes[i] = new HashMap();
             }
-        } else {
-            in = new BufferedInputStream(in, JPEG_SIGNATURE_SIZE);
-            if (!isJpegInputStream((BufferedInputStream) in) && handleRawResult(
-                    nativeGetRawAttributesFromInputStream(in))) {
-                return;
+
+            // Process RAW input stream
+            if (mAssetInputStream != null) {
+                long asset = mAssetInputStream.getNativeAsset();
+                if (handleRawResult(nativeGetRawAttributesFromAsset(asset))) {
+                    return;
+                }
+            } else if (mSeekableFileDescriptor != null) {
+                if (handleRawResult(nativeGetRawAttributesFromFileDescriptor(
+                        mSeekableFileDescriptor))) {
+                    return;
+                }
+            } else {
+                in = new BufferedInputStream(in, JPEG_SIGNATURE_SIZE);
+                if (!isJpegInputStream((BufferedInputStream) in) && handleRawResult(
+                        nativeGetRawAttributesFromInputStream(in))) {
+                    return;
+                }
             }
-        }
 
-        // Process JPEG input stream
-        try {
+            // Process JPEG input stream
             getJpegAttributes(in);
         } catch (IOException e) {
             // Ignore exceptions in order to keep the compatibility with the old versions of
@@ -833,10 +856,10 @@ public class ExifInterface {
             Log.w(TAG, "Invalid JPEG: ExifInterface got an unsupported image format file"
                     + "(ExifInterface supports JPEG and some RAW image formats only) "
                     + "or a corrupted JPEG file to ExifInterface.", e);
-        }
-
-        if (DEBUG) {
-            printAttributes();
+        } finally {
+            if (DEBUG) {
+                printAttributes();
+            }
         }
     }
 
@@ -880,10 +903,6 @@ public class ExifInterface {
                     break;
             }
         }
-
-        if (DEBUG) {
-            printAttributes();
-        }
         return true;
     }
 
@@ -917,7 +936,7 @@ public class ExifInterface {
             throw new UnsupportedOperationException(
                     "ExifInterface does not support saving attributes on RAW formats.");
         }
-        if (mSeekableFileDescriptor == null && mFilename == null) {
+        if (mIsInputStream || (mSeekableFileDescriptor == null && mFilename == null)) {
             throw new UnsupportedOperationException(
                     "ExifInterface does not support saving attributes for the current input.");
         }
@@ -1003,8 +1022,9 @@ public class ExifInterface {
             } else if (mFilename != null) {
                 in = new FileInputStream(mFilename);
             } else if (mSeekableFileDescriptor != null) {
-                Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
-                in = new FileInputStream(mSeekableFileDescriptor);
+                FileDescriptor fileDescriptor = Os.dup(mSeekableFileDescriptor);
+                Os.lseek(fileDescriptor, 0, OsConstants.SEEK_SET);
+                in = new FileInputStream(fileDescriptor);
             }
             if (in == null) {
                 // Should not be reached this.