From cf6255422bf2751d160c1384895ddae3eeea66b4 Mon Sep 17 00:00:00 2001 From: Earl Ou Date: Wed, 28 Nov 2012 13:58:30 +0800 Subject: ExifModifier Change-Id: I001b24d8ba004a4080cd898f0d26d706b1839425 --- .../gallery3d/exif/ByteBufferInputStream.java | 48 ++++++ .../src/com/android/gallery3d/exif/ExifData.java | 8 + .../com/android/gallery3d/exif/ExifModifier.java | 186 +++++++++++++++++++++ .../src/com/android/gallery3d/exif/ExifParser.java | 101 ++++++----- .../src/com/android/gallery3d/exif/ExifTag.java | 6 +- .../src/com/android/gallery3d/exif/IfdData.java | 7 + .../android/gallery3d/exif/ExifModifierTest.java | 172 +++++++++++++++++++ .../gallery3d/exif/ExifOutputStreamTest.java | 3 +- .../com/android/gallery3d/exif/ExifTestRunner.java | 1 + 9 files changed, 483 insertions(+), 49 deletions(-) create mode 100644 gallerycommon/src/com/android/gallery3d/exif/ByteBufferInputStream.java create mode 100644 gallerycommon/src/com/android/gallery3d/exif/ExifModifier.java create mode 100644 tests/src/com/android/gallery3d/exif/ExifModifierTest.java diff --git a/gallerycommon/src/com/android/gallery3d/exif/ByteBufferInputStream.java b/gallerycommon/src/com/android/gallery3d/exif/ByteBufferInputStream.java new file mode 100644 index 000000000..7fb9f22cc --- /dev/null +++ b/gallerycommon/src/com/android/gallery3d/exif/ByteBufferInputStream.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.exif; + +import java.io.InputStream; +import java.nio.ByteBuffer; + +class ByteBufferInputStream extends InputStream { + + private ByteBuffer mBuf; + + public ByteBufferInputStream(ByteBuffer buf) { + mBuf = buf; + } + + @Override + public int read() { + if (!mBuf.hasRemaining()) { + return -1; + } + return mBuf.get() & 0xFF; + } + + @Override + public int read(byte[] bytes, int off, int len) { + if (!mBuf.hasRemaining()) { + return -1; + } + + len = Math.min(len, mBuf.remaining()); + mBuf.get(bytes, off, len); + return len; + } +} diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifData.java b/gallerycommon/src/com/android/gallery3d/exif/ExifData.java index 6e5c227d5..b226dd4fe 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifData.java +++ b/gallerycommon/src/com/android/gallery3d/exif/ExifData.java @@ -286,6 +286,14 @@ public class ExifData { return tag; } + /** + * Adds the given ExifTag to its corresponding IFD. + */ + public void addTag(ExifTag tag) { + IfdData ifdData = getOrCreateIfdData(tag.getIfd()); + ifdData.setTag(tag); + } + /** * Adds a thumbnail-related tag with the given tag ID. If the tag of the given ID * already exists, the original tag will be returned. Otherwise, a new ExifTag will diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifModifier.java b/gallerycommon/src/com/android/gallery3d/exif/ExifModifier.java new file mode 100644 index 000000000..da31a29db --- /dev/null +++ b/gallerycommon/src/com/android/gallery3d/exif/ExifModifier.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.exif; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; + +public class ExifModifier { + private final ByteBuffer mByteBuffer; + private final ExifData mTagToModified; + private final List mTagOffsets = new ArrayList(); + private int mOffsetBase; + + private static class TagOffset { + final int mOffset; + final ExifTag mTag; + + TagOffset(ExifTag tag, int offset) { + mTag = tag; + mOffset = offset; + } + + public ExifTag getTag() { + return mTag; + } + } + + public ExifModifier(ByteBuffer byteBuffer) throws IOException, ExifInvalidFormatException { + mByteBuffer = byteBuffer; + mOffsetBase = byteBuffer.position(); + InputStream is = null; + try { + is = new ByteBufferInputStream(byteBuffer); + // Do not require any IFD; + ExifParser parser = ExifParser.parse(is, 0); + mTagToModified = new ExifData(parser.getByteOrder()); + mOffsetBase += parser.getTiffStartPosition(); + mByteBuffer.position(0); + } finally { + closeSilently(is); + } + } + + public ByteOrder getByteOrder() { + return mTagToModified.getByteOrder(); + } + + public boolean commit() throws IOException, ExifInvalidFormatException { + InputStream is = null; + try { + is = new ByteBufferInputStream(mByteBuffer); + int flag = 0; + IfdData[] ifdDatas = new IfdData[] { + mTagToModified.getIfdData(IfdId.TYPE_IFD_0), + mTagToModified.getIfdData(IfdId.TYPE_IFD_1), + mTagToModified.getIfdData(IfdId.TYPE_IFD_EXIF), + mTagToModified.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY), + mTagToModified.getIfdData(IfdId.TYPE_IFD_GPS) + }; + + if (ifdDatas[IfdId.TYPE_IFD_0] != null) flag |= ExifParser.OPTION_IFD_0; + if (ifdDatas[IfdId.TYPE_IFD_1] != null) flag |= ExifParser.OPTION_IFD_1; + if (ifdDatas[IfdId.TYPE_IFD_EXIF] != null) flag |= ExifParser.OPTION_IFD_EXIF; + if (ifdDatas[IfdId.TYPE_IFD_GPS] != null) flag |= ExifParser.OPTION_IFD_GPS; + if (ifdDatas[IfdId.TYPE_IFD_INTEROPERABILITY] != null) { + flag |= ExifParser.OPTION_IFD_INTEROPERABILITY; + } + + ExifParser parser = ExifParser.parse(is, flag); + int event = parser.next(); + IfdData currIfd = null; + while (event != ExifParser.EVENT_END) { + switch (event) { + case ExifParser.EVENT_START_OF_IFD: + currIfd = ifdDatas[parser.getCurrentIfd()]; + if (currIfd == null) parser.skipRemainingTagsInCurrentIfd(); + break; + case ExifParser.EVENT_NEW_TAG: + ExifTag oldTag = parser.getTag(); + ExifTag newTag = currIfd.getTag(oldTag.getTagId()); + if (newTag != null) { + if (newTag.getComponentCount() != oldTag.getComponentCount() + || newTag.getDataType() != oldTag.getDataType()) { + return false; + } else { + mTagOffsets.add(new TagOffset(newTag, oldTag.getOffset())); + currIfd.removeTag(oldTag.getTagId()); + if (currIfd.getTagCount() == 0) { + parser.skipRemainingTagsInCurrentIfd(); + } + } + } + break; + } + event = parser.next(); + } + for (IfdData ifd: ifdDatas) { + if (ifd != null && ifd.getTagCount() > 0) return false; + } + modify(); + } finally { + closeSilently(is); + } + return true; + } + + private void modify() { + mByteBuffer.order(getByteOrder()); + for (TagOffset tagOffset: mTagOffsets) { + writeTagValue(tagOffset.mTag, tagOffset.mOffset); + } + } + + private void writeTagValue(ExifTag tag, int offset) { + mByteBuffer.position(offset + mOffsetBase); + switch (tag.getDataType()) { + case ExifTag.TYPE_ASCII: + byte buf[] = tag.getStringByte(); + if (buf.length == tag.getComponentCount()) { + buf[buf.length - 1] = 0; + mByteBuffer.put(buf); + } else { + mByteBuffer.put(buf); + mByteBuffer.put((byte) 0); + } + break; + case ExifTag.TYPE_LONG: + case ExifTag.TYPE_UNSIGNED_LONG: + for (int i = 0, n = tag.getComponentCount(); i < n; i++) { + mByteBuffer.putInt((int) tag.getValueAt(i)); + } + break; + case ExifTag.TYPE_RATIONAL: + case ExifTag.TYPE_UNSIGNED_RATIONAL: + for (int i = 0, n = tag.getComponentCount(); i < n; i++) { + Rational v = tag.getRational(i); + mByteBuffer.putInt((int) v.getNominator()); + mByteBuffer.putInt((int) v.getDenominator()); + } + break; + case ExifTag.TYPE_UNDEFINED: + case ExifTag.TYPE_UNSIGNED_BYTE: + buf = new byte[tag.getComponentCount()]; + tag.getBytes(buf); + mByteBuffer.put(buf); + break; + case ExifTag.TYPE_UNSIGNED_SHORT: + for (int i = 0, n = tag.getComponentCount(); i < n; i++) { + mByteBuffer.putShort((short) tag.getValueAt(i)); + } + break; + } + } + + public void modifyTag(ExifTag tag) { + mTagToModified.addTag(tag); + } + + private static void closeSilently(Closeable c) { + if (c == null) return; + try { + c.close(); + } catch (Throwable t) { + // do nothing + } + } +} diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifParser.java b/gallerycommon/src/com/android/gallery3d/exif/ExifParser.java index 2cff12a3d..a1ce13e69 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifParser.java +++ b/gallerycommon/src/com/android/gallery3d/exif/ExifParser.java @@ -18,7 +18,6 @@ package com.android.gallery3d.exif; import android.util.Log; -import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteOrder; @@ -154,6 +153,7 @@ public class ExifParser { private int mApp1End; private byte[] mDataAboveIfd0; private int mIfd0Position; + private int mTiffStartPosition; private final TreeMap mCorrespondingEvent = new TreeMap(); @@ -190,10 +190,13 @@ public class ExifParser { throw new ExifInvalidFormatException("Invalid offset " + offset); } mIfd0Position = (int) offset; - registerIfd(IfdId.TYPE_IFD_0, offset); - if (offset != DEFAULT_IFD0_OFFSET) { - mDataAboveIfd0 = new byte[(int) offset - DEFAULT_IFD0_OFFSET]; - read(mDataAboveIfd0); + mIfdType = IfdId.TYPE_IFD_0; + if (isIfdRequested(IfdId.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) { + registerIfd(IfdId.TYPE_IFD_0, offset); + if (offset != DEFAULT_IFD0_OFFSET) { + mDataAboveIfd0 = new byte[(int) offset - DEFAULT_IFD0_OFFSET]; + read(mDataAboveIfd0); + } } } @@ -203,8 +206,8 @@ public class ExifParser { * @exception ExifInvalidFormatException */ public static ExifParser parse(InputStream inputStream, int options) - throws IOException, ExifInvalidFormatException { - return new ExifParser(inputStream, options); + throws IOException, ExifInvalidFormatException { + return new ExifParser(inputStream, options); } /** @@ -353,7 +356,8 @@ public class ExifParser { switch (mIfdType) { case IfdId.TYPE_IFD_0: return isIfdRequested(IfdId.TYPE_IFD_EXIF) || isIfdRequested(IfdId.TYPE_IFD_GPS) - || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY); + || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY) + || isIfdRequested(IfdId.TYPE_IFD_1); case IfdId.TYPE_IFD_1: return isThumbnailRequested(); case IfdId.TYPE_IFD_EXIF: @@ -515,6 +519,8 @@ public class ExifParser { } else { readFullTagValue(tag); mTiffStream.skip(4 - dataSize); + // Set the offset to the position of value. + tag.setOffset(mTiffStream.getReadByteCount() - 4); } return tag; } @@ -617,42 +623,42 @@ public class ExifParser { tag.setValue(value); } break; - case ExifTag.TYPE_UNSIGNED_RATIONAL: - { - Rational value[] = new Rational[tag.getComponentCount()]; - for (int i = 0, n = value.length; i < n; i++) { - value[i] = readUnsignedRational(); - } - tag.setValue(value); - } - break; - case ExifTag.TYPE_UNSIGNED_SHORT: - { - int value[] = new int[tag.getComponentCount()]; - for (int i = 0, n = value.length; i < n; i++) { - value[i] = readUnsignedShort(); - } - tag.setValue(value); - } - break; - case ExifTag.TYPE_LONG: - { - int value[] = new int[tag.getComponentCount()]; - for (int i = 0, n = value.length; i < n; i++) { - value[i] = readLong(); - } - tag.setValue(value); - } - break; - case ExifTag.TYPE_RATIONAL: - { - Rational value[] = new Rational[tag.getComponentCount()]; - for (int i = 0, n = value.length; i < n; i++) { - value[i] = readRational(); - } - tag.setValue(value); - } - break; + case ExifTag.TYPE_UNSIGNED_RATIONAL: + { + Rational value[] = new Rational[tag.getComponentCount()]; + for (int i = 0, n = value.length; i < n; i++) { + value[i] = readUnsignedRational(); + } + tag.setValue(value); + } + break; + case ExifTag.TYPE_UNSIGNED_SHORT: + { + int value[] = new int[tag.getComponentCount()]; + for (int i = 0, n = value.length; i < n; i++) { + value[i] = readUnsignedShort(); + } + tag.setValue(value); + } + break; + case ExifTag.TYPE_LONG: + { + int value[] = new int[tag.getComponentCount()]; + for (int i = 0, n = value.length; i < n; i++) { + value[i] = readLong(); + } + tag.setValue(value); + } + break; + case ExifTag.TYPE_RATIONAL: + { + Rational value[] = new Rational[tag.getComponentCount()]; + for (int i = 0, n = value.length; i < n; i++) { + value[i] = readRational(); + } + tag.setValue(value); + } + break; } } @@ -675,7 +681,7 @@ public class ExifParser { private boolean seekTiffData(InputStream inputStream) throws IOException, ExifInvalidFormatException { - DataInputStream dataStream = new DataInputStream(inputStream); + CountedDataInputStream dataStream = new CountedDataInputStream(inputStream); if (dataStream.readShort() != JpegHeader.SOI) { throw new ExifInvalidFormatException("Invalid JPEG format"); @@ -695,6 +701,7 @@ public class ExifParser { headerTail = dataStream.readShort(); length -= 6; if (header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) { + mTiffStartPosition = dataStream.getReadByteCount(); mApp1End = length; return true; } @@ -709,6 +716,10 @@ public class ExifParser { return false; } + int getTiffStartPosition() { + return mTiffStartPosition; + } + /** * Reads bytes from the InputStream. */ diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java b/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java index cda67c2e2..753b18ce8 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java +++ b/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java @@ -915,9 +915,9 @@ public class ExifTag { static boolean isValidType(short type) { return type == TYPE_UNSIGNED_BYTE || type == TYPE_ASCII || - type == TYPE_UNSIGNED_SHORT || type == TYPE_UNSIGNED_LONG || - type == TYPE_UNSIGNED_RATIONAL || type == TYPE_UNDEFINED || - type == TYPE_LONG || type == TYPE_RATIONAL; + type == TYPE_UNSIGNED_SHORT || type == TYPE_UNSIGNED_LONG || + type == TYPE_UNSIGNED_RATIONAL || type == TYPE_UNDEFINED || + type == TYPE_LONG || type == TYPE_RATIONAL; } ExifTag(short tagId, short type, int componentCount, int ifd) { diff --git a/gallerycommon/src/com/android/gallery3d/exif/IfdData.java b/gallerycommon/src/com/android/gallery3d/exif/IfdData.java index 78f9173cc..633604940 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/IfdData.java +++ b/gallerycommon/src/com/android/gallery3d/exif/IfdData.java @@ -78,6 +78,13 @@ class IfdData { mExifTags.put(tag.getTagId(), tag); } + /** + * Removes the tag of the given ID + */ + public void removeTag(short tagId) { + mExifTags.remove(tagId); + } + /** * Gets the tags count in the IFD. */ diff --git a/tests/src/com/android/gallery3d/exif/ExifModifierTest.java b/tests/src/com/android/gallery3d/exif/ExifModifierTest.java new file mode 100644 index 000000000..14e956fd3 --- /dev/null +++ b/tests/src/com/android/gallery3d/exif/ExifModifierTest.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.gallery3d.exif; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel.MapMode; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ExifModifierTest extends ExifXmlDataTestCase { + + private File mTmpFile; + private List>> mGroundTruth; + + // TYPE_UNDEFINED with 4 components + private static final ExifTag sVersionTag = ExifTag.buildTag(ExifTag.TAG_EXIF_VERSION); + // TYPE_UNSIGNED_BYTE with 4 components + private static final ExifTag sGpsVersionTag = ExifTag.buildTag(ExifTag.TAG_GPS_VERSION_ID); + // TYPE ASCII with arbitary length + private static final ExifTag sModelTag = ExifTag.buildTag(ExifTag.TAG_MODEL); + // TYPE_ASCII with 20 components + private static final ExifTag sDateTimeTag = ExifTag.buildTag(ExifTag.TAG_DATE_TIME); + // TYPE_UNSIGNED_SHORT with 1 components + private static final ExifTag sCompressionTag = ExifTag.buildTag(ExifTag.TAG_COMPRESSION); + // TYPE_UNSIGNED_LONG with 1 components + private static final ExifTag sThumbnailFormatTag = + ExifTag.buildTag(ExifTag.TAG_JPEG_INTERCHANGE_FORMAT); + // TYPE_UNSIGNED_RATIONAL with 3 components + private static final ExifTag sLongitudeTag = ExifTag.buildTag(ExifTag.TAG_GPS_LONGITUDE); + // TYPE_RATIONAL with 1 components + private static final ExifTag sShutterTag = ExifTag.buildTag(ExifTag.TAG_SHUTTER_SPEED_VALUE); + + private static final Map sTestTags = new HashMap(); + + static { + sVersionTag.setValue(new byte[] {1, 2, 3, 4}); + sGpsVersionTag.setValue(new byte[] {4, 3, 2, 1}); + sModelTag.setValue("end-of-the-world"); + sDateTimeTag.setValue("2012:12:31 23:59:59"); + sCompressionTag.setValue(100); + sThumbnailFormatTag.setValue(100); + sLongitudeTag.setValue(new Rational[] {new Rational(1, 1), new Rational(10, 10), + new Rational(100, 100)}); + sShutterTag.setValue(new Rational(1, 1)); + + sTestTags.put(sVersionTag.getTagId(), sVersionTag); + sTestTags.put(sGpsVersionTag.getTagId(), sGpsVersionTag); + sTestTags.put(sModelTag.getTagId(), sModelTag); + sTestTags.put(sDateTimeTag.getTagId(), sDateTimeTag); + sTestTags.put(sCompressionTag.getTagId(), sCompressionTag); + sTestTags.put(sThumbnailFormatTag.getTagId(), sThumbnailFormatTag); + sTestTags.put(sLongitudeTag.getTagId(), sLongitudeTag); + sTestTags.put(sShutterTag.getTagId(), sShutterTag); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + mGroundTruth = ExifXmlReader.readXml(getXmlParser()); + mTmpFile = File.createTempFile("exif_test", ".jpg"); + FileOutputStream os = null; + InputStream is = getImageInputStream(); + try { + os = new FileOutputStream(mTmpFile); + byte[] buf = new byte[1024]; + int n; + while ((n = is.read(buf)) > 0) { + os.write(buf, 0, n); + } + } finally { + Util.closeSilently(os); + } + } + + public ExifModifierTest(int imageRes, int xmlRes) { + super(imageRes, xmlRes); + } + + public ExifModifierTest(String imagePath, String xmlPath) { + super(imagePath, xmlPath); + } + + public void testModify() throws Exception { + Map results = new HashMap(); + + RandomAccessFile file = null; + try { + file = new RandomAccessFile(mTmpFile, "rw"); + MappedByteBuffer buf = file.getChannel().map(MapMode.READ_WRITE, 0, file.length()); + for (ExifTag tag: sTestTags.values()) { + ExifModifier modifier = new ExifModifier(buf); + modifier.modifyTag(tag); + boolean result = modifier.commit(); + results.put(tag.getTagId(), result); + buf.force(); + buf.position(0); + + if (!result) { + List value = mGroundTruth.get(tag.getIfd()).get(tag.getTagId()); + assertTrue (String.format("Tag %x, ", tag.getTagId()) + getImageTitle(), + value == null || tag.getTagId() == ExifTag.TAG_MODEL); + } + } + } finally { + Util.closeSilently(file); + } + + // Parse the new file and check the result + InputStream is = null; + try { + is = new FileInputStream(mTmpFile); + ExifData data = new ExifReader().read(is); + for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) { + checkIfd(data.getIfdData(i), mGroundTruth.get(i), results); + } + } finally { + Util.closeSilently(is); + } + + } + + + private void checkIfd(IfdData ifd, Map> ifdValue, + Map results) { + if (ifd == null) { + assertEquals(getImageTitle(), 0 ,ifdValue.size()); + return; + } + ExifTag[] tags = ifd.getAllTags(); + for (ExifTag tag : tags) { + List truth = ifdValue.get(tag.getTagId()); + assertNotNull(String.format("Tag %x, ", tag.getTagId()) + getImageTitle(), truth); + if (truth.contains(null)) continue; + + ExifTag newTag = sTestTags.get(tag.getTagId()); + if (newTag != null + && results.get(tag.getTagId())) { + assertEquals(String.format("Tag %x, ", tag.getTagId()) + getImageTitle(), + Util.tagValueToString(newTag), Util.tagValueToString(tag)); + } else { + assertTrue(String.format("Tag %x, ", tag.getTagId()) + getImageTitle(), + truth.contains(Util.tagValueToString(tag).trim())); + } + } + assertEquals(getImageTitle(), ifdValue.size(), tags.length); + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + mTmpFile.delete(); + } +} diff --git a/tests/src/com/android/gallery3d/exif/ExifOutputStreamTest.java b/tests/src/com/android/gallery3d/exif/ExifOutputStreamTest.java index 8c98fa037..789fd3563 100644 --- a/tests/src/com/android/gallery3d/exif/ExifOutputStreamTest.java +++ b/tests/src/com/android/gallery3d/exif/ExifOutputStreamTest.java @@ -103,7 +103,8 @@ public class ExifOutputStreamTest extends ExifXmlDataTestCase { } @Override - public void tearDown() { + public void tearDown() throws Exception { + super.tearDown(); mTmpFile.delete(); } } diff --git a/tests/src/com/android/gallery3d/exif/ExifTestRunner.java b/tests/src/com/android/gallery3d/exif/ExifTestRunner.java index 519e87107..b09426328 100644 --- a/tests/src/com/android/gallery3d/exif/ExifTestRunner.java +++ b/tests/src/com/android/gallery3d/exif/ExifTestRunner.java @@ -56,6 +56,7 @@ public class ExifTestRunner extends InstrumentationTestRunner { addAllTestsFromExifTestCase(ExifParserTest.class, suite); addAllTestsFromExifTestCase(ExifReaderTest.class, suite); addAllTestsFromExifTestCase(ExifOutputStreamTest.class, suite); + addAllTestsFromExifTestCase(ExifModifierTest.class, suite); return suite; } -- cgit v1.2.3