1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.mms.exif;
18 
19 import com.android.mms.LogTag;
20 
21 import android.util.Log;
22 
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.nio.ByteOrder;
26 import java.nio.charset.Charset;
27 import java.util.Map.Entry;
28 import java.util.TreeMap;
29 
30 /**
31  * This class provides a low-level EXIF parsing API. Given a JPEG format
32  * InputStream, the caller can request which IFD's to read via
33  * {@link #parse(InputStream, int)} with given options.
34  * <p>
35  * Below is an example of getting EXIF data from IFD 0 and EXIF IFD using the
36  * parser.
37  *
38  * <pre>
39  * void parse() {
40  *     ExifParser parser = ExifParser.parse(mImageInputStream,
41  *             ExifParser.OPTION_IFD_0 | ExifParser.OPTIONS_IFD_EXIF);
42  *     int event = parser.next();
43  *     while (event != ExifParser.EVENT_END) {
44  *         switch (event) {
45  *             case ExifParser.EVENT_START_OF_IFD:
46  *                 break;
47  *             case ExifParser.EVENT_NEW_TAG:
48  *                 ExifTag tag = parser.getTag();
49  *                 if (!tag.hasValue()) {
50  *                     parser.registerForTagValue(tag);
51  *                 } else {
52  *                     processTag(tag);
53  *                 }
54  *                 break;
55  *             case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
56  *                 tag = parser.getTag();
57  *                 if (tag.getDataType() != ExifTag.TYPE_UNDEFINED) {
58  *                     processTag(tag);
59  *                 }
60  *                 break;
61  *         }
62  *         event = parser.next();
63  *     }
64  * }
65  *
66  * void processTag(ExifTag tag) {
67  *     // process the tag as you like.
68  * }
69  * </pre>
70  */
71 public class ExifParser {
72     private static final boolean LOGV = false;
73     private static final String TAG = LogTag.TAG;
74     /**
75      * When the parser reaches a new IFD area. Call {@link #getCurrentIfd()} to
76      * know which IFD we are in.
77      */
78     public static final int EVENT_START_OF_IFD = 0;
79     /**
80      * When the parser reaches a new tag. Call {@link #getTag()}to get the
81      * corresponding tag.
82      */
83     public static final int EVENT_NEW_TAG = 1;
84     /**
85      * When the parser reaches the value area of tag that is registered by
86      * {@link #registerForTagValue(ExifTag)} previously. Call {@link #getTag()}
87      * to get the corresponding tag.
88      */
89     public static final int EVENT_VALUE_OF_REGISTERED_TAG = 2;
90 
91     /**
92      * When the parser reaches the compressed image area.
93      */
94     public static final int EVENT_COMPRESSED_IMAGE = 3;
95     /**
96      * When the parser reaches the uncompressed image strip. Call
97      * {@link #getStripIndex()} to get the index of the strip.
98      *
99      * @see #getStripIndex()
100      * @see #getStripCount()
101      */
102     public static final int EVENT_UNCOMPRESSED_STRIP = 4;
103     /**
104      * When there is nothing more to parse.
105      */
106     public static final int EVENT_END = 5;
107 
108     /**
109      * Option bit to request to parse IFD0.
110      */
111     public static final int OPTION_IFD_0 = 1 << 0;
112     /**
113      * Option bit to request to parse IFD1.
114      */
115     public static final int OPTION_IFD_1 = 1 << 1;
116     /**
117      * Option bit to request to parse Exif-IFD.
118      */
119     public static final int OPTION_IFD_EXIF = 1 << 2;
120     /**
121      * Option bit to request to parse GPS-IFD.
122      */
123     public static final int OPTION_IFD_GPS = 1 << 3;
124     /**
125      * Option bit to request to parse Interoperability-IFD.
126      */
127     public static final int OPTION_IFD_INTEROPERABILITY = 1 << 4;
128     /**
129      * Option bit to request to parse thumbnail.
130      */
131     public static final int OPTION_THUMBNAIL = 1 << 5;
132 
133     protected static final int EXIF_HEADER = 0x45786966; // EXIF header "Exif"
134     protected static final short EXIF_HEADER_TAIL = (short) 0x0000; // EXIF header in APP1
135 
136     // TIFF header
137     protected static final short LITTLE_ENDIAN_TAG = (short) 0x4949; // "II"
138     protected static final short BIG_ENDIAN_TAG = (short) 0x4d4d; // "MM"
139     protected static final short TIFF_HEADER_TAIL = 0x002A;
140 
141     protected static final int TAG_SIZE = 12;
142     protected static final int OFFSET_SIZE = 2;
143 
144     private static final Charset US_ASCII = Charset.forName("US-ASCII");
145 
146     protected static final int DEFAULT_IFD0_OFFSET = 8;
147 
148     private final CountedDataInputStream mTiffStream;
149     private final int mOptions;
150     private int mIfdStartOffset = 0;
151     private int mNumOfTagInIfd = 0;
152     private int mIfdType;
153     private ExifTag mTag;
154     private ImageEvent mImageEvent;
155     private int mStripCount;
156     private ExifTag mStripSizeTag;
157     private ExifTag mJpegSizeTag;
158     private boolean mNeedToParseOffsetsInCurrentIfd;
159     private boolean mContainExifData = false;
160     private int mApp1End;
161     private int mOffsetToApp1EndFromSOF = 0;
162     private byte[] mDataAboveIfd0;
163     private int mIfd0Position;
164     private int mTiffStartPosition;
165     private final ExifInterface mInterface;
166 
167     private static final short TAG_EXIF_IFD = ExifInterface
168             .getTrueTagKey(ExifInterface.TAG_EXIF_IFD);
169     private static final short TAG_GPS_IFD = ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD);
170     private static final short TAG_INTEROPERABILITY_IFD = ExifInterface
171             .getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD);
172     private static final short TAG_JPEG_INTERCHANGE_FORMAT = ExifInterface
173             .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
174     private static final short TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = ExifInterface
175             .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
176     private static final short TAG_STRIP_OFFSETS = ExifInterface
177             .getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS);
178     private static final short TAG_STRIP_BYTE_COUNTS = ExifInterface
179             .getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS);
180 
181     private final TreeMap<Integer, Object> mCorrespondingEvent = new TreeMap<Integer, Object>();
182 
isIfdRequested(int ifdType)183     private boolean isIfdRequested(int ifdType) {
184         switch (ifdType) {
185             case IfdId.TYPE_IFD_0:
186                 return (mOptions & OPTION_IFD_0) != 0;
187             case IfdId.TYPE_IFD_1:
188                 return (mOptions & OPTION_IFD_1) != 0;
189             case IfdId.TYPE_IFD_EXIF:
190                 return (mOptions & OPTION_IFD_EXIF) != 0;
191             case IfdId.TYPE_IFD_GPS:
192                 return (mOptions & OPTION_IFD_GPS) != 0;
193             case IfdId.TYPE_IFD_INTEROPERABILITY:
194                 return (mOptions & OPTION_IFD_INTEROPERABILITY) != 0;
195         }
196         return false;
197     }
198 
isThumbnailRequested()199     private boolean isThumbnailRequested() {
200         return (mOptions & OPTION_THUMBNAIL) != 0;
201     }
202 
ExifParser(InputStream inputStream, int options, ExifInterface iRef)203     private ExifParser(InputStream inputStream, int options, ExifInterface iRef)
204             throws IOException, ExifInvalidFormatException {
205         if (inputStream == null) {
206             throw new IOException("Null argument inputStream to ExifParser");
207         }
208         if (LOGV) {
209             Log.v(TAG, "Reading exif...");
210         }
211         mInterface = iRef;
212         mContainExifData = seekTiffData(inputStream);
213         mTiffStream = new CountedDataInputStream(inputStream);
214         mOptions = options;
215         if (!mContainExifData) {
216             return;
217         }
218 
219         parseTiffHeader();
220         long offset = mTiffStream.readUnsignedInt();
221         if (offset > Integer.MAX_VALUE) {
222             throw new ExifInvalidFormatException("Invalid offset " + offset);
223         }
224         mIfd0Position = (int) offset;
225         mIfdType = IfdId.TYPE_IFD_0;
226         if (isIfdRequested(IfdId.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) {
227             registerIfd(IfdId.TYPE_IFD_0, offset);
228             if (offset != DEFAULT_IFD0_OFFSET) {
229                 mDataAboveIfd0 = new byte[(int) offset - DEFAULT_IFD0_OFFSET];
230                 read(mDataAboveIfd0);
231             }
232         }
233     }
234 
235     /**
236      * Parses the the given InputStream with the given options
237      *
238      * @exception IOException
239      * @exception ExifInvalidFormatException
240      */
parse(InputStream inputStream, int options, ExifInterface iRef)241     protected static ExifParser parse(InputStream inputStream, int options, ExifInterface iRef)
242             throws IOException, ExifInvalidFormatException {
243         return new ExifParser(inputStream, options, iRef);
244     }
245 
246     /**
247      * Parses the the given InputStream with default options; that is, every IFD
248      * and thumbnaill will be parsed.
249      *
250      * @exception IOException
251      * @exception ExifInvalidFormatException
252      * @see #parse(InputStream, int)
253      */
parse(InputStream inputStream, ExifInterface iRef)254     protected static ExifParser parse(InputStream inputStream, ExifInterface iRef)
255             throws IOException, ExifInvalidFormatException {
256         return new ExifParser(inputStream, OPTION_IFD_0 | OPTION_IFD_1
257                 | OPTION_IFD_EXIF | OPTION_IFD_GPS | OPTION_IFD_INTEROPERABILITY
258                 | OPTION_THUMBNAIL, iRef);
259     }
260 
261     /**
262      * Moves the parser forward and returns the next parsing event
263      *
264      * @exception IOException
265      * @exception ExifInvalidFormatException
266      * @see #EVENT_START_OF_IFD
267      * @see #EVENT_NEW_TAG
268      * @see #EVENT_VALUE_OF_REGISTERED_TAG
269      * @see #EVENT_COMPRESSED_IMAGE
270      * @see #EVENT_UNCOMPRESSED_STRIP
271      * @see #EVENT_END
272      */
next()273     protected int next() throws IOException, ExifInvalidFormatException {
274         if (!mContainExifData) {
275             return EVENT_END;
276         }
277         int offset = mTiffStream.getReadByteCount();
278         int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
279         if (offset < endOfTags) {
280             mTag = readTag();
281             if (mTag == null) {
282                 return next();
283             }
284             if (mNeedToParseOffsetsInCurrentIfd) {
285                 checkOffsetOrImageTag(mTag);
286             }
287             return EVENT_NEW_TAG;
288         } else if (offset == endOfTags) {
289             // There is a link to ifd1 at the end of ifd0
290             if (mIfdType == IfdId.TYPE_IFD_0) {
291                 long ifdOffset = readUnsignedLong();
292                 if (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested()) {
293                     if (ifdOffset != 0) {
294                         registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
295                     }
296                 }
297             } else {
298                 int offsetSize = 4;
299                 // Some camera models use invalid length of the offset
300                 if (mCorrespondingEvent.size() > 0) {
301                     offsetSize = mCorrespondingEvent.firstEntry().getKey() -
302                             mTiffStream.getReadByteCount();
303                 }
304                 if (offsetSize < 4) {
305                     Log.w(TAG, "Invalid size of link to next IFD: " + offsetSize);
306                 } else {
307                     long ifdOffset = readUnsignedLong();
308                     if (ifdOffset != 0) {
309                         Log.w(TAG, "Invalid link to next IFD: " + ifdOffset);
310                     }
311                 }
312             }
313         }
314         while (mCorrespondingEvent.size() != 0) {
315             Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
316             Object event = entry.getValue();
317             try {
318                 skipTo(entry.getKey());
319             } catch (IOException e) {
320                 Log.w(TAG, "Failed to skip to data at: " + entry.getKey() +
321                         " for " + event.getClass().getName() + ", the file may be broken.");
322                 continue;
323             }
324             if (event instanceof IfdEvent) {
325                 mIfdType = ((IfdEvent) event).ifd;
326                 mNumOfTagInIfd = mTiffStream.readUnsignedShort();
327                 mIfdStartOffset = entry.getKey();
328 
329                 if (mNumOfTagInIfd * TAG_SIZE + mIfdStartOffset + OFFSET_SIZE > mApp1End) {
330                     Log.w(TAG, "Invalid size of IFD " + mIfdType);
331                     return EVENT_END;
332                 }
333 
334                 mNeedToParseOffsetsInCurrentIfd = needToParseOffsetsInCurrentIfd();
335                 if (((IfdEvent) event).isRequested) {
336                     return EVENT_START_OF_IFD;
337                 } else {
338                     skipRemainingTagsInCurrentIfd();
339                 }
340             } else if (event instanceof ImageEvent) {
341                 mImageEvent = (ImageEvent) event;
342                 return mImageEvent.type;
343             } else {
344                 ExifTagEvent tagEvent = (ExifTagEvent) event;
345                 mTag = tagEvent.tag;
346                 if (mTag.getDataType() != ExifTag.TYPE_UNDEFINED) {
347                     readFullTagValue(mTag);
348                     checkOffsetOrImageTag(mTag);
349                 }
350                 if (tagEvent.isRequested) {
351                     return EVENT_VALUE_OF_REGISTERED_TAG;
352                 }
353             }
354         }
355         return EVENT_END;
356     }
357 
358     /**
359      * Skips the tags area of current IFD, if the parser is not in the tag area,
360      * nothing will happen.
361      *
362      * @throws IOException
363      * @throws ExifInvalidFormatException
364      */
skipRemainingTagsInCurrentIfd()365     protected void skipRemainingTagsInCurrentIfd() throws IOException, ExifInvalidFormatException {
366         int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
367         int offset = mTiffStream.getReadByteCount();
368         if (offset > endOfTags) {
369             return;
370         }
371         if (mNeedToParseOffsetsInCurrentIfd) {
372             while (offset < endOfTags) {
373                 mTag = readTag();
374                 offset += TAG_SIZE;
375                 if (mTag == null) {
376                     continue;
377                 }
378                 checkOffsetOrImageTag(mTag);
379             }
380         } else {
381             skipTo(endOfTags);
382         }
383         long ifdOffset = readUnsignedLong();
384         // For ifd0, there is a link to ifd1 in the end of all tags
385         if (mIfdType == IfdId.TYPE_IFD_0
386                 && (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested())) {
387             if (ifdOffset > 0) {
388                 registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
389             }
390         }
391     }
392 
needToParseOffsetsInCurrentIfd()393     private boolean needToParseOffsetsInCurrentIfd() {
394         switch (mIfdType) {
395             case IfdId.TYPE_IFD_0:
396                 return isIfdRequested(IfdId.TYPE_IFD_EXIF) || isIfdRequested(IfdId.TYPE_IFD_GPS)
397                         || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)
398                         || isIfdRequested(IfdId.TYPE_IFD_1);
399             case IfdId.TYPE_IFD_1:
400                 return isThumbnailRequested();
401             case IfdId.TYPE_IFD_EXIF:
402                 // The offset to interoperability IFD is located in Exif IFD
403                 return isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY);
404             default:
405                 return false;
406         }
407     }
408 
409     /**
410      * If {@link #next()} return {@link #EVENT_NEW_TAG} or
411      * {@link #EVENT_VALUE_OF_REGISTERED_TAG}, call this function to get the
412      * corresponding tag.
413      * <p>
414      * For {@link #EVENT_NEW_TAG}, the tag may not contain the value if the size
415      * of the value is greater than 4 bytes. One should call
416      * {@link ExifTag#hasValue()} to check if the tag contains value. If there
417      * is no value,call {@link #registerForTagValue(ExifTag)} to have the parser
418      * emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
419      * pointed by the offset.
420      * <p>
421      * When {@link #EVENT_VALUE_OF_REGISTERED_TAG} is emitted, the value of the
422      * tag will have already been read except for tags of undefined type. For
423      * tags of undefined type, call one of the read methods to get the value.
424      *
425      * @see #registerForTagValue(ExifTag)
426      * @see #read(byte[])
427      * @see #read(byte[], int, int)
428      * @see #readLong()
429      * @see #readRational()
430      * @see #readString(int)
431      * @see #readString(int, Charset)
432      */
getTag()433     protected ExifTag getTag() {
434         return mTag;
435     }
436 
437     /**
438      * Gets number of tags in the current IFD area.
439      */
getTagCountInCurrentIfd()440     protected int getTagCountInCurrentIfd() {
441         return mNumOfTagInIfd;
442     }
443 
444     /**
445      * Gets the ID of current IFD.
446      *
447      * @see IfdId#TYPE_IFD_0
448      * @see IfdId#TYPE_IFD_1
449      * @see IfdId#TYPE_IFD_GPS
450      * @see IfdId#TYPE_IFD_INTEROPERABILITY
451      * @see IfdId#TYPE_IFD_EXIF
452      */
getCurrentIfd()453     protected int getCurrentIfd() {
454         return mIfdType;
455     }
456 
457     /**
458      * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
459      * get the index of this strip.
460      *
461      * @see #getStripCount()
462      */
getStripIndex()463     protected int getStripIndex() {
464         return mImageEvent.stripIndex;
465     }
466 
467     /**
468      * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
469      * get the number of strip data.
470      *
471      * @see #getStripIndex()
472      */
getStripCount()473     protected int getStripCount() {
474         return mStripCount;
475     }
476 
477     /**
478      * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
479      * get the strip size.
480      */
getStripSize()481     protected int getStripSize() {
482         if (mStripSizeTag == null)
483             return 0;
484         return (int) mStripSizeTag.getValueAt(0);
485     }
486 
487     /**
488      * When receiving {@link #EVENT_COMPRESSED_IMAGE}, call this function to get
489      * the image data size.
490      */
getCompressedImageSize()491     protected int getCompressedImageSize() {
492         if (mJpegSizeTag == null) {
493             return 0;
494         }
495         return (int) mJpegSizeTag.getValueAt(0);
496     }
497 
skipTo(int offset)498     private void skipTo(int offset) throws IOException {
499         mTiffStream.skipTo(offset);
500         while (!mCorrespondingEvent.isEmpty() && mCorrespondingEvent.firstKey() < offset) {
501             mCorrespondingEvent.pollFirstEntry();
502         }
503     }
504 
505     /**
506      * When getting {@link #EVENT_NEW_TAG} in the tag area of IFD, the tag may
507      * not contain the value if the size of the value is greater than 4 bytes.
508      * When the value is not available here, call this method so that the parser
509      * will emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
510      * where the value is located.
511      *
512      * @see #EVENT_VALUE_OF_REGISTERED_TAG
513      */
registerForTagValue(ExifTag tag)514     protected void registerForTagValue(ExifTag tag) {
515         if (tag.getOffset() >= mTiffStream.getReadByteCount()) {
516             mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, true));
517         }
518     }
519 
registerIfd(int ifdType, long offset)520     private void registerIfd(int ifdType, long offset) {
521         // Cast unsigned int to int since the offset is always smaller
522         // than the size of APP1 (65536)
523         mCorrespondingEvent.put((int) offset, new IfdEvent(ifdType, isIfdRequested(ifdType)));
524     }
525 
registerCompressedImage(long offset)526     private void registerCompressedImage(long offset) {
527         mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_COMPRESSED_IMAGE));
528     }
529 
registerUncompressedStrip(int stripIndex, long offset)530     private void registerUncompressedStrip(int stripIndex, long offset) {
531         mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_UNCOMPRESSED_STRIP
532                 , stripIndex));
533     }
534 
readTag()535     private ExifTag readTag() throws IOException, ExifInvalidFormatException {
536         short tagId = mTiffStream.readShort();
537         short dataFormat = mTiffStream.readShort();
538         long numOfComp = mTiffStream.readUnsignedInt();
539         if (numOfComp > Integer.MAX_VALUE) {
540             throw new ExifInvalidFormatException(
541                     "Number of component is larger then Integer.MAX_VALUE");
542         }
543         // Some invalid image file contains invalid data type. Ignore those tags
544         if (!ExifTag.isValidType(dataFormat)) {
545             Log.w(TAG, String.format("Tag %04x: Invalid data type %d", tagId, dataFormat));
546             mTiffStream.skip(4);
547             return null;
548         }
549         // TODO: handle numOfComp overflow
550         ExifTag tag = new ExifTag(tagId, dataFormat, (int) numOfComp, mIfdType,
551                 ((int) numOfComp) != ExifTag.SIZE_UNDEFINED);
552         int dataSize = tag.getDataSize();
553         if (dataSize > 4) {
554             long offset = mTiffStream.readUnsignedInt();
555             if (offset > Integer.MAX_VALUE) {
556                 throw new ExifInvalidFormatException(
557                         "offset is larger then Integer.MAX_VALUE");
558             }
559             // Some invalid images put some undefined data before IFD0.
560             // Read the data here.
561             if ((offset < mIfd0Position) && (dataFormat == ExifTag.TYPE_UNDEFINED)) {
562                 byte[] buf = new byte[(int) numOfComp];
563                 System.arraycopy(mDataAboveIfd0, (int) offset - DEFAULT_IFD0_OFFSET,
564                         buf, 0, (int) numOfComp);
565                 tag.setValue(buf);
566             } else {
567                 tag.setOffset((int) offset);
568             }
569         } else {
570             boolean defCount = tag.hasDefinedCount();
571             // Set defined count to 0 so we can add \0 to non-terminated strings
572             tag.setHasDefinedCount(false);
573             // Read value
574             readFullTagValue(tag);
575             tag.setHasDefinedCount(defCount);
576             mTiffStream.skip(4 - dataSize);
577             // Set the offset to the position of value.
578             tag.setOffset(mTiffStream.getReadByteCount() - 4);
579         }
580         return tag;
581     }
582 
583     /**
584      * Check the tag, if the tag is one of the offset tag that points to the IFD
585      * or image the caller is interested in, register the IFD or image.
586      */
checkOffsetOrImageTag(ExifTag tag)587     private void checkOffsetOrImageTag(ExifTag tag) {
588         // Some invalid formattd image contains tag with 0 size.
589         if (tag.getComponentCount() == 0) {
590             return;
591         }
592         short tid = tag.getTagId();
593         int ifd = tag.getIfd();
594         if (tid == TAG_EXIF_IFD && checkAllowed(ifd, ExifInterface.TAG_EXIF_IFD)) {
595             if (isIfdRequested(IfdId.TYPE_IFD_EXIF)
596                     || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
597                 registerIfd(IfdId.TYPE_IFD_EXIF, tag.getValueAt(0));
598             }
599         } else if (tid == TAG_GPS_IFD && checkAllowed(ifd, ExifInterface.TAG_GPS_IFD)) {
600             if (isIfdRequested(IfdId.TYPE_IFD_GPS)) {
601                 registerIfd(IfdId.TYPE_IFD_GPS, tag.getValueAt(0));
602             }
603         } else if (tid == TAG_INTEROPERABILITY_IFD
604                 && checkAllowed(ifd, ExifInterface.TAG_INTEROPERABILITY_IFD)) {
605             if (isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
606                 registerIfd(IfdId.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0));
607             }
608         } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT
609                 && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) {
610             if (isThumbnailRequested()) {
611                 registerCompressedImage(tag.getValueAt(0));
612             }
613         } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT_LENGTH
614                 && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)) {
615             if (isThumbnailRequested()) {
616                 mJpegSizeTag = tag;
617             }
618         } else if (tid == TAG_STRIP_OFFSETS && checkAllowed(ifd, ExifInterface.TAG_STRIP_OFFSETS)) {
619             if (isThumbnailRequested()) {
620                 if (tag.hasValue()) {
621                     for (int i = 0; i < tag.getComponentCount(); i++) {
622                         if (tag.getDataType() == ExifTag.TYPE_UNSIGNED_SHORT) {
623                             registerUncompressedStrip(i, tag.getValueAt(i));
624                         } else {
625                             registerUncompressedStrip(i, tag.getValueAt(i));
626                         }
627                     }
628                 } else {
629                     mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, false));
630                 }
631             }
632         } else if (tid == TAG_STRIP_BYTE_COUNTS
633                 && checkAllowed(ifd, ExifInterface.TAG_STRIP_BYTE_COUNTS)
634                 &&isThumbnailRequested() && tag.hasValue()) {
635             mStripSizeTag = tag;
636         }
637     }
638 
checkAllowed(int ifd, int tagId)639     private boolean checkAllowed(int ifd, int tagId) {
640         int info = mInterface.getTagInfo().get(tagId);
641         if (info == ExifInterface.DEFINITION_NULL) {
642             return false;
643         }
644         return ExifInterface.isIfdAllowed(info, ifd);
645     }
646 
readFullTagValue(ExifTag tag)647     protected void readFullTagValue(ExifTag tag) throws IOException {
648         // Some invalid images contains tags with wrong size, check it here
649         short type = tag.getDataType();
650         if (type == ExifTag.TYPE_ASCII || type == ExifTag.TYPE_UNDEFINED ||
651                 type == ExifTag.TYPE_UNSIGNED_BYTE) {
652             int size = tag.getComponentCount();
653             if (mCorrespondingEvent.size() > 0) {
654                 if (mCorrespondingEvent.firstEntry().getKey() < mTiffStream.getReadByteCount()
655                         + size) {
656                     Object event = mCorrespondingEvent.firstEntry().getValue();
657                     if (event instanceof ImageEvent) {
658                         // Tag value overlaps thumbnail, ignore thumbnail.
659                         Log.w(TAG, "Thumbnail overlaps value for tag: \n" + tag.toString());
660                         Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
661                         Log.w(TAG, "Invalid thumbnail offset: " + entry.getKey());
662                     } else {
663                         // Tag value overlaps another tag, shorten count
664                         if (event instanceof IfdEvent) {
665                             Log.w(TAG, "Ifd " + ((IfdEvent) event).ifd
666                                     + " overlaps value for tag: \n" + tag.toString());
667                         } else if (event instanceof ExifTagEvent) {
668                             Log.w(TAG, "Tag value for tag: \n"
669                                     + ((ExifTagEvent) event).tag.toString()
670                                     + " overlaps value for tag: \n" + tag.toString());
671                         }
672                         size = mCorrespondingEvent.firstEntry().getKey()
673                                 - mTiffStream.getReadByteCount();
674                         Log.w(TAG, "Invalid size of tag: \n" + tag.toString()
675                                 + " setting count to: " + size);
676                         tag.forceSetComponentCount(size);
677                     }
678                 }
679             }
680         }
681         switch (tag.getDataType()) {
682             case ExifTag.TYPE_UNSIGNED_BYTE:
683             case ExifTag.TYPE_UNDEFINED: {
684                 byte buf[] = new byte[tag.getComponentCount()];
685                 read(buf);
686                 tag.setValue(buf);
687             }
688                 break;
689             case ExifTag.TYPE_ASCII:
690                 tag.setValue(readString(tag.getComponentCount()));
691                 break;
692             case ExifTag.TYPE_UNSIGNED_LONG: {
693                 long value[] = new long[tag.getComponentCount()];
694                 for (int i = 0, n = value.length; i < n; i++) {
695                     value[i] = readUnsignedLong();
696                 }
697                 tag.setValue(value);
698             }
699                 break;
700             case ExifTag.TYPE_UNSIGNED_RATIONAL: {
701                 Rational value[] = new Rational[tag.getComponentCount()];
702                 for (int i = 0, n = value.length; i < n; i++) {
703                     value[i] = readUnsignedRational();
704                 }
705                 tag.setValue(value);
706             }
707                 break;
708             case ExifTag.TYPE_UNSIGNED_SHORT: {
709                 int value[] = new int[tag.getComponentCount()];
710                 for (int i = 0, n = value.length; i < n; i++) {
711                     value[i] = readUnsignedShort();
712                 }
713                 tag.setValue(value);
714             }
715                 break;
716             case ExifTag.TYPE_LONG: {
717                 int value[] = new int[tag.getComponentCount()];
718                 for (int i = 0, n = value.length; i < n; i++) {
719                     value[i] = readLong();
720                 }
721                 tag.setValue(value);
722             }
723                 break;
724             case ExifTag.TYPE_RATIONAL: {
725                 Rational value[] = new Rational[tag.getComponentCount()];
726                 for (int i = 0, n = value.length; i < n; i++) {
727                     value[i] = readRational();
728                 }
729                 tag.setValue(value);
730             }
731                 break;
732         }
733         if (LOGV) {
734             Log.v(TAG, "\n" + tag.toString());
735         }
736     }
737 
parseTiffHeader()738     private void parseTiffHeader() throws IOException,
739             ExifInvalidFormatException {
740         short byteOrder = mTiffStream.readShort();
741         if (LITTLE_ENDIAN_TAG == byteOrder) {
742             mTiffStream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
743         } else if (BIG_ENDIAN_TAG == byteOrder) {
744             mTiffStream.setByteOrder(ByteOrder.BIG_ENDIAN);
745         } else {
746             throw new ExifInvalidFormatException("Invalid TIFF header");
747         }
748 
749         if (mTiffStream.readShort() != TIFF_HEADER_TAIL) {
750             throw new ExifInvalidFormatException("Invalid TIFF header");
751         }
752     }
753 
seekTiffData(InputStream inputStream)754     private boolean seekTiffData(InputStream inputStream) throws IOException,
755             ExifInvalidFormatException {
756         CountedDataInputStream dataStream = new CountedDataInputStream(inputStream);
757         if (dataStream.readShort() != JpegHeader.SOI) {
758             throw new ExifInvalidFormatException("Invalid JPEG format");
759         }
760 
761         short marker = dataStream.readShort();
762         while (marker != JpegHeader.EOI
763                 && !JpegHeader.isSofMarker(marker)) {
764             int length = dataStream.readUnsignedShort();
765             // Some invalid formatted image contains multiple APP1,
766             // try to find the one with Exif data.
767             if (marker == JpegHeader.APP1) {
768                 int header = 0;
769                 short headerTail = 0;
770                 if (length >= 8) {
771                     header = dataStream.readInt();
772                     headerTail = dataStream.readShort();
773                     length -= 6;
774                     if (header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) {
775                         mTiffStartPosition = dataStream.getReadByteCount();
776                         mApp1End = length;
777                         mOffsetToApp1EndFromSOF = mTiffStartPosition + mApp1End;
778                         return true;
779                     }
780                 }
781             }
782             if (length < 2 || (length - 2) != dataStream.skip(length - 2)) {
783                 Log.w(TAG, "Invalid JPEG format.");
784                 return false;
785             }
786             marker = dataStream.readShort();
787         }
788         return false;
789     }
790 
getOffsetToExifEndFromSOF()791     protected int getOffsetToExifEndFromSOF() {
792         return mOffsetToApp1EndFromSOF;
793     }
794 
getTiffStartPosition()795     protected int getTiffStartPosition() {
796         return mTiffStartPosition;
797     }
798 
799     /**
800      * Reads bytes from the InputStream.
801      */
read(byte[] buffer, int offset, int length)802     protected int read(byte[] buffer, int offset, int length) throws IOException {
803         return mTiffStream.read(buffer, offset, length);
804     }
805 
806     /**
807      * Equivalent to read(buffer, 0, buffer.length).
808      */
read(byte[] buffer)809     protected int read(byte[] buffer) throws IOException {
810         return mTiffStream.read(buffer);
811     }
812 
813     /**
814      * Reads a String from the InputStream with US-ASCII charset. The parser
815      * will read n bytes and convert it to ascii string. This is used for
816      * reading values of type {@link ExifTag#TYPE_ASCII}.
817      */
readString(int n)818     protected String readString(int n) throws IOException {
819         return readString(n, US_ASCII);
820     }
821 
822     /**
823      * Reads a String from the InputStream with the given charset. The parser
824      * will read n bytes and convert it to string. This is used for reading
825      * values of type {@link ExifTag#TYPE_ASCII}.
826      */
readString(int n, Charset charset)827     protected String readString(int n, Charset charset) throws IOException {
828         if (n > 0) {
829             return mTiffStream.readString(n, charset);
830         } else {
831             return "";
832         }
833     }
834 
835     /**
836      * Reads value of type {@link ExifTag#TYPE_UNSIGNED_SHORT} from the
837      * InputStream.
838      */
readUnsignedShort()839     protected int readUnsignedShort() throws IOException {
840         return mTiffStream.readShort() & 0xffff;
841     }
842 
843     /**
844      * Reads value of type {@link ExifTag#TYPE_UNSIGNED_LONG} from the
845      * InputStream.
846      */
readUnsignedLong()847     protected long readUnsignedLong() throws IOException {
848         return readLong() & 0xffffffffL;
849     }
850 
851     /**
852      * Reads value of type {@link ExifTag#TYPE_UNSIGNED_RATIONAL} from the
853      * InputStream.
854      */
readUnsignedRational()855     protected Rational readUnsignedRational() throws IOException {
856         long nomi = readUnsignedLong();
857         long denomi = readUnsignedLong();
858         return new Rational(nomi, denomi);
859     }
860 
861     /**
862      * Reads value of type {@link ExifTag#TYPE_LONG} from the InputStream.
863      */
readLong()864     protected int readLong() throws IOException {
865         return mTiffStream.readInt();
866     }
867 
868     /**
869      * Reads value of type {@link ExifTag#TYPE_RATIONAL} from the InputStream.
870      */
readRational()871     protected Rational readRational() throws IOException {
872         int nomi = readLong();
873         int denomi = readLong();
874         return new Rational(nomi, denomi);
875     }
876 
877     private static class ImageEvent {
878         int stripIndex;
879         int type;
880 
ImageEvent(int type)881         ImageEvent(int type) {
882             this.stripIndex = 0;
883             this.type = type;
884         }
885 
ImageEvent(int type, int stripIndex)886         ImageEvent(int type, int stripIndex) {
887             this.type = type;
888             this.stripIndex = stripIndex;
889         }
890     }
891 
892     private static class IfdEvent {
893         int ifd;
894         boolean isRequested;
895 
IfdEvent(int ifd, boolean isInterestedIfd)896         IfdEvent(int ifd, boolean isInterestedIfd) {
897             this.ifd = ifd;
898             this.isRequested = isInterestedIfd;
899         }
900     }
901 
902     private static class ExifTagEvent {
903         ExifTag tag;
904         boolean isRequested;
905 
ExifTagEvent(ExifTag tag, boolean isRequireByUser)906         ExifTagEvent(ExifTag tag, boolean isRequireByUser) {
907             this.tag = tag;
908             this.isRequested = isRequireByUser;
909         }
910     }
911 
912     /**
913      * Gets the byte order of the current InputStream.
914      */
getByteOrder()915     protected ByteOrder getByteOrder() {
916         return mTiffStream.getByteOrder();
917     }
918 }
919