Merge "ExifInterface: close the resource after reading EXIF data explicitly" into nyc-dev
am: 976bbac

* commit '976bbaccc47daa700a9ac2fa4fe9b0b759cf343a':
  ExifInterface: close the resource after reading EXIF data explicitly
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 83a6c74..a5b3179 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -651,6 +651,7 @@
     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 @@
         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 @@
         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 @@
             mAssetInputStream = null;
             mSeekableFileDescriptor = null;
         }
+        mIsInputStream = true;
         loadAttributes(inputStream);
     }
 
@@ -800,32 +823,32 @@
      * 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;
-            }
-        } else {
-            in = new BufferedInputStream(in, JPEG_SIGNATURE_SIZE);
-            if (!isJpegInputStream((BufferedInputStream) in) && handleRawResult(
-                    nativeGetRawAttributesFromInputStream(in))) {
-                return;
-            }
-        }
-
-        // Process JPEG input stream
         try {
+            // 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;
+                }
+            } else {
+                in = new BufferedInputStream(in, JPEG_SIGNATURE_SIZE);
+                if (!isJpegInputStream((BufferedInputStream) in) && handleRawResult(
+                        nativeGetRawAttributesFromInputStream(in))) {
+                    return;
+                }
+            }
+
+            // 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 @@
             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 @@
                     break;
             }
         }
-
-        if (DEBUG) {
-            printAttributes();
-        }
         return true;
     }
 
@@ -917,7 +936,7 @@
             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 @@
             } 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.