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