ExifInterface: do not throw an Exception when XMP
In JPEG, an APP1 section can contain EXIF or XMP. The old code throws an
Exception when XMP APP1 segment is showns.
Bug: 27580432
Change-Id: If42197ea0c33ec4446302c969b42afd3d4b4e3c3
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index e35fcf6..2b944b6 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -605,8 +605,9 @@
// not only getting information from EXIF but also from some JPEG special segments such as
// MARKER_COM for user comment and MARKER_SOFx for image width and height.
- // Identifier for APP1 segment in JPEG
- private static final byte[] IDENTIFIER_APP1 = "Exif\0\0".getBytes(Charset.forName("US-ASCII"));
+ // Identifier for EXIF APP1 segment in JPEG
+ private static final byte[] IDENTIFIER_EXIF_APP1 =
+ "Exif\0\0".getBytes(Charset.forName("US-ASCII"));
// JPEG segment markers, that each marker consumes two bytes beginning with 0xff and ending with
// the indicator. There is no SOF4, SOF8, SOF16 markers in JPEG and SOFx markers indicates start
// of frame(baseline DCT) and the image size info exists in its beginning part.
@@ -1119,7 +1120,9 @@
String time = getAttribute(TAG_GPS_TIMESTAMP);
if (date == null || time == null
|| (!sNonZeroTimePattern.matcher(date).matches()
- && !sNonZeroTimePattern.matcher(time).matches())) return -1;
+ && !sNonZeroTimePattern.matcher(time).matches())) {
+ return -1;
+ }
String dateTimeString = date + ' ' + time;
@@ -1170,7 +1173,6 @@
DataInputStream dataInputStream = new DataInputStream(inputStream);
byte marker;
int bytesRead = 0;
- ++bytesRead;
if ((marker = dataInputStream.readByte()) != MARKER) {
throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
}
@@ -1178,8 +1180,8 @@
if (dataInputStream.readByte() != MARKER_SOI) {
throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
}
+ ++bytesRead;
while (true) {
- ++bytesRead;
marker = dataInputStream.readByte();
if (marker != MARKER) {
throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff));
@@ -1189,36 +1191,40 @@
if (DEBUG) {
Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff));
}
+ ++bytesRead;
// EOI indicates the end of an image and in case of SOS, JPEG image stream starts and
// the image data will terminate right after.
if (marker == MARKER_EOI || marker == MARKER_SOS) {
break;
}
- bytesRead += 2;
int length = dataInputStream.readUnsignedShort() - 2;
+ bytesRead += 2;
+ if (DEBUG) {
+ Log.d(TAG, "JPEG segment: " + marker + " (length: " + (length + 2) + ")");
+ }
if (length < 0) {
throw new IOException("Invalid length");
}
- bytesRead += length;
switch (marker) {
case MARKER_APP1: {
if (DEBUG) {
Log.d(TAG, "MARKER_APP1");
}
- bytesRead -= length;
if (length < 6) {
- throw new IOException("Invalid exif");
+ // Skip if it's not an EXIF APP1 segment.
+ break;
}
byte[] identifier = new byte[6];
if (inputStream.read(identifier) != 6) {
throw new IOException("Invalid exif");
}
- if (!Arrays.equals(identifier, IDENTIFIER_APP1)) {
- throw new IOException("Invalid app1 identifier");
- }
bytesRead += 6;
length -= 6;
+ if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
+ // Skip if it's not an EXIF APP1 segment.
+ break;
+ }
if (length <= 0) {
throw new IOException("Invalid exif");
}
@@ -1240,6 +1246,7 @@
if (dataInputStream.read(bytes) != length) {
throw new IOException("Invalid exif");
}
+ length = 0;
setAttribute("UserComment", new String(bytes, Charset.forName("US-ASCII")));
break;
}
@@ -1273,6 +1280,7 @@
throw new IOException("Invalid length");
}
dataInputStream.skipBytes(length);
+ bytesRead += length;
}
}
@@ -1286,68 +1294,84 @@
}
DataInputStream dataInputStream = new DataInputStream(inputStream);
ExifDataOutputStream dataOutputStream = new ExifDataOutputStream(outputStream);
- int bytesRead = 0;
- ++bytesRead;
if (dataInputStream.readByte() != MARKER) {
throw new IOException("Invalid marker");
}
dataOutputStream.writeByte(MARKER);
- ++bytesRead;
if (dataInputStream.readByte() != MARKER_SOI) {
throw new IOException("Invalid marker");
}
dataOutputStream.writeByte(MARKER_SOI);
+ // Write EXIF APP1 segment
+ dataOutputStream.writeByte(MARKER);
+ dataOutputStream.writeByte(MARKER_APP1);
+ writeExifSegment(dataOutputStream, 6);
+
byte[] bytes = new byte[4096];
while (true) {
- ++bytesRead;
if (dataInputStream.readByte() != MARKER) {
throw new IOException("Invalid marker");
}
- dataOutputStream.writeByte(MARKER);
- ++bytesRead;
byte marker = dataInputStream.readByte();
- dataOutputStream.writeByte(marker);
switch (marker) {
case MARKER_APP1: {
- // Rewrite EXIF segment
int length = dataInputStream.readUnsignedShort() - 2;
if (length < 0) {
throw new IOException("Invalid length");
}
- bytesRead += 2;
+ byte[] identifier = new byte[6];
+ if (length >= 6) {
+ if (dataInputStream.read(identifier) != 6) {
+ throw new IOException("Invalid exif");
+ }
+ if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
+ // Skip the original EXIF APP1 segment.
+ if (dataInputStream.skip(length - 6) != length - 6) {
+ throw new IOException("Invalid length");
+ }
+ break;
+ }
+ }
+ // Copy non-EXIF APP1 segment.
+ dataOutputStream.writeUnsignedShort(length + 2);
+ if (length >= 6) {
+ length -= 6;
+ dataOutputStream.write(identifier);
+ }
int read;
- while ((read = dataInputStream.read(
- bytes, 0, Math.min(length, bytes.length))) > 0) {
+ while (length > 0 && (read = dataInputStream.read(
+ bytes, 0, Math.min(length, bytes.length))) >= 0) {
+ dataOutputStream.write(bytes, 0, read);
length -= read;
}
- bytesRead += length;
- writeExifSegment(dataOutputStream, bytesRead);
break;
}
case MARKER_EOI:
case MARKER_SOS: {
+ dataOutputStream.writeByte(MARKER);
+ dataOutputStream.writeByte(marker);
// Copy all the remaining data
Streams.copy(dataInputStream, dataOutputStream);
return;
}
default: {
// Copy JPEG segment
+ dataOutputStream.writeByte(MARKER);
+ dataOutputStream.writeByte(marker);
int length = dataInputStream.readUnsignedShort();
dataOutputStream.writeUnsignedShort(length);
+ length -= 2;
if (length < 0) {
throw new IOException("Invalid length");
}
- length -= 2;
- bytesRead += 2;
int read;
- while ((read = dataInputStream.read(
- bytes, 0, Math.min(length, bytes.length))) > 0) {
+ while (length > 0 && (read = dataInputStream.read(
+ bytes, 0, Math.min(length, bytes.length))) >= 0) {
dataOutputStream.write(bytes, 0, read);
length -= read;
}
- bytesRead += length;
break;
}
}
@@ -1918,7 +1942,7 @@
// Write TIFF Headers. See JEITA CP-3451C Table 1. page 10.
dataOutputStream.writeUnsignedShort(totalSize);
- dataOutputStream.write(IDENTIFIER_APP1);
+ dataOutputStream.write(IDENTIFIER_EXIF_APP1);
dataOutputStream.writeShort(BYTE_ALIGN_MM);
dataOutputStream.writeUnsignedShort(0x2a);
dataOutputStream.writeUnsignedInt(8);