From f2af1112fa72329e4ffc616f6184a084868b037e Mon Sep 17 00:00:00 2001 From: Jin Seok Park Date: Thu, 6 Feb 2020 23:26:07 +0900 Subject: [PATCH] Fix bug where JPEG file with XMP is incorrectly saved Currently, even if a JPEG file has an XMP data stored separately from the EXIF data, it saves it as an EXIF tag, and when ExifInterface#saveAttributes is called, the XMP data is stored inside the EXIF data. This can cause a problem since the maximum bytes that an EXIF data can hold inside a JPEG file is 65536 bytes, and having the XMP data inside the EXIF data can make the EXIF data exceed this limit. This CL solves this issue by separately saving the EXIF data if it existed separately in the original file. Bug: 147778520, Bug: 149010485 Test: manually with file attached in bug Change-Id: I320942858beb14cc3b2e80330857089a0ad26533 --- media/java/android/media/ExifInterface.java | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index 767b67b7e8857..d2379757f2268 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -1457,6 +1457,9 @@ public class ExifInterface { private int mRw2JpgFromRawOffset; private boolean mIsSupportedFile; private boolean mModified; + // XMP data can be contained as either part of the EXIF data (tag number 700), or as a + // separate data marker (a separate MARKER_APP1). + private boolean mXmpIsFromSeparateMarker; // Pattern to check non zero timestamp private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*"); @@ -2837,10 +2840,12 @@ public class ExifInterface { final long offset = start + IDENTIFIER_XMP_APP1.length; final byte[] value = Arrays.copyOfRange(bytes, IDENTIFIER_XMP_APP1.length, bytes.length); - + // TODO: check if ignoring separate XMP data when tag 700 already exists is + // valid. if (getAttribute(TAG_XMP) == null) { mAttributes[IFD_TYPE_PRIMARY].put(TAG_XMP, new ExifAttribute( IFD_FORMAT_BYTE, value.length, offset, value)); + mXmpIsFromSeparateMarker = true; } } break; @@ -3445,11 +3450,24 @@ public class ExifInterface { } dataOutputStream.writeByte(MARKER_SOI); + // Remove XMP data if it is from a separate marker (IDENTIFIER_XMP_APP1, not + // IDENTIFIER_EXIF_APP1) + // Will re-add it later after the rest of the file is written + ExifAttribute xmpAttribute = null; + if (getAttribute(TAG_XMP) != null && mXmpIsFromSeparateMarker) { + xmpAttribute = (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].remove(TAG_XMP); + } + // Write EXIF APP1 segment dataOutputStream.writeByte(MARKER); dataOutputStream.writeByte(MARKER_APP1); writeExifSegment(dataOutputStream); + // Re-add previously removed XMP data. + if (xmpAttribute != null) { + mAttributes[IFD_TYPE_PRIMARY].put(TAG_XMP, xmpAttribute); + } + byte[] bytes = new byte[4096]; while (true) {