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
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 767b67b..d237975 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1457,6 +1457,9 @@
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 @@
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 @@
}
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) {