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