Home | History | Annotate | Download | only in exif
      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 
    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 
    169   private boolean isThumbnailRequested() {
    170     return (options & OPTION_THUMBNAIL) != 0;
    171   }
    172 
    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    */
    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    */
    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    */
    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    */
    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 
    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    */
    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    */
    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    */
    431   int getStripIndex() {
    432     return imageEvent.stripIndex;
    433   }
    434 
    435   /** When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to get the strip size. */
    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    */
    446   int getCompressedImageSize() {
    447     if (jpegSizeTag == null) {
    448       return 0;
    449     }
    450     return (int) jpegSizeTag.getValueAt(0);
    451   }
    452 
    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    */
    468   void registerForTagValue(ExifTag tag) {
    469     if (tag.getOffset() >= tiffStream.getReadByteCount()) {
    470       correspondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, true));
    471     }
    472   }
    473 
    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 
    480   private void registerCompressedImage(long offset) {
    481     correspondingEvent.put((int) offset, new ImageEvent(EVENT_COMPRESSED_IMAGE));
    482   }
    483 
    484   private void registerUncompressedStrip(int stripIndex, long offset) {
    485     correspondingEvent.put((int) offset, new ImageEvent(EVENT_UNCOMPRESSED_STRIP, stripIndex));
    486   }
    487 
    488   @SuppressLint("DefaultLocale")
    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    */
    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 
    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 
    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 
    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 
    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. */
    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). */
    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    */
    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    */
    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. */
    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. */
    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. */
    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. */
    801   private int readLong() throws IOException {
    802     return tiffStream.readInt();
    803   }
    804 
    805   /** Reads value of type {@link ExifTag#TYPE_RATIONAL} from the InputStream. */
    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 
    816     ImageEvent(int type) {
    817       this.stripIndex = 0;
    818       this.type = type;
    819     }
    820 
    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 
    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 
    841     ExifTagEvent(ExifTag tag, boolean isRequireByUser) {
    842       this.tag = tag;
    843       this.isRequested = isRequireByUser;
    844     }
    845   }
    846 }
    847