Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2007 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 android.media;
     18 
     19 import android.annotation.NonNull;
     20 import android.content.res.AssetManager;
     21 import android.graphics.Bitmap;
     22 import android.graphics.BitmapFactory;
     23 import android.system.ErrnoException;
     24 import android.system.Os;
     25 import android.system.OsConstants;
     26 import android.util.Log;
     27 import android.util.Pair;
     28 import android.annotation.IntDef;
     29 
     30 import java.io.BufferedInputStream;
     31 import java.io.ByteArrayInputStream;
     32 import java.io.DataInputStream;
     33 import java.io.DataInput;
     34 import java.io.EOFException;
     35 import java.io.File;
     36 import java.io.FileDescriptor;
     37 import java.io.FileInputStream;
     38 import java.io.FileNotFoundException;
     39 import java.io.FileOutputStream;
     40 import java.io.FilterOutputStream;
     41 import java.io.IOException;
     42 import java.io.InputStream;
     43 import java.io.OutputStream;
     44 import java.nio.ByteBuffer;
     45 import java.nio.ByteOrder;
     46 import java.nio.charset.Charset;
     47 import java.nio.charset.StandardCharsets;
     48 import java.text.ParsePosition;
     49 import java.text.SimpleDateFormat;
     50 import java.util.Arrays;
     51 import java.util.LinkedList;
     52 import java.util.Date;
     53 import java.util.HashMap;
     54 import java.util.HashSet;
     55 import java.util.Map;
     56 import java.util.Set;
     57 import java.util.TimeZone;
     58 import java.util.regex.Matcher;
     59 import java.util.regex.Pattern;
     60 import java.lang.annotation.Retention;
     61 import java.lang.annotation.RetentionPolicy;
     62 
     63 import libcore.io.IoUtils;
     64 import libcore.io.Streams;
     65 
     66 /**
     67  * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file.
     68  * <p>
     69  * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW, RAF and HEIF.
     70  * <p>
     71  * Attribute mutation is supported for JPEG image files.
     72  */
     73 public class ExifInterface {
     74     private static final String TAG = "ExifInterface";
     75     private static final boolean DEBUG = false;
     76 
     77     // The Exif tag names. See Tiff 6.0 Section 3 and Section 8.
     78     /** Type is String. */
     79     public static final String TAG_ARTIST = "Artist";
     80     /** Type is int. */
     81     public static final String TAG_BITS_PER_SAMPLE = "BitsPerSample";
     82     /** Type is int. */
     83     public static final String TAG_COMPRESSION = "Compression";
     84     /** Type is String. */
     85     public static final String TAG_COPYRIGHT = "Copyright";
     86     /** Type is String. */
     87     public static final String TAG_DATETIME = "DateTime";
     88     /** Type is String. */
     89     public static final String TAG_IMAGE_DESCRIPTION = "ImageDescription";
     90     /** Type is int. */
     91     public static final String TAG_IMAGE_LENGTH = "ImageLength";
     92     /** Type is int. */
     93     public static final String TAG_IMAGE_WIDTH = "ImageWidth";
     94     /** Type is int. */
     95     public static final String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
     96     /** Type is int. */
     97     public static final String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
     98     /** Type is String. */
     99     public static final String TAG_MAKE = "Make";
    100     /** Type is String. */
    101     public static final String TAG_MODEL = "Model";
    102     /** Type is int. */
    103     public static final String TAG_ORIENTATION = "Orientation";
    104     /** Type is int. */
    105     public static final String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
    106     /** Type is int. */
    107     public static final String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
    108     /** Type is rational. */
    109     public static final String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
    110     /** Type is rational. */
    111     public static final String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
    112     /** Type is int. */
    113     public static final String TAG_RESOLUTION_UNIT = "ResolutionUnit";
    114     /** Type is int. */
    115     public static final String TAG_ROWS_PER_STRIP = "RowsPerStrip";
    116     /** Type is int. */
    117     public static final String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
    118     /** Type is String. */
    119     public static final String TAG_SOFTWARE = "Software";
    120     /** Type is int. */
    121     public static final String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
    122     /** Type is int. */
    123     public static final String TAG_STRIP_OFFSETS = "StripOffsets";
    124     /** Type is int. */
    125     public static final String TAG_TRANSFER_FUNCTION = "TransferFunction";
    126     /** Type is rational. */
    127     public static final String TAG_WHITE_POINT = "WhitePoint";
    128     /** Type is rational. */
    129     public static final String TAG_X_RESOLUTION = "XResolution";
    130     /** Type is rational. */
    131     public static final String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
    132     /** Type is int. */
    133     public static final String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
    134     /** Type is int. */
    135     public static final String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
    136     /** Type is rational. */
    137     public static final String TAG_Y_RESOLUTION = "YResolution";
    138     /** Type is rational. */
    139     public static final String TAG_APERTURE_VALUE = "ApertureValue";
    140     /** Type is rational. */
    141     public static final String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
    142     /** Type is String. */
    143     public static final String TAG_CFA_PATTERN = "CFAPattern";
    144     /** Type is int. */
    145     public static final String TAG_COLOR_SPACE = "ColorSpace";
    146     /** Type is String. */
    147     public static final String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
    148     /** Type is rational. */
    149     public static final String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
    150     /** Type is int. */
    151     public static final String TAG_CONTRAST = "Contrast";
    152     /** Type is int. */
    153     public static final String TAG_CUSTOM_RENDERED = "CustomRendered";
    154     /** Type is String. */
    155     public static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
    156     /** Type is String. */
    157     public static final String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
    158     /** Type is String. */
    159     public static final String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
    160     /** Type is double. */
    161     public static final String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
    162     /** Type is String. */
    163     public static final String TAG_EXIF_VERSION = "ExifVersion";
    164     /** Type is double. */
    165     public static final String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
    166     /** Type is rational. */
    167     public static final String TAG_EXPOSURE_INDEX = "ExposureIndex";
    168     /** Type is int. */
    169     public static final String TAG_EXPOSURE_MODE = "ExposureMode";
    170     /** Type is int. */
    171     public static final String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
    172     /** Type is double. */
    173     public static final String TAG_EXPOSURE_TIME = "ExposureTime";
    174     /** Type is double. */
    175     public static final String TAG_F_NUMBER = "FNumber";
    176     /**
    177      * Type is double.
    178      *
    179      * @deprecated use {@link #TAG_F_NUMBER} instead
    180      */
    181     @Deprecated
    182     public static final String TAG_APERTURE = "FNumber";
    183     /** Type is String. */
    184     public static final String TAG_FILE_SOURCE = "FileSource";
    185     /** Type is int. */
    186     public static final String TAG_FLASH = "Flash";
    187     /** Type is rational. */
    188     public static final String TAG_FLASH_ENERGY = "FlashEnergy";
    189     /** Type is String. */
    190     public static final String TAG_FLASHPIX_VERSION = "FlashpixVersion";
    191     /** Type is rational. */
    192     public static final String TAG_FOCAL_LENGTH = "FocalLength";
    193     /** Type is int. */
    194     public static final String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
    195     /** Type is int. */
    196     public static final String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
    197     /** Type is rational. */
    198     public static final String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
    199     /** Type is rational. */
    200     public static final String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
    201     /** Type is int. */
    202     public static final String TAG_GAIN_CONTROL = "GainControl";
    203     /** Type is int. */
    204     public static final String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
    205     /**
    206      * Type is int.
    207      *
    208      * @deprecated use {@link #TAG_ISO_SPEED_RATINGS} instead
    209      */
    210     @Deprecated
    211     public static final String TAG_ISO = "ISOSpeedRatings";
    212     /** Type is String. */
    213     public static final String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
    214     /** Type is int. */
    215     public static final String TAG_LIGHT_SOURCE = "LightSource";
    216     /** Type is String. */
    217     public static final String TAG_MAKER_NOTE = "MakerNote";
    218     /** Type is rational. */
    219     public static final String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
    220     /** Type is int. */
    221     public static final String TAG_METERING_MODE = "MeteringMode";
    222     /** Type is int. */
    223     public static final String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
    224     /** Type is String. */
    225     public static final String TAG_OECF = "OECF";
    226     /** Type is int. */
    227     public static final String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
    228     /** Type is int. */
    229     public static final String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
    230     /** Type is String. */
    231     public static final String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
    232     /** Type is int. */
    233     public static final String TAG_SATURATION = "Saturation";
    234     /** Type is int. */
    235     public static final String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
    236     /** Type is String. */
    237     public static final String TAG_SCENE_TYPE = "SceneType";
    238     /** Type is int. */
    239     public static final String TAG_SENSING_METHOD = "SensingMethod";
    240     /** Type is int. */
    241     public static final String TAG_SHARPNESS = "Sharpness";
    242     /** Type is rational. */
    243     public static final String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
    244     /** Type is String. */
    245     public static final String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
    246     /** Type is String. */
    247     public static final String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
    248     /** Type is int. */
    249     public static final String TAG_SUBFILE_TYPE = "SubfileType";
    250     /** Type is String. */
    251     public static final String TAG_SUBSEC_TIME = "SubSecTime";
    252     /**
    253      * Type is String.
    254      *
    255      * @deprecated use {@link #TAG_SUBSEC_TIME_DIGITIZED} instead
    256      */
    257     public static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
    258     /** Type is String. */
    259     public static final String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
    260     /**
    261      * Type is String.
    262      *
    263      * @deprecated use {@link #TAG_SUBSEC_TIME_ORIGINAL} instead
    264      */
    265     public static final String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
    266     /** Type is String. */
    267     public static final String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
    268     /** Type is int. */
    269     public static final String TAG_SUBJECT_AREA = "SubjectArea";
    270     /** Type is double. */
    271     public static final String TAG_SUBJECT_DISTANCE = "SubjectDistance";
    272     /** Type is int. */
    273     public static final String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
    274     /** Type is int. */
    275     public static final String TAG_SUBJECT_LOCATION = "SubjectLocation";
    276     /** Type is String. */
    277     public static final String TAG_USER_COMMENT = "UserComment";
    278     /** Type is int. */
    279     public static final String TAG_WHITE_BALANCE = "WhiteBalance";
    280     /**
    281      * The altitude (in meters) based on the reference in TAG_GPS_ALTITUDE_REF.
    282      * Type is rational.
    283      */
    284     public static final String TAG_GPS_ALTITUDE = "GPSAltitude";
    285     /**
    286      * 0 if the altitude is above sea level. 1 if the altitude is below sea
    287      * level. Type is int.
    288      */
    289     public static final String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
    290     /** Type is String. */
    291     public static final String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
    292     /** Type is rational. */
    293     public static final String TAG_GPS_DOP = "GPSDOP";
    294     /** Type is String. */
    295     public static final String TAG_GPS_DATESTAMP = "GPSDateStamp";
    296     /** Type is rational. */
    297     public static final String TAG_GPS_DEST_BEARING = "GPSDestBearing";
    298     /** Type is String. */
    299     public static final String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
    300     /** Type is rational. */
    301     public static final String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
    302     /** Type is String. */
    303     public static final String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
    304     /** Type is rational. */
    305     public static final String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
    306     /** Type is String. */
    307     public static final String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
    308     /** Type is rational. */
    309     public static final String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
    310     /** Type is String. */
    311     public static final String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
    312     /** Type is int. */
    313     public static final String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
    314     /** Type is rational. */
    315     public static final String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
    316     /** Type is String. */
    317     public static final String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
    318     /** Type is rational. Format is "num1/denom1,num2/denom2,num3/denom3". */
    319     public static final String TAG_GPS_LATITUDE = "GPSLatitude";
    320     /** Type is String. */
    321     public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
    322     /** Type is rational. Format is "num1/denom1,num2/denom2,num3/denom3". */
    323     public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
    324     /** Type is String. */
    325     public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
    326     /** Type is String. */
    327     public static final String TAG_GPS_MAP_DATUM = "GPSMapDatum";
    328     /** Type is String. */
    329     public static final String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
    330     /** Type is String. Name of GPS processing method used for location finding. */
    331     public static final String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
    332     /** Type is String. */
    333     public static final String TAG_GPS_SATELLITES = "GPSSatellites";
    334     /** Type is rational. */
    335     public static final String TAG_GPS_SPEED = "GPSSpeed";
    336     /** Type is String. */
    337     public static final String TAG_GPS_SPEED_REF = "GPSSpeedRef";
    338     /** Type is String. */
    339     public static final String TAG_GPS_STATUS = "GPSStatus";
    340     /** Type is String. Format is "hh:mm:ss". */
    341     public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
    342     /** Type is rational. */
    343     public static final String TAG_GPS_TRACK = "GPSTrack";
    344     /** Type is String. */
    345     public static final String TAG_GPS_TRACK_REF = "GPSTrackRef";
    346     /** Type is String. */
    347     public static final String TAG_GPS_VERSION_ID = "GPSVersionID";
    348     /** Type is String. */
    349     public static final String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
    350     /** Type is int. */
    351     public static final String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
    352     /** Type is int. */
    353     public static final String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
    354     /** Type is int. DNG Specification 1.4.0.0. Section 4 */
    355     public static final String TAG_DNG_VERSION = "DNGVersion";
    356     /** Type is int. DNG Specification 1.4.0.0. Section 4 */
    357     public static final String TAG_DEFAULT_CROP_SIZE = "DefaultCropSize";
    358     /** Type is undefined. See Olympus MakerNote tags in http://www.exiv2.org/tags-olympus.html. */
    359     public static final String TAG_ORF_THUMBNAIL_IMAGE = "ThumbnailImage";
    360     /** Type is int. See Olympus Camera Settings tags in http://www.exiv2.org/tags-olympus.html. */
    361     public static final String TAG_ORF_PREVIEW_IMAGE_START = "PreviewImageStart";
    362     /** Type is int. See Olympus Camera Settings tags in http://www.exiv2.org/tags-olympus.html. */
    363     public static final String TAG_ORF_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
    364     /** Type is int. See Olympus Image Processing tags in http://www.exiv2.org/tags-olympus.html. */
    365     public static final String TAG_ORF_ASPECT_FRAME = "AspectFrame";
    366     /**
    367      * Type is int. See PanasonicRaw tags in
    368      * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
    369      */
    370     public static final String TAG_RW2_SENSOR_BOTTOM_BORDER = "SensorBottomBorder";
    371     /**
    372      * Type is int. See PanasonicRaw tags in
    373      * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
    374      */
    375     public static final String TAG_RW2_SENSOR_LEFT_BORDER = "SensorLeftBorder";
    376     /**
    377      * Type is int. See PanasonicRaw tags in
    378      * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
    379      */
    380     public static final String TAG_RW2_SENSOR_RIGHT_BORDER = "SensorRightBorder";
    381     /**
    382      * Type is int. See PanasonicRaw tags in
    383      * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
    384      */
    385     public static final String TAG_RW2_SENSOR_TOP_BORDER = "SensorTopBorder";
    386     /**
    387      * Type is int. See PanasonicRaw tags in
    388      * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
    389      */
    390     public static final String TAG_RW2_ISO = "ISO";
    391     /**
    392      * Type is undefined. See PanasonicRaw tags in
    393      * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
    394      */
    395     public static final String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw";
    396 
    397     /**
    398      * Private tags used for pointing the other IFD offsets.
    399      * The types of the following tags are int.
    400      * See JEITA CP-3451C Section 4.6.3: Exif-specific IFD.
    401      * For SubIFD, see Note 1 of Adobe PageMaker 6.0 TIFF Technical Notes.
    402      */
    403     private static final String TAG_EXIF_IFD_POINTER = "ExifIFDPointer";
    404     private static final String TAG_GPS_INFO_IFD_POINTER = "GPSInfoIFDPointer";
    405     private static final String TAG_INTEROPERABILITY_IFD_POINTER = "InteroperabilityIFDPointer";
    406     private static final String TAG_SUB_IFD_POINTER = "SubIFDPointer";
    407     // Proprietary pointer tags used for ORF files.
    408     // See http://www.exiv2.org/tags-olympus.html
    409     private static final String TAG_ORF_CAMERA_SETTINGS_IFD_POINTER = "CameraSettingsIFDPointer";
    410     private static final String TAG_ORF_IMAGE_PROCESSING_IFD_POINTER = "ImageProcessingIFDPointer";
    411 
    412     // Private tags used for thumbnail information.
    413     private static final String TAG_HAS_THUMBNAIL = "HasThumbnail";
    414     private static final String TAG_THUMBNAIL_OFFSET = "ThumbnailOffset";
    415     private static final String TAG_THUMBNAIL_LENGTH = "ThumbnailLength";
    416     private static final String TAG_THUMBNAIL_DATA = "ThumbnailData";
    417     private static final int MAX_THUMBNAIL_SIZE = 512;
    418 
    419     // Constants used for the Orientation Exif tag.
    420     public static final int ORIENTATION_UNDEFINED = 0;
    421     public static final int ORIENTATION_NORMAL = 1;
    422     public static final int ORIENTATION_FLIP_HORIZONTAL = 2;  // left right reversed mirror
    423     public static final int ORIENTATION_ROTATE_180 = 3;
    424     public static final int ORIENTATION_FLIP_VERTICAL = 4;  // upside down mirror
    425     // flipped about top-left <--> bottom-right axis
    426     public static final int ORIENTATION_TRANSPOSE = 5;
    427     public static final int ORIENTATION_ROTATE_90 = 6;  // rotate 90 cw to right it
    428     // flipped about top-right <--> bottom-left axis
    429     public static final int ORIENTATION_TRANSVERSE = 7;
    430     public static final int ORIENTATION_ROTATE_270 = 8;  // rotate 270 to right it
    431 
    432     // Constants used for white balance
    433     public static final int WHITEBALANCE_AUTO = 0;
    434     public static final int WHITEBALANCE_MANUAL = 1;
    435 
    436     // Maximum size for checking file type signature (see image_type_recognition_lite.cc)
    437     private static final int SIGNATURE_CHECK_SIZE = 5000;
    438 
    439     private static final byte[] JPEG_SIGNATURE = new byte[] {(byte) 0xff, (byte) 0xd8, (byte) 0xff};
    440     private static final String RAF_SIGNATURE = "FUJIFILMCCD-RAW";
    441     private static final int RAF_OFFSET_TO_JPEG_IMAGE_OFFSET = 84;
    442     private static final int RAF_INFO_SIZE = 160;
    443     private static final int RAF_JPEG_LENGTH_VALUE_SIZE = 4;
    444 
    445     private static final byte[] HEIF_TYPE_FTYP = new byte[] {'f', 't', 'y', 'p'};
    446     private static final byte[] HEIF_BRAND_MIF1 = new byte[] {'m', 'i', 'f', '1'};
    447     private static final byte[] HEIF_BRAND_HEIC = new byte[] {'h', 'e', 'i', 'c'};
    448 
    449     // See http://fileformats.archiveteam.org/wiki/Olympus_ORF
    450     private static final short ORF_SIGNATURE_1 = 0x4f52;
    451     private static final short ORF_SIGNATURE_2 = 0x5352;
    452     // There are two formats for Olympus Makernote Headers. Each has different identifiers and
    453     // offsets to the actual data.
    454     // See http://www.exiv2.org/makernote.html#R1
    455     private static final byte[] ORF_MAKER_NOTE_HEADER_1 = new byte[] {(byte) 0x4f, (byte) 0x4c,
    456             (byte) 0x59, (byte) 0x4d, (byte) 0x50, (byte) 0x00}; // "OLYMP\0"
    457     private static final byte[] ORF_MAKER_NOTE_HEADER_2 = new byte[] {(byte) 0x4f, (byte) 0x4c,
    458             (byte) 0x59, (byte) 0x4d, (byte) 0x50, (byte) 0x55, (byte) 0x53, (byte) 0x00,
    459             (byte) 0x49, (byte) 0x49}; // "OLYMPUS\0II"
    460     private static final int ORF_MAKER_NOTE_HEADER_1_SIZE = 8;
    461     private static final int ORF_MAKER_NOTE_HEADER_2_SIZE = 12;
    462 
    463     // See http://fileformats.archiveteam.org/wiki/RW2
    464     private static final short RW2_SIGNATURE = 0x0055;
    465 
    466     // See http://fileformats.archiveteam.org/wiki/Pentax_PEF
    467     private static final String PEF_SIGNATURE = "PENTAX";
    468     // See http://www.exiv2.org/makernote.html#R11
    469     private static final int PEF_MAKER_NOTE_SKIP_SIZE = 6;
    470 
    471     private static SimpleDateFormat sFormatter;
    472 
    473     // See Exchangeable image file format for digital still cameras: Exif version 2.2.
    474     // The following values are for parsing EXIF data area. There are tag groups in EXIF data area.
    475     // They are called "Image File Directory". They have multiple data formats to cover various
    476     // image metadata from GPS longitude to camera model name.
    477 
    478     // Types of Exif byte alignments (see JEITA CP-3451C Section 4.5.2)
    479     private static final short BYTE_ALIGN_II = 0x4949;  // II: Intel order
    480     private static final short BYTE_ALIGN_MM = 0x4d4d;  // MM: Motorola order
    481 
    482     // TIFF Header Fixed Constant (see JEITA CP-3451C Section 4.5.2)
    483     private static final byte START_CODE = 0x2a; // 42
    484     private static final int IFD_OFFSET = 8;
    485 
    486     // Formats for the value in IFD entry (See TIFF 6.0 Section 2, "Image File Directory".)
    487     private static final int IFD_FORMAT_BYTE = 1;
    488     private static final int IFD_FORMAT_STRING = 2;
    489     private static final int IFD_FORMAT_USHORT = 3;
    490     private static final int IFD_FORMAT_ULONG = 4;
    491     private static final int IFD_FORMAT_URATIONAL = 5;
    492     private static final int IFD_FORMAT_SBYTE = 6;
    493     private static final int IFD_FORMAT_UNDEFINED = 7;
    494     private static final int IFD_FORMAT_SSHORT = 8;
    495     private static final int IFD_FORMAT_SLONG = 9;
    496     private static final int IFD_FORMAT_SRATIONAL = 10;
    497     private static final int IFD_FORMAT_SINGLE = 11;
    498     private static final int IFD_FORMAT_DOUBLE = 12;
    499     // Format indicating a new IFD entry (See Adobe PageMaker 6.0 TIFF Technical Notes, "New Tag")
    500     private static final int IFD_FORMAT_IFD = 13;
    501     // Names for the data formats for debugging purpose.
    502     private static final String[] IFD_FORMAT_NAMES = new String[] {
    503             "", "BYTE", "STRING", "USHORT", "ULONG", "URATIONAL", "SBYTE", "UNDEFINED", "SSHORT",
    504             "SLONG", "SRATIONAL", "SINGLE", "DOUBLE"
    505     };
    506     // Sizes of the components of each IFD value format
    507     private static final int[] IFD_FORMAT_BYTES_PER_FORMAT = new int[] {
    508             0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 1
    509     };
    510     private static final byte[] EXIF_ASCII_PREFIX = new byte[] {
    511             0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0
    512     };
    513 
    514     /**
    515      * Constants used for Compression tag.
    516      * For Value 1, 2, 32773, see TIFF 6.0 Spec Section 3: Bilevel Images, Compression
    517      * For Value 6, see TIFF 6.0 Spec Section 22: JPEG Compression, Extensions to Existing Fields
    518      * For Value 7, 8, 34892, see DNG Specification 1.4.0.0. Section 3, Compression
    519      */
    520     private static final int DATA_UNCOMPRESSED = 1;
    521     private static final int DATA_HUFFMAN_COMPRESSED = 2;
    522     private static final int DATA_JPEG = 6;
    523     private static final int DATA_JPEG_COMPRESSED = 7;
    524     private static final int DATA_DEFLATE_ZIP = 8;
    525     private static final int DATA_PACK_BITS_COMPRESSED = 32773;
    526     private static final int DATA_LOSSY_JPEG = 34892;
    527 
    528     /**
    529      * Constants used for BitsPerSample tag.
    530      * For RGB, see TIFF 6.0 Spec Section 6, Differences from Palette Color Images
    531      * For Greyscale, see TIFF 6.0 Spec Section 4, Differences from Bilevel Images
    532      */
    533     private static final int[] BITS_PER_SAMPLE_RGB = new int[] { 8, 8, 8 };
    534     private static final int[] BITS_PER_SAMPLE_GREYSCALE_1 = new int[] { 4 };
    535     private static final int[] BITS_PER_SAMPLE_GREYSCALE_2 = new int[] { 8 };
    536 
    537     /**
    538      * Constants used for PhotometricInterpretation tag.
    539      * For White/Black, see Section 3, Color.
    540      * See TIFF 6.0 Spec Section 22, Minimum Requirements for TIFF with JPEG Compression.
    541      */
    542     private static final int PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO = 0;
    543     private static final int PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO = 1;
    544     private static final int PHOTOMETRIC_INTERPRETATION_RGB = 2;
    545     private static final int PHOTOMETRIC_INTERPRETATION_YCBCR = 6;
    546 
    547     /**
    548      * Constants used for NewSubfileType tag.
    549      * See TIFF 6.0 Spec Section 8
    550      * */
    551     private static final int ORIGINAL_RESOLUTION_IMAGE = 0;
    552     private static final int REDUCED_RESOLUTION_IMAGE = 1;
    553 
    554     // A class for indicating EXIF rational type.
    555     private static class Rational {
    556         public final long numerator;
    557         public final long denominator;
    558 
    559         private Rational(long numerator, long denominator) {
    560             // Handle erroneous case
    561             if (denominator == 0) {
    562                 this.numerator = 0;
    563                 this.denominator = 1;
    564                 return;
    565             }
    566             this.numerator = numerator;
    567             this.denominator = denominator;
    568         }
    569 
    570         @Override
    571         public String toString() {
    572             return numerator + "/" + denominator;
    573         }
    574 
    575         public double calculate() {
    576             return (double) numerator / denominator;
    577         }
    578     }
    579 
    580     // A class for indicating EXIF attribute.
    581     private static class ExifAttribute {
    582         public final int format;
    583         public final int numberOfComponents;
    584         public final byte[] bytes;
    585 
    586         private ExifAttribute(int format, int numberOfComponents, byte[] bytes) {
    587             this.format = format;
    588             this.numberOfComponents = numberOfComponents;
    589             this.bytes = bytes;
    590         }
    591 
    592         public static ExifAttribute createUShort(int[] values, ByteOrder byteOrder) {
    593             final ByteBuffer buffer = ByteBuffer.wrap(
    594                     new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_USHORT] * values.length]);
    595             buffer.order(byteOrder);
    596             for (int value : values) {
    597                 buffer.putShort((short) value);
    598             }
    599             return new ExifAttribute(IFD_FORMAT_USHORT, values.length, buffer.array());
    600         }
    601 
    602         public static ExifAttribute createUShort(int value, ByteOrder byteOrder) {
    603             return createUShort(new int[] {value}, byteOrder);
    604         }
    605 
    606         public static ExifAttribute createULong(long[] values, ByteOrder byteOrder) {
    607             final ByteBuffer buffer = ByteBuffer.wrap(
    608                     new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_ULONG] * values.length]);
    609             buffer.order(byteOrder);
    610             for (long value : values) {
    611                 buffer.putInt((int) value);
    612             }
    613             return new ExifAttribute(IFD_FORMAT_ULONG, values.length, buffer.array());
    614         }
    615 
    616         public static ExifAttribute createULong(long value, ByteOrder byteOrder) {
    617             return createULong(new long[] {value}, byteOrder);
    618         }
    619 
    620         public static ExifAttribute createSLong(int[] values, ByteOrder byteOrder) {
    621             final ByteBuffer buffer = ByteBuffer.wrap(
    622                     new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_SLONG] * values.length]);
    623             buffer.order(byteOrder);
    624             for (int value : values) {
    625                 buffer.putInt(value);
    626             }
    627             return new ExifAttribute(IFD_FORMAT_SLONG, values.length, buffer.array());
    628         }
    629 
    630         public static ExifAttribute createSLong(int value, ByteOrder byteOrder) {
    631             return createSLong(new int[] {value}, byteOrder);
    632         }
    633 
    634         public static ExifAttribute createByte(String value) {
    635             // Exception for GPSAltitudeRef tag
    636             if (value.length() == 1 && value.charAt(0) >= '0' && value.charAt(0) <= '1') {
    637                 final byte[] bytes = new byte[] { (byte) (value.charAt(0) - '0') };
    638                 return new ExifAttribute(IFD_FORMAT_BYTE, bytes.length, bytes);
    639             }
    640             final byte[] ascii = value.getBytes(ASCII);
    641             return new ExifAttribute(IFD_FORMAT_BYTE, ascii.length, ascii);
    642         }
    643 
    644         public static ExifAttribute createString(String value) {
    645             final byte[] ascii = (value + '\0').getBytes(ASCII);
    646             return new ExifAttribute(IFD_FORMAT_STRING, ascii.length, ascii);
    647         }
    648 
    649         public static ExifAttribute createURational(Rational[] values, ByteOrder byteOrder) {
    650             final ByteBuffer buffer = ByteBuffer.wrap(
    651                     new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_URATIONAL] * values.length]);
    652             buffer.order(byteOrder);
    653             for (Rational value : values) {
    654                 buffer.putInt((int) value.numerator);
    655                 buffer.putInt((int) value.denominator);
    656             }
    657             return new ExifAttribute(IFD_FORMAT_URATIONAL, values.length, buffer.array());
    658         }
    659 
    660         public static ExifAttribute createURational(Rational value, ByteOrder byteOrder) {
    661             return createURational(new Rational[] {value}, byteOrder);
    662         }
    663 
    664         public static ExifAttribute createSRational(Rational[] values, ByteOrder byteOrder) {
    665             final ByteBuffer buffer = ByteBuffer.wrap(
    666                     new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_SRATIONAL] * values.length]);
    667             buffer.order(byteOrder);
    668             for (Rational value : values) {
    669                 buffer.putInt((int) value.numerator);
    670                 buffer.putInt((int) value.denominator);
    671             }
    672             return new ExifAttribute(IFD_FORMAT_SRATIONAL, values.length, buffer.array());
    673         }
    674 
    675         public static ExifAttribute createSRational(Rational value, ByteOrder byteOrder) {
    676             return createSRational(new Rational[] {value}, byteOrder);
    677         }
    678 
    679         public static ExifAttribute createDouble(double[] values, ByteOrder byteOrder) {
    680             final ByteBuffer buffer = ByteBuffer.wrap(
    681                     new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_DOUBLE] * values.length]);
    682             buffer.order(byteOrder);
    683             for (double value : values) {
    684                 buffer.putDouble(value);
    685             }
    686             return new ExifAttribute(IFD_FORMAT_DOUBLE, values.length, buffer.array());
    687         }
    688 
    689         public static ExifAttribute createDouble(double value, ByteOrder byteOrder) {
    690             return createDouble(new double[] {value}, byteOrder);
    691         }
    692 
    693         @Override
    694         public String toString() {
    695             return "(" + IFD_FORMAT_NAMES[format] + ", data length:" + bytes.length + ")";
    696         }
    697 
    698         private Object getValue(ByteOrder byteOrder) {
    699             try {
    700                 ByteOrderedDataInputStream inputStream =
    701                         new ByteOrderedDataInputStream(bytes);
    702                 inputStream.setByteOrder(byteOrder);
    703                 switch (format) {
    704                     case IFD_FORMAT_BYTE:
    705                     case IFD_FORMAT_SBYTE: {
    706                         // Exception for GPSAltitudeRef tag
    707                         if (bytes.length == 1 && bytes[0] >= 0 && bytes[0] <= 1) {
    708                             return new String(new char[] { (char) (bytes[0] + '0') });
    709                         }
    710                         return new String(bytes, ASCII);
    711                     }
    712                     case IFD_FORMAT_UNDEFINED:
    713                     case IFD_FORMAT_STRING: {
    714                         int index = 0;
    715                         if (numberOfComponents >= EXIF_ASCII_PREFIX.length) {
    716                             boolean same = true;
    717                             for (int i = 0; i < EXIF_ASCII_PREFIX.length; ++i) {
    718                                 if (bytes[i] != EXIF_ASCII_PREFIX[i]) {
    719                                     same = false;
    720                                     break;
    721                                 }
    722                             }
    723                             if (same) {
    724                                 index = EXIF_ASCII_PREFIX.length;
    725                             }
    726                         }
    727 
    728                         StringBuilder stringBuilder = new StringBuilder();
    729                         while (index < numberOfComponents) {
    730                             int ch = bytes[index];
    731                             if (ch == 0) {
    732                                 break;
    733                             }
    734                             if (ch >= 32) {
    735                                 stringBuilder.append((char) ch);
    736                             } else {
    737                                 stringBuilder.append('?');
    738                             }
    739                             ++index;
    740                         }
    741                         return stringBuilder.toString();
    742                     }
    743                     case IFD_FORMAT_USHORT: {
    744                         final int[] values = new int[numberOfComponents];
    745                         for (int i = 0; i < numberOfComponents; ++i) {
    746                             values[i] = inputStream.readUnsignedShort();
    747                         }
    748                         return values;
    749                     }
    750                     case IFD_FORMAT_ULONG: {
    751                         final long[] values = new long[numberOfComponents];
    752                         for (int i = 0; i < numberOfComponents; ++i) {
    753                             values[i] = inputStream.readUnsignedInt();
    754                         }
    755                         return values;
    756                     }
    757                     case IFD_FORMAT_URATIONAL: {
    758                         final Rational[] values = new Rational[numberOfComponents];
    759                         for (int i = 0; i < numberOfComponents; ++i) {
    760                             final long numerator = inputStream.readUnsignedInt();
    761                             final long denominator = inputStream.readUnsignedInt();
    762                             values[i] = new Rational(numerator, denominator);
    763                         }
    764                         return values;
    765                     }
    766                     case IFD_FORMAT_SSHORT: {
    767                         final int[] values = new int[numberOfComponents];
    768                         for (int i = 0; i < numberOfComponents; ++i) {
    769                             values[i] = inputStream.readShort();
    770                         }
    771                         return values;
    772                     }
    773                     case IFD_FORMAT_SLONG: {
    774                         final int[] values = new int[numberOfComponents];
    775                         for (int i = 0; i < numberOfComponents; ++i) {
    776                             values[i] = inputStream.readInt();
    777                         }
    778                         return values;
    779                     }
    780                     case IFD_FORMAT_SRATIONAL: {
    781                         final Rational[] values = new Rational[numberOfComponents];
    782                         for (int i = 0; i < numberOfComponents; ++i) {
    783                             final long numerator = inputStream.readInt();
    784                             final long denominator = inputStream.readInt();
    785                             values[i] = new Rational(numerator, denominator);
    786                         }
    787                         return values;
    788                     }
    789                     case IFD_FORMAT_SINGLE: {
    790                         final double[] values = new double[numberOfComponents];
    791                         for (int i = 0; i < numberOfComponents; ++i) {
    792                             values[i] = inputStream.readFloat();
    793                         }
    794                         return values;
    795                     }
    796                     case IFD_FORMAT_DOUBLE: {
    797                         final double[] values = new double[numberOfComponents];
    798                         for (int i = 0; i < numberOfComponents; ++i) {
    799                             values[i] = inputStream.readDouble();
    800                         }
    801                         return values;
    802                     }
    803                     default:
    804                         return null;
    805                 }
    806             } catch (IOException e) {
    807                 Log.w(TAG, "IOException occurred during reading a value", e);
    808                 return null;
    809             }
    810         }
    811 
    812         public double getDoubleValue(ByteOrder byteOrder) {
    813             Object value = getValue(byteOrder);
    814             if (value == null) {
    815                 throw new NumberFormatException("NULL can't be converted to a double value");
    816             }
    817             if (value instanceof String) {
    818                 return Double.parseDouble((String) value);
    819             }
    820             if (value instanceof long[]) {
    821                 long[] array = (long[]) value;
    822                 if (array.length == 1) {
    823                     return array[0];
    824                 }
    825                 throw new NumberFormatException("There are more than one component");
    826             }
    827             if (value instanceof int[]) {
    828                 int[] array = (int[]) value;
    829                 if (array.length == 1) {
    830                     return array[0];
    831                 }
    832                 throw new NumberFormatException("There are more than one component");
    833             }
    834             if (value instanceof double[]) {
    835                 double[] array = (double[]) value;
    836                 if (array.length == 1) {
    837                     return array[0];
    838                 }
    839                 throw new NumberFormatException("There are more than one component");
    840             }
    841             if (value instanceof Rational[]) {
    842                 Rational[] array = (Rational[]) value;
    843                 if (array.length == 1) {
    844                     return array[0].calculate();
    845                 }
    846                 throw new NumberFormatException("There are more than one component");
    847             }
    848             throw new NumberFormatException("Couldn't find a double value");
    849         }
    850 
    851         public int getIntValue(ByteOrder byteOrder) {
    852             Object value = getValue(byteOrder);
    853             if (value == null) {
    854                 throw new NumberFormatException("NULL can't be converted to a integer value");
    855             }
    856             if (value instanceof String) {
    857                 return Integer.parseInt((String) value);
    858             }
    859             if (value instanceof long[]) {
    860                 long[] array = (long[]) value;
    861                 if (array.length == 1) {
    862                     return (int) array[0];
    863                 }
    864                 throw new NumberFormatException("There are more than one component");
    865             }
    866             if (value instanceof int[]) {
    867                 int[] array = (int[]) value;
    868                 if (array.length == 1) {
    869                     return array[0];
    870                 }
    871                 throw new NumberFormatException("There are more than one component");
    872             }
    873             throw new NumberFormatException("Couldn't find a integer value");
    874         }
    875 
    876         public String getStringValue(ByteOrder byteOrder) {
    877             Object value = getValue(byteOrder);
    878             if (value == null) {
    879                 return null;
    880             }
    881             if (value instanceof String) {
    882                 return (String) value;
    883             }
    884 
    885             final StringBuilder stringBuilder = new StringBuilder();
    886             if (value instanceof long[]) {
    887                 long[] array = (long[]) value;
    888                 for (int i = 0; i < array.length; ++i) {
    889                     stringBuilder.append(array[i]);
    890                     if (i + 1 != array.length) {
    891                         stringBuilder.append(",");
    892                     }
    893                 }
    894                 return stringBuilder.toString();
    895             }
    896             if (value instanceof int[]) {
    897                 int[] array = (int[]) value;
    898                 for (int i = 0; i < array.length; ++i) {
    899                     stringBuilder.append(array[i]);
    900                     if (i + 1 != array.length) {
    901                         stringBuilder.append(",");
    902                     }
    903                 }
    904                 return stringBuilder.toString();
    905             }
    906             if (value instanceof double[]) {
    907                 double[] array = (double[]) value;
    908                 for (int i = 0; i < array.length; ++i) {
    909                     stringBuilder.append(array[i]);
    910                     if (i + 1 != array.length) {
    911                         stringBuilder.append(",");
    912                     }
    913                 }
    914                 return stringBuilder.toString();
    915             }
    916             if (value instanceof Rational[]) {
    917                 Rational[] array = (Rational[]) value;
    918                 for (int i = 0; i < array.length; ++i) {
    919                     stringBuilder.append(array[i].numerator);
    920                     stringBuilder.append('/');
    921                     stringBuilder.append(array[i].denominator);
    922                     if (i + 1 != array.length) {
    923                         stringBuilder.append(",");
    924                     }
    925                 }
    926                 return stringBuilder.toString();
    927             }
    928             return null;
    929         }
    930 
    931         public int size() {
    932             return IFD_FORMAT_BYTES_PER_FORMAT[format] * numberOfComponents;
    933         }
    934     }
    935 
    936     // A class for indicating EXIF tag.
    937     private static class ExifTag {
    938         public final int number;
    939         public final String name;
    940         public final int primaryFormat;
    941         public final int secondaryFormat;
    942 
    943         private ExifTag(String name, int number, int format) {
    944             this.name = name;
    945             this.number = number;
    946             this.primaryFormat = format;
    947             this.secondaryFormat = -1;
    948         }
    949 
    950         private ExifTag(String name, int number, int primaryFormat, int secondaryFormat) {
    951             this.name = name;
    952             this.number = number;
    953             this.primaryFormat = primaryFormat;
    954             this.secondaryFormat = secondaryFormat;
    955         }
    956     }
    957 
    958     // Primary image IFD TIFF tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
    959     private static final ExifTag[] IFD_TIFF_TAGS = new ExifTag[] {
    960             // For below two, see TIFF 6.0 Spec Section 3: Bilevel Images.
    961             new ExifTag(TAG_NEW_SUBFILE_TYPE, 254, IFD_FORMAT_ULONG),
    962             new ExifTag(TAG_SUBFILE_TYPE, 255, IFD_FORMAT_ULONG),
    963             new ExifTag(TAG_IMAGE_WIDTH, 256, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
    964             new ExifTag(TAG_IMAGE_LENGTH, 257, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
    965             new ExifTag(TAG_BITS_PER_SAMPLE, 258, IFD_FORMAT_USHORT),
    966             new ExifTag(TAG_COMPRESSION, 259, IFD_FORMAT_USHORT),
    967             new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262, IFD_FORMAT_USHORT),
    968             new ExifTag(TAG_IMAGE_DESCRIPTION, 270, IFD_FORMAT_STRING),
    969             new ExifTag(TAG_MAKE, 271, IFD_FORMAT_STRING),
    970             new ExifTag(TAG_MODEL, 272, IFD_FORMAT_STRING),
    971             new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
    972             new ExifTag(TAG_ORIENTATION, 274, IFD_FORMAT_USHORT),
    973             new ExifTag(TAG_SAMPLES_PER_PIXEL, 277, IFD_FORMAT_USHORT),
    974             new ExifTag(TAG_ROWS_PER_STRIP, 278, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
    975             new ExifTag(TAG_STRIP_BYTE_COUNTS, 279, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
    976             new ExifTag(TAG_X_RESOLUTION, 282, IFD_FORMAT_URATIONAL),
    977             new ExifTag(TAG_Y_RESOLUTION, 283, IFD_FORMAT_URATIONAL),
    978             new ExifTag(TAG_PLANAR_CONFIGURATION, 284, IFD_FORMAT_USHORT),
    979             new ExifTag(TAG_RESOLUTION_UNIT, 296, IFD_FORMAT_USHORT),
    980             new ExifTag(TAG_TRANSFER_FUNCTION, 301, IFD_FORMAT_USHORT),
    981             new ExifTag(TAG_SOFTWARE, 305, IFD_FORMAT_STRING),
    982             new ExifTag(TAG_DATETIME, 306, IFD_FORMAT_STRING),
    983             new ExifTag(TAG_ARTIST, 315, IFD_FORMAT_STRING),
    984             new ExifTag(TAG_WHITE_POINT, 318, IFD_FORMAT_URATIONAL),
    985             new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319, IFD_FORMAT_URATIONAL),
    986             // See Adobe PageMaker 6.0 TIFF Technical Notes, Note 1.
    987             new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
    988             new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG),
    989             new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG),
    990             new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529, IFD_FORMAT_URATIONAL),
    991             new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
    992             new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
    993             new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
    994             new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
    995             new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
    996             new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
    997             // RW2 file tags
    998             // See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html)
    999             new ExifTag(TAG_RW2_SENSOR_TOP_BORDER, 4, IFD_FORMAT_ULONG),
   1000             new ExifTag(TAG_RW2_SENSOR_LEFT_BORDER, 5, IFD_FORMAT_ULONG),
   1001             new ExifTag(TAG_RW2_SENSOR_BOTTOM_BORDER, 6, IFD_FORMAT_ULONG),
   1002             new ExifTag(TAG_RW2_SENSOR_RIGHT_BORDER, 7, IFD_FORMAT_ULONG),
   1003             new ExifTag(TAG_RW2_ISO, 23, IFD_FORMAT_USHORT),
   1004             new ExifTag(TAG_RW2_JPG_FROM_RAW, 46, IFD_FORMAT_UNDEFINED)
   1005     };
   1006 
   1007     // Primary image IFD Exif Private tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
   1008     private static final ExifTag[] IFD_EXIF_TAGS = new ExifTag[] {
   1009             new ExifTag(TAG_EXPOSURE_TIME, 33434, IFD_FORMAT_URATIONAL),
   1010             new ExifTag(TAG_F_NUMBER, 33437, IFD_FORMAT_URATIONAL),
   1011             new ExifTag(TAG_EXPOSURE_PROGRAM, 34850, IFD_FORMAT_USHORT),
   1012             new ExifTag(TAG_SPECTRAL_SENSITIVITY, 34852, IFD_FORMAT_STRING),
   1013             new ExifTag(TAG_ISO_SPEED_RATINGS, 34855, IFD_FORMAT_USHORT),
   1014             new ExifTag(TAG_OECF, 34856, IFD_FORMAT_UNDEFINED),
   1015             new ExifTag(TAG_EXIF_VERSION, 36864, IFD_FORMAT_STRING),
   1016             new ExifTag(TAG_DATETIME_ORIGINAL, 36867, IFD_FORMAT_STRING),
   1017             new ExifTag(TAG_DATETIME_DIGITIZED, 36868, IFD_FORMAT_STRING),
   1018             new ExifTag(TAG_COMPONENTS_CONFIGURATION, 37121, IFD_FORMAT_UNDEFINED),
   1019             new ExifTag(TAG_COMPRESSED_BITS_PER_PIXEL, 37122, IFD_FORMAT_URATIONAL),
   1020             new ExifTag(TAG_SHUTTER_SPEED_VALUE, 37377, IFD_FORMAT_SRATIONAL),
   1021             new ExifTag(TAG_APERTURE_VALUE, 37378, IFD_FORMAT_URATIONAL),
   1022             new ExifTag(TAG_BRIGHTNESS_VALUE, 37379, IFD_FORMAT_SRATIONAL),
   1023             new ExifTag(TAG_EXPOSURE_BIAS_VALUE, 37380, IFD_FORMAT_SRATIONAL),
   1024             new ExifTag(TAG_MAX_APERTURE_VALUE, 37381, IFD_FORMAT_URATIONAL),
   1025             new ExifTag(TAG_SUBJECT_DISTANCE, 37382, IFD_FORMAT_URATIONAL),
   1026             new ExifTag(TAG_METERING_MODE, 37383, IFD_FORMAT_USHORT),
   1027             new ExifTag(TAG_LIGHT_SOURCE, 37384, IFD_FORMAT_USHORT),
   1028             new ExifTag(TAG_FLASH, 37385, IFD_FORMAT_USHORT),
   1029             new ExifTag(TAG_FOCAL_LENGTH, 37386, IFD_FORMAT_URATIONAL),
   1030             new ExifTag(TAG_SUBJECT_AREA, 37396, IFD_FORMAT_USHORT),
   1031             new ExifTag(TAG_MAKER_NOTE, 37500, IFD_FORMAT_UNDEFINED),
   1032             new ExifTag(TAG_USER_COMMENT, 37510, IFD_FORMAT_UNDEFINED),
   1033             new ExifTag(TAG_SUBSEC_TIME, 37520, IFD_FORMAT_STRING),
   1034             new ExifTag(TAG_SUBSEC_TIME_ORIG, 37521, IFD_FORMAT_STRING),
   1035             new ExifTag(TAG_SUBSEC_TIME_DIG, 37522, IFD_FORMAT_STRING),
   1036             new ExifTag(TAG_FLASHPIX_VERSION, 40960, IFD_FORMAT_UNDEFINED),
   1037             new ExifTag(TAG_COLOR_SPACE, 40961, IFD_FORMAT_USHORT),
   1038             new ExifTag(TAG_PIXEL_X_DIMENSION, 40962, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
   1039             new ExifTag(TAG_PIXEL_Y_DIMENSION, 40963, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
   1040             new ExifTag(TAG_RELATED_SOUND_FILE, 40964, IFD_FORMAT_STRING),
   1041             new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG),
   1042             new ExifTag(TAG_FLASH_ENERGY, 41483, IFD_FORMAT_URATIONAL),
   1043             new ExifTag(TAG_SPATIAL_FREQUENCY_RESPONSE, 41484, IFD_FORMAT_UNDEFINED),
   1044             new ExifTag(TAG_FOCAL_PLANE_X_RESOLUTION, 41486, IFD_FORMAT_URATIONAL),
   1045             new ExifTag(TAG_FOCAL_PLANE_Y_RESOLUTION, 41487, IFD_FORMAT_URATIONAL),
   1046             new ExifTag(TAG_FOCAL_PLANE_RESOLUTION_UNIT, 41488, IFD_FORMAT_USHORT),
   1047             new ExifTag(TAG_SUBJECT_LOCATION, 41492, IFD_FORMAT_USHORT),
   1048             new ExifTag(TAG_EXPOSURE_INDEX, 41493, IFD_FORMAT_URATIONAL),
   1049             new ExifTag(TAG_SENSING_METHOD, 41495, IFD_FORMAT_USHORT),
   1050             new ExifTag(TAG_FILE_SOURCE, 41728, IFD_FORMAT_UNDEFINED),
   1051             new ExifTag(TAG_SCENE_TYPE, 41729, IFD_FORMAT_UNDEFINED),
   1052             new ExifTag(TAG_CFA_PATTERN, 41730, IFD_FORMAT_UNDEFINED),
   1053             new ExifTag(TAG_CUSTOM_RENDERED, 41985, IFD_FORMAT_USHORT),
   1054             new ExifTag(TAG_EXPOSURE_MODE, 41986, IFD_FORMAT_USHORT),
   1055             new ExifTag(TAG_WHITE_BALANCE, 41987, IFD_FORMAT_USHORT),
   1056             new ExifTag(TAG_DIGITAL_ZOOM_RATIO, 41988, IFD_FORMAT_URATIONAL),
   1057             new ExifTag(TAG_FOCAL_LENGTH_IN_35MM_FILM, 41989, IFD_FORMAT_USHORT),
   1058             new ExifTag(TAG_SCENE_CAPTURE_TYPE, 41990, IFD_FORMAT_USHORT),
   1059             new ExifTag(TAG_GAIN_CONTROL, 41991, IFD_FORMAT_USHORT),
   1060             new ExifTag(TAG_CONTRAST, 41992, IFD_FORMAT_USHORT),
   1061             new ExifTag(TAG_SATURATION, 41993, IFD_FORMAT_USHORT),
   1062             new ExifTag(TAG_SHARPNESS, 41994, IFD_FORMAT_USHORT),
   1063             new ExifTag(TAG_DEVICE_SETTING_DESCRIPTION, 41995, IFD_FORMAT_UNDEFINED),
   1064             new ExifTag(TAG_SUBJECT_DISTANCE_RANGE, 41996, IFD_FORMAT_USHORT),
   1065             new ExifTag(TAG_IMAGE_UNIQUE_ID, 42016, IFD_FORMAT_STRING),
   1066             new ExifTag(TAG_DNG_VERSION, 50706, IFD_FORMAT_BYTE),
   1067             new ExifTag(TAG_DEFAULT_CROP_SIZE, 50720, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG)
   1068     };
   1069 
   1070     // Primary image IFD GPS Info tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
   1071     private static final ExifTag[] IFD_GPS_TAGS = new ExifTag[] {
   1072             new ExifTag(TAG_GPS_VERSION_ID, 0, IFD_FORMAT_BYTE),
   1073             new ExifTag(TAG_GPS_LATITUDE_REF, 1, IFD_FORMAT_STRING),
   1074             new ExifTag(TAG_GPS_LATITUDE, 2, IFD_FORMAT_URATIONAL),
   1075             new ExifTag(TAG_GPS_LONGITUDE_REF, 3, IFD_FORMAT_STRING),
   1076             new ExifTag(TAG_GPS_LONGITUDE, 4, IFD_FORMAT_URATIONAL),
   1077             new ExifTag(TAG_GPS_ALTITUDE_REF, 5, IFD_FORMAT_BYTE),
   1078             new ExifTag(TAG_GPS_ALTITUDE, 6, IFD_FORMAT_URATIONAL),
   1079             new ExifTag(TAG_GPS_TIMESTAMP, 7, IFD_FORMAT_URATIONAL),
   1080             new ExifTag(TAG_GPS_SATELLITES, 8, IFD_FORMAT_STRING),
   1081             new ExifTag(TAG_GPS_STATUS, 9, IFD_FORMAT_STRING),
   1082             new ExifTag(TAG_GPS_MEASURE_MODE, 10, IFD_FORMAT_STRING),
   1083             new ExifTag(TAG_GPS_DOP, 11, IFD_FORMAT_URATIONAL),
   1084             new ExifTag(TAG_GPS_SPEED_REF, 12, IFD_FORMAT_STRING),
   1085             new ExifTag(TAG_GPS_SPEED, 13, IFD_FORMAT_URATIONAL),
   1086             new ExifTag(TAG_GPS_TRACK_REF, 14, IFD_FORMAT_STRING),
   1087             new ExifTag(TAG_GPS_TRACK, 15, IFD_FORMAT_URATIONAL),
   1088             new ExifTag(TAG_GPS_IMG_DIRECTION_REF, 16, IFD_FORMAT_STRING),
   1089             new ExifTag(TAG_GPS_IMG_DIRECTION, 17, IFD_FORMAT_URATIONAL),
   1090             new ExifTag(TAG_GPS_MAP_DATUM, 18, IFD_FORMAT_STRING),
   1091             new ExifTag(TAG_GPS_DEST_LATITUDE_REF, 19, IFD_FORMAT_STRING),
   1092             new ExifTag(TAG_GPS_DEST_LATITUDE, 20, IFD_FORMAT_URATIONAL),
   1093             new ExifTag(TAG_GPS_DEST_LONGITUDE_REF, 21, IFD_FORMAT_STRING),
   1094             new ExifTag(TAG_GPS_DEST_LONGITUDE, 22, IFD_FORMAT_URATIONAL),
   1095             new ExifTag(TAG_GPS_DEST_BEARING_REF, 23, IFD_FORMAT_STRING),
   1096             new ExifTag(TAG_GPS_DEST_BEARING, 24, IFD_FORMAT_URATIONAL),
   1097             new ExifTag(TAG_GPS_DEST_DISTANCE_REF, 25, IFD_FORMAT_STRING),
   1098             new ExifTag(TAG_GPS_DEST_DISTANCE, 26, IFD_FORMAT_URATIONAL),
   1099             new ExifTag(TAG_GPS_PROCESSING_METHOD, 27, IFD_FORMAT_UNDEFINED),
   1100             new ExifTag(TAG_GPS_AREA_INFORMATION, 28, IFD_FORMAT_UNDEFINED),
   1101             new ExifTag(TAG_GPS_DATESTAMP, 29, IFD_FORMAT_STRING),
   1102             new ExifTag(TAG_GPS_DIFFERENTIAL, 30, IFD_FORMAT_USHORT)
   1103     };
   1104     // Primary image IFD Interoperability tag (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
   1105     private static final ExifTag[] IFD_INTEROPERABILITY_TAGS = new ExifTag[] {
   1106             new ExifTag(TAG_INTEROPERABILITY_INDEX, 1, IFD_FORMAT_STRING)
   1107     };
   1108     // IFD Thumbnail tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
   1109     private static final ExifTag[] IFD_THUMBNAIL_TAGS = new ExifTag[] {
   1110             // For below two, see TIFF 6.0 Spec Section 3: Bilevel Images.
   1111             new ExifTag(TAG_NEW_SUBFILE_TYPE, 254, IFD_FORMAT_ULONG),
   1112             new ExifTag(TAG_SUBFILE_TYPE, 255, IFD_FORMAT_ULONG),
   1113             new ExifTag(TAG_THUMBNAIL_IMAGE_WIDTH, 256, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
   1114             new ExifTag(TAG_THUMBNAIL_IMAGE_LENGTH, 257, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
   1115             new ExifTag(TAG_BITS_PER_SAMPLE, 258, IFD_FORMAT_USHORT),
   1116             new ExifTag(TAG_COMPRESSION, 259, IFD_FORMAT_USHORT),
   1117             new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262, IFD_FORMAT_USHORT),
   1118             new ExifTag(TAG_IMAGE_DESCRIPTION, 270, IFD_FORMAT_STRING),
   1119             new ExifTag(TAG_MAKE, 271, IFD_FORMAT_STRING),
   1120             new ExifTag(TAG_MODEL, 272, IFD_FORMAT_STRING),
   1121             new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
   1122             new ExifTag(TAG_ORIENTATION, 274, IFD_FORMAT_USHORT),
   1123             new ExifTag(TAG_SAMPLES_PER_PIXEL, 277, IFD_FORMAT_USHORT),
   1124             new ExifTag(TAG_ROWS_PER_STRIP, 278, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
   1125             new ExifTag(TAG_STRIP_BYTE_COUNTS, 279, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
   1126             new ExifTag(TAG_X_RESOLUTION, 282, IFD_FORMAT_URATIONAL),
   1127             new ExifTag(TAG_Y_RESOLUTION, 283, IFD_FORMAT_URATIONAL),
   1128             new ExifTag(TAG_PLANAR_CONFIGURATION, 284, IFD_FORMAT_USHORT),
   1129             new ExifTag(TAG_RESOLUTION_UNIT, 296, IFD_FORMAT_USHORT),
   1130             new ExifTag(TAG_TRANSFER_FUNCTION, 301, IFD_FORMAT_USHORT),
   1131             new ExifTag(TAG_SOFTWARE, 305, IFD_FORMAT_STRING),
   1132             new ExifTag(TAG_DATETIME, 306, IFD_FORMAT_STRING),
   1133             new ExifTag(TAG_ARTIST, 315, IFD_FORMAT_STRING),
   1134             new ExifTag(TAG_WHITE_POINT, 318, IFD_FORMAT_URATIONAL),
   1135             new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319, IFD_FORMAT_URATIONAL),
   1136             // See Adobe PageMaker 6.0 TIFF Technical Notes, Note 1.
   1137             new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
   1138             new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG),
   1139             new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG),
   1140             new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529, IFD_FORMAT_URATIONAL),
   1141             new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
   1142             new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
   1143             new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
   1144             new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
   1145             new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
   1146             new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
   1147             new ExifTag(TAG_DNG_VERSION, 50706, IFD_FORMAT_BYTE),
   1148             new ExifTag(TAG_DEFAULT_CROP_SIZE, 50720, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG)
   1149     };
   1150 
   1151     // RAF file tag (See piex.cc line 372)
   1152     private static final ExifTag TAG_RAF_IMAGE_SIZE =
   1153             new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT);
   1154 
   1155     // ORF file tags (See http://www.exiv2.org/tags-olympus.html)
   1156     private static final ExifTag[] ORF_MAKER_NOTE_TAGS = new ExifTag[] {
   1157             new ExifTag(TAG_ORF_THUMBNAIL_IMAGE, 256, IFD_FORMAT_UNDEFINED),
   1158             new ExifTag(TAG_ORF_CAMERA_SETTINGS_IFD_POINTER, 8224, IFD_FORMAT_ULONG),
   1159             new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_ULONG)
   1160     };
   1161     private static final ExifTag[] ORF_CAMERA_SETTINGS_TAGS = new ExifTag[] {
   1162             new ExifTag(TAG_ORF_PREVIEW_IMAGE_START, 257, IFD_FORMAT_ULONG),
   1163             new ExifTag(TAG_ORF_PREVIEW_IMAGE_LENGTH, 258, IFD_FORMAT_ULONG)
   1164     };
   1165     private static final ExifTag[] ORF_IMAGE_PROCESSING_TAGS = new ExifTag[] {
   1166             new ExifTag(TAG_ORF_ASPECT_FRAME, 4371, IFD_FORMAT_USHORT)
   1167     };
   1168     // PEF file tag (See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Pentax.html)
   1169     private static final ExifTag[] PEF_TAGS = new ExifTag[] {
   1170             new ExifTag(TAG_COLOR_SPACE, 55, IFD_FORMAT_USHORT)
   1171     };
   1172 
   1173     // See JEITA CP-3451C Section 4.6.3: Exif-specific IFD.
   1174     // The following values are used for indicating pointers to the other Image File Directories.
   1175 
   1176     // Indices of Exif Ifd tag groups
   1177     /** @hide */
   1178     @Retention(RetentionPolicy.SOURCE)
   1179     @IntDef({IFD_TYPE_PRIMARY, IFD_TYPE_EXIF, IFD_TYPE_GPS, IFD_TYPE_INTEROPERABILITY,
   1180             IFD_TYPE_THUMBNAIL, IFD_TYPE_PREVIEW, IFD_TYPE_ORF_MAKER_NOTE,
   1181             IFD_TYPE_ORF_CAMERA_SETTINGS, IFD_TYPE_ORF_IMAGE_PROCESSING, IFD_TYPE_PEF})
   1182     public @interface IfdType {}
   1183 
   1184     private static final int IFD_TYPE_PRIMARY = 0;
   1185     private static final int IFD_TYPE_EXIF = 1;
   1186     private static final int IFD_TYPE_GPS = 2;
   1187     private static final int IFD_TYPE_INTEROPERABILITY = 3;
   1188     private static final int IFD_TYPE_THUMBNAIL = 4;
   1189     private static final int IFD_TYPE_PREVIEW = 5;
   1190     private static final int IFD_TYPE_ORF_MAKER_NOTE = 6;
   1191     private static final int IFD_TYPE_ORF_CAMERA_SETTINGS = 7;
   1192     private static final int IFD_TYPE_ORF_IMAGE_PROCESSING = 8;
   1193     private static final int IFD_TYPE_PEF = 9;
   1194 
   1195     // List of Exif tag groups
   1196     private static final ExifTag[][] EXIF_TAGS = new ExifTag[][] {
   1197             IFD_TIFF_TAGS, IFD_EXIF_TAGS, IFD_GPS_TAGS, IFD_INTEROPERABILITY_TAGS,
   1198             IFD_THUMBNAIL_TAGS, IFD_TIFF_TAGS, ORF_MAKER_NOTE_TAGS, ORF_CAMERA_SETTINGS_TAGS,
   1199             ORF_IMAGE_PROCESSING_TAGS, PEF_TAGS
   1200     };
   1201     // List of tags for pointing to the other image file directory offset.
   1202     private static final ExifTag[] EXIF_POINTER_TAGS = new ExifTag[] {
   1203             new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
   1204             new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
   1205             new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
   1206             new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG),
   1207             new ExifTag(TAG_ORF_CAMERA_SETTINGS_IFD_POINTER, 8224, IFD_FORMAT_BYTE),
   1208             new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_BYTE)
   1209     };
   1210 
   1211     // Tags for indicating the thumbnail offset and length
   1212     private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG =
   1213             new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG);
   1214     private static final ExifTag JPEG_INTERCHANGE_FORMAT_LENGTH_TAG =
   1215             new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG);
   1216 
   1217     // Mappings from tag number to tag name and each item represents one IFD tag group.
   1218     private static final HashMap[] sExifTagMapsForReading = new HashMap[EXIF_TAGS.length];
   1219     // Mappings from tag name to tag number and each item represents one IFD tag group.
   1220     private static final HashMap[] sExifTagMapsForWriting = new HashMap[EXIF_TAGS.length];
   1221     private static final HashSet<String> sTagSetForCompatibility = new HashSet<>(Arrays.asList(
   1222             TAG_F_NUMBER, TAG_DIGITAL_ZOOM_RATIO, TAG_EXPOSURE_TIME, TAG_SUBJECT_DISTANCE,
   1223             TAG_GPS_TIMESTAMP));
   1224     // Mappings from tag number to IFD type for pointer tags.
   1225     private static final HashMap<Integer, Integer> sExifPointerTagMap = new HashMap();
   1226 
   1227     // See JPEG File Interchange Format Version 1.02.
   1228     // The following values are defined for handling JPEG streams. In this implementation, we are
   1229     // not only getting information from EXIF but also from some JPEG special segments such as
   1230     // MARKER_COM for user comment and MARKER_SOFx for image width and height.
   1231 
   1232     private static final Charset ASCII = Charset.forName("US-ASCII");
   1233     // Identifier for EXIF APP1 segment in JPEG
   1234     private static final byte[] IDENTIFIER_EXIF_APP1 = "Exif\0\0".getBytes(ASCII);
   1235     // JPEG segment markers, that each marker consumes two bytes beginning with 0xff and ending with
   1236     // the indicator. There is no SOF4, SOF8, SOF16 markers in JPEG and SOFx markers indicates start
   1237     // of frame(baseline DCT) and the image size info exists in its beginning part.
   1238     private static final byte MARKER = (byte) 0xff;
   1239     private static final byte MARKER_SOI = (byte) 0xd8;
   1240     private static final byte MARKER_SOF0 = (byte) 0xc0;
   1241     private static final byte MARKER_SOF1 = (byte) 0xc1;
   1242     private static final byte MARKER_SOF2 = (byte) 0xc2;
   1243     private static final byte MARKER_SOF3 = (byte) 0xc3;
   1244     private static final byte MARKER_SOF5 = (byte) 0xc5;
   1245     private static final byte MARKER_SOF6 = (byte) 0xc6;
   1246     private static final byte MARKER_SOF7 = (byte) 0xc7;
   1247     private static final byte MARKER_SOF9 = (byte) 0xc9;
   1248     private static final byte MARKER_SOF10 = (byte) 0xca;
   1249     private static final byte MARKER_SOF11 = (byte) 0xcb;
   1250     private static final byte MARKER_SOF13 = (byte) 0xcd;
   1251     private static final byte MARKER_SOF14 = (byte) 0xce;
   1252     private static final byte MARKER_SOF15 = (byte) 0xcf;
   1253     private static final byte MARKER_SOS = (byte) 0xda;
   1254     private static final byte MARKER_APP1 = (byte) 0xe1;
   1255     private static final byte MARKER_COM = (byte) 0xfe;
   1256     private static final byte MARKER_EOI = (byte) 0xd9;
   1257 
   1258     // Supported Image File Types
   1259     private static final int IMAGE_TYPE_UNKNOWN = 0;
   1260     private static final int IMAGE_TYPE_ARW = 1;
   1261     private static final int IMAGE_TYPE_CR2 = 2;
   1262     private static final int IMAGE_TYPE_DNG = 3;
   1263     private static final int IMAGE_TYPE_JPEG = 4;
   1264     private static final int IMAGE_TYPE_NEF = 5;
   1265     private static final int IMAGE_TYPE_NRW = 6;
   1266     private static final int IMAGE_TYPE_ORF = 7;
   1267     private static final int IMAGE_TYPE_PEF = 8;
   1268     private static final int IMAGE_TYPE_RAF = 9;
   1269     private static final int IMAGE_TYPE_RW2 = 10;
   1270     private static final int IMAGE_TYPE_SRW = 11;
   1271     private static final int IMAGE_TYPE_HEIF = 12;
   1272 
   1273     static {
   1274         sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
   1275         sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
   1276 
   1277         // Build up the hash tables to look up Exif tags for reading Exif tags.
   1278         for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
   1279             sExifTagMapsForReading[ifdType] = new HashMap();
   1280             sExifTagMapsForWriting[ifdType] = new HashMap();
   1281             for (ExifTag tag : EXIF_TAGS[ifdType]) {
   1282                 sExifTagMapsForReading[ifdType].put(tag.number, tag);
   1283                 sExifTagMapsForWriting[ifdType].put(tag.name, tag);
   1284             }
   1285         }
   1286 
   1287         // Build up the hash table to look up Exif pointer tags.
   1288         sExifPointerTagMap.put(EXIF_POINTER_TAGS[0].number, IFD_TYPE_PREVIEW); // 330
   1289         sExifPointerTagMap.put(EXIF_POINTER_TAGS[1].number, IFD_TYPE_EXIF); // 34665
   1290         sExifPointerTagMap.put(EXIF_POINTER_TAGS[2].number, IFD_TYPE_GPS); // 34853
   1291         sExifPointerTagMap.put(EXIF_POINTER_TAGS[3].number, IFD_TYPE_INTEROPERABILITY); // 40965
   1292         sExifPointerTagMap.put(EXIF_POINTER_TAGS[4].number, IFD_TYPE_ORF_CAMERA_SETTINGS); // 8224
   1293         sExifPointerTagMap.put(EXIF_POINTER_TAGS[5].number, IFD_TYPE_ORF_IMAGE_PROCESSING); // 8256
   1294     }
   1295 
   1296     private final String mFilename;
   1297     private final FileDescriptor mSeekableFileDescriptor;
   1298     private final AssetManager.AssetInputStream mAssetInputStream;
   1299     private final boolean mIsInputStream;
   1300     private int mMimeType;
   1301     private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
   1302     private Set<Integer> mAttributesOffsets = new HashSet<>(EXIF_TAGS.length);
   1303     private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN;
   1304     private boolean mHasThumbnail;
   1305     // The following values used for indicating a thumbnail position.
   1306     private int mThumbnailOffset;
   1307     private int mThumbnailLength;
   1308     private byte[] mThumbnailBytes;
   1309     private int mThumbnailCompression;
   1310     private int mExifOffset;
   1311     private int mOrfMakerNoteOffset;
   1312     private int mOrfThumbnailOffset;
   1313     private int mOrfThumbnailLength;
   1314     private int mRw2JpgFromRawOffset;
   1315     private boolean mIsSupportedFile;
   1316 
   1317     // Pattern to check non zero timestamp
   1318     private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
   1319     // Pattern to check gps timestamp
   1320     private static final Pattern sGpsTimestampPattern =
   1321             Pattern.compile("^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$");
   1322 
   1323     /**
   1324      * Reads Exif tags from the specified image file.
   1325      */
   1326     public ExifInterface(String filename) throws IOException {
   1327         if (filename == null) {
   1328             throw new IllegalArgumentException("filename cannot be null");
   1329         }
   1330         FileInputStream in = null;
   1331         mAssetInputStream = null;
   1332         mFilename = filename;
   1333         mIsInputStream = false;
   1334         try {
   1335             in = new FileInputStream(filename);
   1336             if (isSeekableFD(in.getFD())) {
   1337                 mSeekableFileDescriptor = in.getFD();
   1338             } else {
   1339                 mSeekableFileDescriptor = null;
   1340             }
   1341             loadAttributes(in);
   1342         } finally {
   1343             IoUtils.closeQuietly(in);
   1344         }
   1345     }
   1346 
   1347     /**
   1348      * Reads Exif tags from the specified image file descriptor. Attribute mutation is supported
   1349      * for writable and seekable file descriptors only. This constructor will not rewind the offset
   1350      * of the given file descriptor. Developers should close the file descriptor after use.
   1351      */
   1352     public ExifInterface(FileDescriptor fileDescriptor) throws IOException {
   1353         if (fileDescriptor == null) {
   1354             throw new IllegalArgumentException("fileDescriptor cannot be null");
   1355         }
   1356         mAssetInputStream = null;
   1357         mFilename = null;
   1358         if (isSeekableFD(fileDescriptor)) {
   1359             mSeekableFileDescriptor = fileDescriptor;
   1360             // Keep the original file descriptor in order to save attributes when it's seekable.
   1361             // Otherwise, just close the given file descriptor after reading it because the save
   1362             // feature won't be working.
   1363             try {
   1364                 fileDescriptor = Os.dup(fileDescriptor);
   1365             } catch (ErrnoException e) {
   1366                 throw e.rethrowAsIOException();
   1367             }
   1368         } else {
   1369             mSeekableFileDescriptor = null;
   1370         }
   1371         mIsInputStream = false;
   1372         FileInputStream in = null;
   1373         try {
   1374             in = new FileInputStream(fileDescriptor);
   1375             loadAttributes(in);
   1376         } finally {
   1377             IoUtils.closeQuietly(in);
   1378         }
   1379     }
   1380 
   1381     /**
   1382      * Reads Exif tags from the specified image input stream. Attribute mutation is not supported
   1383      * for input streams. The given input stream will proceed its current position. Developers
   1384      * should close the input stream after use.
   1385      */
   1386     public ExifInterface(InputStream inputStream) throws IOException {
   1387         if (inputStream == null) {
   1388             throw new IllegalArgumentException("inputStream cannot be null");
   1389         }
   1390         mFilename = null;
   1391         if (inputStream instanceof AssetManager.AssetInputStream) {
   1392             mAssetInputStream = (AssetManager.AssetInputStream) inputStream;
   1393             mSeekableFileDescriptor = null;
   1394         } else if (inputStream instanceof FileInputStream
   1395                 && isSeekableFD(((FileInputStream) inputStream).getFD())) {
   1396             mAssetInputStream = null;
   1397             mSeekableFileDescriptor = ((FileInputStream) inputStream).getFD();
   1398         } else {
   1399             mAssetInputStream = null;
   1400             mSeekableFileDescriptor = null;
   1401         }
   1402         mIsInputStream = true;
   1403         loadAttributes(inputStream);
   1404     }
   1405 
   1406     /**
   1407      * Returns the EXIF attribute of the specified tag or {@code null} if there is no such tag in
   1408      * the image file.
   1409      *
   1410      * @param tag the name of the tag.
   1411      */
   1412     private ExifAttribute getExifAttribute(String tag) {
   1413         // Retrieves all tag groups. The value from primary image tag group has a higher priority
   1414         // than the value from the thumbnail tag group if there are more than one candidates.
   1415         for (int i = 0; i < EXIF_TAGS.length; ++i) {
   1416             Object value = mAttributes[i].get(tag);
   1417             if (value != null) {
   1418                 return (ExifAttribute) value;
   1419             }
   1420         }
   1421         return null;
   1422     }
   1423 
   1424     /**
   1425      * Returns the value of the specified tag or {@code null} if there
   1426      * is no such tag in the image file.
   1427      *
   1428      * @param tag the name of the tag.
   1429      */
   1430     public String getAttribute(String tag) {
   1431         ExifAttribute attribute = getExifAttribute(tag);
   1432         if (attribute != null) {
   1433             if (!sTagSetForCompatibility.contains(tag)) {
   1434                 return attribute.getStringValue(mExifByteOrder);
   1435             }
   1436             if (tag.equals(TAG_GPS_TIMESTAMP)) {
   1437                 // Convert the rational values to the custom formats for backwards compatibility.
   1438                 if (attribute.format != IFD_FORMAT_URATIONAL
   1439                         && attribute.format != IFD_FORMAT_SRATIONAL) {
   1440                     return null;
   1441                 }
   1442                 Rational[] array = (Rational[]) attribute.getValue(mExifByteOrder);
   1443                 if (array.length != 3) {
   1444                     return null;
   1445                 }
   1446                 return String.format("%02d:%02d:%02d",
   1447                         (int) ((float) array[0].numerator / array[0].denominator),
   1448                         (int) ((float) array[1].numerator / array[1].denominator),
   1449                         (int) ((float) array[2].numerator / array[2].denominator));
   1450             }
   1451             try {
   1452                 return Double.toString(attribute.getDoubleValue(mExifByteOrder));
   1453             } catch (NumberFormatException e) {
   1454                 return null;
   1455             }
   1456         }
   1457         return null;
   1458     }
   1459 
   1460     /**
   1461      * Returns the integer value of the specified tag. If there is no such tag
   1462      * in the image file or the value cannot be parsed as integer, return
   1463      * <var>defaultValue</var>.
   1464      *
   1465      * @param tag the name of the tag.
   1466      * @param defaultValue the value to return if the tag is not available.
   1467      */
   1468     public int getAttributeInt(String tag, int defaultValue) {
   1469         ExifAttribute exifAttribute = getExifAttribute(tag);
   1470         if (exifAttribute == null) {
   1471             return defaultValue;
   1472         }
   1473 
   1474         try {
   1475             return exifAttribute.getIntValue(mExifByteOrder);
   1476         } catch (NumberFormatException e) {
   1477             return defaultValue;
   1478         }
   1479     }
   1480 
   1481     /**
   1482      * Returns the double value of the tag that is specified as rational or contains a
   1483      * double-formatted value. If there is no such tag in the image file or the value cannot be
   1484      * parsed as double, return <var>defaultValue</var>.
   1485      *
   1486      * @param tag the name of the tag.
   1487      * @param defaultValue the value to return if the tag is not available.
   1488      */
   1489     public double getAttributeDouble(String tag, double defaultValue) {
   1490         ExifAttribute exifAttribute = getExifAttribute(tag);
   1491         if (exifAttribute == null) {
   1492             return defaultValue;
   1493         }
   1494 
   1495         try {
   1496             return exifAttribute.getDoubleValue(mExifByteOrder);
   1497         } catch (NumberFormatException e) {
   1498             return defaultValue;
   1499         }
   1500     }
   1501 
   1502     /**
   1503      * Set the value of the specified tag.
   1504      *
   1505      * @param tag the name of the tag.
   1506      * @param value the value of the tag.
   1507      */
   1508     public void setAttribute(String tag, String value) {
   1509         // Convert the given value to rational values for backwards compatibility.
   1510         if (value != null && sTagSetForCompatibility.contains(tag)) {
   1511             if (tag.equals(TAG_GPS_TIMESTAMP)) {
   1512                 Matcher m = sGpsTimestampPattern.matcher(value);
   1513                 if (!m.find()) {
   1514                     Log.w(TAG, "Invalid value for " + tag + " : " + value);
   1515                     return;
   1516                 }
   1517                 value = Integer.parseInt(m.group(1)) + "/1," + Integer.parseInt(m.group(2)) + "/1,"
   1518                         + Integer.parseInt(m.group(3)) + "/1";
   1519             } else {
   1520                 try {
   1521                     double doubleValue = Double.parseDouble(value);
   1522                     value = (long) (doubleValue * 10000L) + "/10000";
   1523                 } catch (NumberFormatException e) {
   1524                     Log.w(TAG, "Invalid value for " + tag + " : " + value);
   1525                     return;
   1526                 }
   1527             }
   1528         }
   1529 
   1530         for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
   1531             if (i == IFD_TYPE_THUMBNAIL && !mHasThumbnail) {
   1532                 continue;
   1533             }
   1534             final Object obj = sExifTagMapsForWriting[i].get(tag);
   1535             if (obj != null) {
   1536                 if (value == null) {
   1537                     mAttributes[i].remove(tag);
   1538                     continue;
   1539                 }
   1540                 final ExifTag exifTag = (ExifTag) obj;
   1541                 Pair<Integer, Integer> guess = guessDataFormat(value);
   1542                 int dataFormat;
   1543                 if (exifTag.primaryFormat == guess.first || exifTag.primaryFormat == guess.second) {
   1544                     dataFormat = exifTag.primaryFormat;
   1545                 } else if (exifTag.secondaryFormat != -1 && (exifTag.secondaryFormat == guess.first
   1546                         || exifTag.secondaryFormat == guess.second)) {
   1547                     dataFormat = exifTag.secondaryFormat;
   1548                 } else if (exifTag.primaryFormat == IFD_FORMAT_BYTE
   1549                         || exifTag.primaryFormat == IFD_FORMAT_UNDEFINED
   1550                         || exifTag.primaryFormat == IFD_FORMAT_STRING) {
   1551                     dataFormat = exifTag.primaryFormat;
   1552                 } else {
   1553                     Log.w(TAG, "Given tag (" + tag + ") value didn't match with one of expected "
   1554                             + "formats: " + IFD_FORMAT_NAMES[exifTag.primaryFormat]
   1555                             + (exifTag.secondaryFormat == -1 ? "" : ", "
   1556                             + IFD_FORMAT_NAMES[exifTag.secondaryFormat]) + " (guess: "
   1557                             + IFD_FORMAT_NAMES[guess.first] + (guess.second == -1 ? "" : ", "
   1558                             + IFD_FORMAT_NAMES[guess.second]) + ")");
   1559                     continue;
   1560                 }
   1561                 switch (dataFormat) {
   1562                     case IFD_FORMAT_BYTE: {
   1563                         mAttributes[i].put(tag, ExifAttribute.createByte(value));
   1564                         break;
   1565                     }
   1566                     case IFD_FORMAT_UNDEFINED:
   1567                     case IFD_FORMAT_STRING: {
   1568                         mAttributes[i].put(tag, ExifAttribute.createString(value));
   1569                         break;
   1570                     }
   1571                     case IFD_FORMAT_USHORT: {
   1572                         final String[] values = value.split(",");
   1573                         final int[] intArray = new int[values.length];
   1574                         for (int j = 0; j < values.length; ++j) {
   1575                             intArray[j] = Integer.parseInt(values[j]);
   1576                         }
   1577                         mAttributes[i].put(tag,
   1578                                 ExifAttribute.createUShort(intArray, mExifByteOrder));
   1579                         break;
   1580                     }
   1581                     case IFD_FORMAT_SLONG: {
   1582                         final String[] values = value.split(",");
   1583                         final int[] intArray = new int[values.length];
   1584                         for (int j = 0; j < values.length; ++j) {
   1585                             intArray[j] = Integer.parseInt(values[j]);
   1586                         }
   1587                         mAttributes[i].put(tag,
   1588                                 ExifAttribute.createSLong(intArray, mExifByteOrder));
   1589                         break;
   1590                     }
   1591                     case IFD_FORMAT_ULONG: {
   1592                         final String[] values = value.split(",");
   1593                         final long[] longArray = new long[values.length];
   1594                         for (int j = 0; j < values.length; ++j) {
   1595                             longArray[j] = Long.parseLong(values[j]);
   1596                         }
   1597                         mAttributes[i].put(tag,
   1598                                 ExifAttribute.createULong(longArray, mExifByteOrder));
   1599                         break;
   1600                     }
   1601                     case IFD_FORMAT_URATIONAL: {
   1602                         final String[] values = value.split(",");
   1603                         final Rational[] rationalArray = new Rational[values.length];
   1604                         for (int j = 0; j < values.length; ++j) {
   1605                             final String[] numbers = values[j].split("/");
   1606                             rationalArray[j] = new Rational((long) Double.parseDouble(numbers[0]),
   1607                                     (long) Double.parseDouble(numbers[1]));
   1608                         }
   1609                         mAttributes[i].put(tag,
   1610                                 ExifAttribute.createURational(rationalArray, mExifByteOrder));
   1611                         break;
   1612                     }
   1613                     case IFD_FORMAT_SRATIONAL: {
   1614                         final String[] values = value.split(",");
   1615                         final Rational[] rationalArray = new Rational[values.length];
   1616                         for (int j = 0; j < values.length; ++j) {
   1617                             final String[] numbers = values[j].split("/");
   1618                             rationalArray[j] = new Rational((long) Double.parseDouble(numbers[0]),
   1619                                     (long) Double.parseDouble(numbers[1]));
   1620                         }
   1621                         mAttributes[i].put(tag,
   1622                                 ExifAttribute.createSRational(rationalArray, mExifByteOrder));
   1623                         break;
   1624                     }
   1625                     case IFD_FORMAT_DOUBLE: {
   1626                         final String[] values = value.split(",");
   1627                         final double[] doubleArray = new double[values.length];
   1628                         for (int j = 0; j < values.length; ++j) {
   1629                             doubleArray[j] = Double.parseDouble(values[j]);
   1630                         }
   1631                         mAttributes[i].put(tag,
   1632                                 ExifAttribute.createDouble(doubleArray, mExifByteOrder));
   1633                         break;
   1634                     }
   1635                     default:
   1636                         Log.w(TAG, "Data format isn't one of expected formats: " + dataFormat);
   1637                         continue;
   1638                 }
   1639             }
   1640         }
   1641     }
   1642 
   1643     /**
   1644      * Update the values of the tags in the tag groups if any value for the tag already was stored.
   1645      *
   1646      * @param tag the name of the tag.
   1647      * @param value the value of the tag in a form of {@link ExifAttribute}.
   1648      * @return Returns {@code true} if updating is placed.
   1649      */
   1650     private boolean updateAttribute(String tag, ExifAttribute value) {
   1651         boolean updated = false;
   1652         for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
   1653             if (mAttributes[i].containsKey(tag)) {
   1654                 mAttributes[i].put(tag, value);
   1655                 updated = true;
   1656             }
   1657         }
   1658         return updated;
   1659     }
   1660 
   1661     /**
   1662      * Remove any values of the specified tag.
   1663      *
   1664      * @param tag the name of the tag.
   1665      */
   1666     private void removeAttribute(String tag) {
   1667         for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
   1668             mAttributes[i].remove(tag);
   1669         }
   1670     }
   1671 
   1672     /**
   1673      * This function decides which parser to read the image data according to the given input stream
   1674      * type and the content of the input stream. In each case, it reads the first three bytes to
   1675      * determine whether the image data format is JPEG or not.
   1676      */
   1677     private void loadAttributes(@NonNull InputStream in) throws IOException {
   1678         try {
   1679             // Initialize mAttributes.
   1680             for (int i = 0; i < EXIF_TAGS.length; ++i) {
   1681                 mAttributes[i] = new HashMap();
   1682             }
   1683 
   1684             // Check file type
   1685             in = new BufferedInputStream(in, SIGNATURE_CHECK_SIZE);
   1686             mMimeType = getMimeType((BufferedInputStream) in);
   1687 
   1688             // Create byte-ordered input stream
   1689             ByteOrderedDataInputStream inputStream = new ByteOrderedDataInputStream(in);
   1690 
   1691             switch (mMimeType) {
   1692                 case IMAGE_TYPE_JPEG: {
   1693                     getJpegAttributes(inputStream, 0, IFD_TYPE_PRIMARY); // 0 is offset
   1694                     break;
   1695                 }
   1696                 case IMAGE_TYPE_RAF: {
   1697                     getRafAttributes(inputStream);
   1698                     break;
   1699                 }
   1700                 case IMAGE_TYPE_HEIF: {
   1701                     getHeifAttributes(inputStream);
   1702                     break;
   1703                 }
   1704                 case IMAGE_TYPE_ORF: {
   1705                     getOrfAttributes(inputStream);
   1706                     break;
   1707                 }
   1708                 case IMAGE_TYPE_RW2: {
   1709                     getRw2Attributes(inputStream);
   1710                     break;
   1711                 }
   1712                 case IMAGE_TYPE_ARW:
   1713                 case IMAGE_TYPE_CR2:
   1714                 case IMAGE_TYPE_DNG:
   1715                 case IMAGE_TYPE_NEF:
   1716                 case IMAGE_TYPE_NRW:
   1717                 case IMAGE_TYPE_PEF:
   1718                 case IMAGE_TYPE_SRW:
   1719                 case IMAGE_TYPE_UNKNOWN: {
   1720                     getRawAttributes(inputStream);
   1721                     break;
   1722                 }
   1723                 default: {
   1724                     break;
   1725                 }
   1726             }
   1727             // Set thumbnail image offset and length
   1728             setThumbnailData(inputStream);
   1729             mIsSupportedFile = true;
   1730         } catch (IOException e) {
   1731             // Ignore exceptions in order to keep the compatibility with the old versions of
   1732             // ExifInterface.
   1733             mIsSupportedFile = false;
   1734             if (DEBUG) {
   1735                 Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file"
   1736                         + "(ExifInterface supports JPEG and some RAW image formats only) "
   1737                         + "or a corrupted JPEG file to ExifInterface.", e);
   1738             }
   1739         } finally {
   1740             addDefaultValuesForCompatibility();
   1741 
   1742             if (DEBUG) {
   1743                 printAttributes();
   1744             }
   1745         }
   1746     }
   1747 
   1748     private static boolean isSeekableFD(FileDescriptor fd) throws IOException {
   1749         try {
   1750             Os.lseek(fd, 0, OsConstants.SEEK_CUR);
   1751             return true;
   1752         } catch (ErrnoException e) {
   1753             return false;
   1754         }
   1755     }
   1756 
   1757     // Prints out attributes for debugging.
   1758     private void printAttributes() {
   1759         for (int i = 0; i < mAttributes.length; ++i) {
   1760             Log.d(TAG, "The size of tag group[" + i + "]: " + mAttributes[i].size());
   1761             for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
   1762                 final ExifAttribute tagValue = (ExifAttribute) entry.getValue();
   1763                 Log.d(TAG, "tagName: " + entry.getKey() + ", tagType: " + tagValue.toString()
   1764                         + ", tagValue: '" + tagValue.getStringValue(mExifByteOrder) + "'");
   1765             }
   1766         }
   1767     }
   1768 
   1769     /**
   1770      * Save the tag data into the original image file. This is expensive because it involves
   1771      * copying all the data from one file to another and deleting the old file and renaming the
   1772      * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write
   1773      * and make a single call rather than multiple calls for each attribute.
   1774      * <p>
   1775      * This method is only supported for JPEG files.
   1776      * </p>
   1777      */
   1778     public void saveAttributes() throws IOException {
   1779         if (!mIsSupportedFile || mMimeType != IMAGE_TYPE_JPEG) {
   1780             throw new IOException("ExifInterface only supports saving attributes on JPEG formats.");
   1781         }
   1782         if (mIsInputStream || (mSeekableFileDescriptor == null && mFilename == null)) {
   1783             throw new IOException(
   1784                     "ExifInterface does not support saving attributes for the current input.");
   1785         }
   1786 
   1787         // Keep the thumbnail in memory
   1788         mThumbnailBytes = getThumbnail();
   1789 
   1790         FileInputStream in = null;
   1791         FileOutputStream out = null;
   1792         File tempFile = null;
   1793         try {
   1794             // Move the original file to temporary file.
   1795             if (mFilename != null) {
   1796                 tempFile = new File(mFilename + ".tmp");
   1797                 File originalFile = new File(mFilename);
   1798                 if (!originalFile.renameTo(tempFile)) {
   1799                     throw new IOException("Could'nt rename to " + tempFile.getAbsolutePath());
   1800                 }
   1801             } else if (mSeekableFileDescriptor != null) {
   1802                 tempFile = File.createTempFile("temp", "jpg");
   1803                 Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
   1804                 in = new FileInputStream(mSeekableFileDescriptor);
   1805                 out = new FileOutputStream(tempFile);
   1806                 Streams.copy(in, out);
   1807             }
   1808         } catch (ErrnoException e) {
   1809             throw e.rethrowAsIOException();
   1810         } finally {
   1811             IoUtils.closeQuietly(in);
   1812             IoUtils.closeQuietly(out);
   1813         }
   1814 
   1815         in = null;
   1816         out = null;
   1817         try {
   1818             // Save the new file.
   1819             in = new FileInputStream(tempFile);
   1820             if (mFilename != null) {
   1821                 out = new FileOutputStream(mFilename);
   1822             } else if (mSeekableFileDescriptor != null) {
   1823                 Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
   1824                 out = new FileOutputStream(mSeekableFileDescriptor);
   1825             }
   1826             saveJpegAttributes(in, out);
   1827         } catch (ErrnoException e) {
   1828             throw e.rethrowAsIOException();
   1829         } finally {
   1830             IoUtils.closeQuietly(in);
   1831             IoUtils.closeQuietly(out);
   1832             tempFile.delete();
   1833         }
   1834 
   1835         // Discard the thumbnail in memory
   1836         mThumbnailBytes = null;
   1837     }
   1838 
   1839     /**
   1840      * Returns true if the image file has a thumbnail.
   1841      */
   1842     public boolean hasThumbnail() {
   1843         return mHasThumbnail;
   1844     }
   1845 
   1846     /**
   1847      * Returns the JPEG compressed thumbnail inside the image file, or {@code null} if there is no
   1848      * JPEG compressed thumbnail.
   1849      * The returned data can be decoded using
   1850      * {@link android.graphics.BitmapFactory#decodeByteArray(byte[],int,int)}
   1851      */
   1852     public byte[] getThumbnail() {
   1853         if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) {
   1854             return getThumbnailBytes();
   1855         }
   1856         return null;
   1857     }
   1858 
   1859     /**
   1860      * Returns the thumbnail bytes inside the image file, regardless of the compression type of the
   1861      * thumbnail image.
   1862      */
   1863     public byte[] getThumbnailBytes() {
   1864         if (!mHasThumbnail) {
   1865             return null;
   1866         }
   1867         if (mThumbnailBytes != null) {
   1868             return mThumbnailBytes;
   1869         }
   1870 
   1871         // Read the thumbnail.
   1872         InputStream in = null;
   1873         try {
   1874             if (mAssetInputStream != null) {
   1875                 in = mAssetInputStream;
   1876                 if (in.markSupported()) {
   1877                     in.reset();
   1878                 } else {
   1879                     Log.d(TAG, "Cannot read thumbnail from inputstream without mark/reset support");
   1880                     return null;
   1881                 }
   1882             } else if (mFilename != null) {
   1883                 in = new FileInputStream(mFilename);
   1884             } else if (mSeekableFileDescriptor != null) {
   1885                 FileDescriptor fileDescriptor = Os.dup(mSeekableFileDescriptor);
   1886                 Os.lseek(fileDescriptor, 0, OsConstants.SEEK_SET);
   1887                 in = new FileInputStream(fileDescriptor);
   1888             }
   1889             if (in == null) {
   1890                 // Should not be reached this.
   1891                 throw new FileNotFoundException();
   1892             }
   1893             if (in.skip(mThumbnailOffset) != mThumbnailOffset) {
   1894                 throw new IOException("Corrupted image");
   1895             }
   1896             byte[] buffer = new byte[mThumbnailLength];
   1897             if (in.read(buffer) != mThumbnailLength) {
   1898                 throw new IOException("Corrupted image");
   1899             }
   1900             mThumbnailBytes = buffer;
   1901             return buffer;
   1902         } catch (IOException | ErrnoException e) {
   1903             // Couldn't get a thumbnail image.
   1904             Log.d(TAG, "Encountered exception while getting thumbnail", e);
   1905         } finally {
   1906             IoUtils.closeQuietly(in);
   1907         }
   1908         return null;
   1909     }
   1910 
   1911     /**
   1912      * Creates and returns a Bitmap object of the thumbnail image based on the byte array and the
   1913      * thumbnail compression value, or {@code null} if the compression type is unsupported.
   1914      */
   1915     public Bitmap getThumbnailBitmap() {
   1916         if (!mHasThumbnail) {
   1917             return null;
   1918         } else if (mThumbnailBytes == null) {
   1919             mThumbnailBytes = getThumbnailBytes();
   1920         }
   1921 
   1922         if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) {
   1923             return BitmapFactory.decodeByteArray(mThumbnailBytes, 0, mThumbnailLength);
   1924         } else if (mThumbnailCompression == DATA_UNCOMPRESSED) {
   1925             int[] rgbValues = new int[mThumbnailBytes.length / 3];
   1926             byte alpha = (byte) 0xff000000;
   1927             for (int i = 0; i < rgbValues.length; i++) {
   1928                 rgbValues[i] = alpha + (mThumbnailBytes[3 * i] << 16)
   1929                         + (mThumbnailBytes[3 * i + 1] << 8) + mThumbnailBytes[3 * i + 2];
   1930             }
   1931 
   1932             ExifAttribute imageLengthAttribute =
   1933                     (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_LENGTH);
   1934             ExifAttribute imageWidthAttribute =
   1935                     (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_WIDTH);
   1936             if (imageLengthAttribute != null && imageWidthAttribute != null) {
   1937                 int imageLength = imageLengthAttribute.getIntValue(mExifByteOrder);
   1938                 int imageWidth = imageWidthAttribute.getIntValue(mExifByteOrder);
   1939                 return Bitmap.createBitmap(
   1940                         rgbValues, imageWidth, imageLength, Bitmap.Config.ARGB_8888);
   1941             }
   1942         }
   1943         return null;
   1944     }
   1945 
   1946     /**
   1947      * Returns true if thumbnail image is JPEG Compressed, or false if either thumbnail image does
   1948      * not exist or thumbnail image is uncompressed.
   1949      */
   1950     public boolean isThumbnailCompressed() {
   1951         if (!mHasThumbnail) {
   1952             return false;
   1953         }
   1954         if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) {
   1955             return true;
   1956         }
   1957         return false;
   1958     }
   1959 
   1960     /**
   1961      * Returns the offset and length of thumbnail inside the image file, or
   1962      * {@code null} if there is no thumbnail.
   1963      *
   1964      * @return two-element array, the offset in the first value, and length in
   1965      *         the second, or {@code null} if no thumbnail was found.
   1966      */
   1967     public long[] getThumbnailRange() {
   1968         if (!mHasThumbnail) {
   1969             return null;
   1970         }
   1971 
   1972         long[] range = new long[2];
   1973         range[0] = mThumbnailOffset;
   1974         range[1] = mThumbnailLength;
   1975 
   1976         return range;
   1977     }
   1978 
   1979     /**
   1980      * Stores the latitude and longitude value in a float array. The first element is
   1981      * the latitude, and the second element is the longitude. Returns false if the
   1982      * Exif tags are not available.
   1983      */
   1984     public boolean getLatLong(float output[]) {
   1985         String latValue = getAttribute(TAG_GPS_LATITUDE);
   1986         String latRef = getAttribute(TAG_GPS_LATITUDE_REF);
   1987         String lngValue = getAttribute(TAG_GPS_LONGITUDE);
   1988         String lngRef = getAttribute(TAG_GPS_LONGITUDE_REF);
   1989 
   1990         if (latValue != null && latRef != null && lngValue != null && lngRef != null) {
   1991             try {
   1992                 output[0] = convertRationalLatLonToFloat(latValue, latRef);
   1993                 output[1] = convertRationalLatLonToFloat(lngValue, lngRef);
   1994                 return true;
   1995             } catch (IllegalArgumentException e) {
   1996                 // if values are not parseable
   1997             }
   1998         }
   1999 
   2000         return false;
   2001     }
   2002 
   2003     /**
   2004      * Return the altitude in meters. If the exif tag does not exist, return
   2005      * <var>defaultValue</var>.
   2006      *
   2007      * @param defaultValue the value to return if the tag is not available.
   2008      */
   2009     public double getAltitude(double defaultValue) {
   2010         double altitude = getAttributeDouble(TAG_GPS_ALTITUDE, -1);
   2011         int ref = getAttributeInt(TAG_GPS_ALTITUDE_REF, -1);
   2012 
   2013         if (altitude >= 0 && ref >= 0) {
   2014             return (altitude * ((ref == 1) ? -1 : 1));
   2015         } else {
   2016             return defaultValue;
   2017         }
   2018     }
   2019 
   2020     /**
   2021      * Returns number of milliseconds since Jan. 1, 1970, midnight local time.
   2022      * Returns -1 if the date time information if not available.
   2023      * @hide
   2024      */
   2025     public long getDateTime() {
   2026         String dateTimeString = getAttribute(TAG_DATETIME);
   2027         if (dateTimeString == null
   2028                 || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
   2029 
   2030         ParsePosition pos = new ParsePosition(0);
   2031         try {
   2032             // The exif field is in local time. Parsing it as if it is UTC will yield time
   2033             // since 1/1/1970 local time
   2034             Date datetime = sFormatter.parse(dateTimeString, pos);
   2035             if (datetime == null) return -1;
   2036             long msecs = datetime.getTime();
   2037 
   2038             String subSecs = getAttribute(TAG_SUBSEC_TIME);
   2039             if (subSecs != null) {
   2040                 try {
   2041                     long sub = Long.parseLong(subSecs);
   2042                     while (sub > 1000) {
   2043                         sub /= 10;
   2044                     }
   2045                     msecs += sub;
   2046                 } catch (NumberFormatException e) {
   2047                     // Ignored
   2048                 }
   2049             }
   2050             return msecs;
   2051         } catch (IllegalArgumentException e) {
   2052             return -1;
   2053         }
   2054     }
   2055 
   2056     /**
   2057      * Returns number of milliseconds since Jan. 1, 1970, midnight UTC.
   2058      * Returns -1 if the date time information if not available.
   2059      * @hide
   2060      */
   2061     public long getGpsDateTime() {
   2062         String date = getAttribute(TAG_GPS_DATESTAMP);
   2063         String time = getAttribute(TAG_GPS_TIMESTAMP);
   2064         if (date == null || time == null
   2065                 || (!sNonZeroTimePattern.matcher(date).matches()
   2066                 && !sNonZeroTimePattern.matcher(time).matches())) {
   2067             return -1;
   2068         }
   2069 
   2070         String dateTimeString = date + ' ' + time;
   2071 
   2072         ParsePosition pos = new ParsePosition(0);
   2073         try {
   2074             Date datetime = sFormatter.parse(dateTimeString, pos);
   2075             if (datetime == null) return -1;
   2076             return datetime.getTime();
   2077         } catch (IllegalArgumentException e) {
   2078             return -1;
   2079         }
   2080     }
   2081 
   2082     /** {@hide} */
   2083     public static float convertRationalLatLonToFloat(String rationalString, String ref) {
   2084         try {
   2085             String [] parts = rationalString.split(",");
   2086 
   2087             String [] pair;
   2088             pair = parts[0].split("/");
   2089             double degrees = Double.parseDouble(pair[0].trim())
   2090                     / Double.parseDouble(pair[1].trim());
   2091 
   2092             pair = parts[1].split("/");
   2093             double minutes = Double.parseDouble(pair[0].trim())
   2094                     / Double.parseDouble(pair[1].trim());
   2095 
   2096             pair = parts[2].split("/");
   2097             double seconds = Double.parseDouble(pair[0].trim())
   2098                     / Double.parseDouble(pair[1].trim());
   2099 
   2100             double result = degrees + (minutes / 60.0) + (seconds / 3600.0);
   2101             if ((ref.equals("S") || ref.equals("W"))) {
   2102                 return (float) -result;
   2103             }
   2104             return (float) result;
   2105         } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
   2106             // Not valid
   2107             throw new IllegalArgumentException();
   2108         }
   2109     }
   2110 
   2111     // Checks the type of image file
   2112     private int getMimeType(BufferedInputStream in) throws IOException {
   2113         in.mark(SIGNATURE_CHECK_SIZE);
   2114         byte[] signatureCheckBytes = new byte[SIGNATURE_CHECK_SIZE];
   2115         in.read(signatureCheckBytes);
   2116         in.reset();
   2117         if (isJpegFormat(signatureCheckBytes)) {
   2118             return IMAGE_TYPE_JPEG;
   2119         } else if (isRafFormat(signatureCheckBytes)) {
   2120             return IMAGE_TYPE_RAF;
   2121         } else if (isHeifFormat(signatureCheckBytes)) {
   2122             return IMAGE_TYPE_HEIF;
   2123         } else if (isOrfFormat(signatureCheckBytes)) {
   2124             return IMAGE_TYPE_ORF;
   2125         } else if (isRw2Format(signatureCheckBytes)) {
   2126             return IMAGE_TYPE_RW2;
   2127         }
   2128         // Certain file formats (PEF) are identified in readImageFileDirectory()
   2129         return IMAGE_TYPE_UNKNOWN;
   2130     }
   2131 
   2132     /**
   2133      * This method looks at the first 3 bytes to determine if this file is a JPEG file.
   2134      * See http://www.media.mit.edu/pia/Research/deepview/exif.html, "JPEG format and Marker"
   2135      */
   2136     private static boolean isJpegFormat(byte[] signatureCheckBytes) throws IOException {
   2137         for (int i = 0; i < JPEG_SIGNATURE.length; i++) {
   2138             if (signatureCheckBytes[i] != JPEG_SIGNATURE[i]) {
   2139                 return false;
   2140             }
   2141         }
   2142         return true;
   2143     }
   2144 
   2145     /**
   2146      * This method looks at the first 15 bytes to determine if this file is a RAF file.
   2147      * There is no official specification for RAF files from Fuji, but there is an online archive of
   2148      * image file specifications:
   2149      * http://fileformats.archiveteam.org/wiki/Fujifilm_RAF
   2150      */
   2151     private boolean isRafFormat(byte[] signatureCheckBytes) throws IOException {
   2152         byte[] rafSignatureBytes = RAF_SIGNATURE.getBytes();
   2153         for (int i = 0; i < rafSignatureBytes.length; i++) {
   2154             if (signatureCheckBytes[i] != rafSignatureBytes[i]) {
   2155                 return false;
   2156             }
   2157         }
   2158         return true;
   2159     }
   2160 
   2161     private boolean isHeifFormat(byte[] signatureCheckBytes) throws IOException {
   2162         ByteOrderedDataInputStream signatureInputStream = null;
   2163         try {
   2164             signatureInputStream = new ByteOrderedDataInputStream(signatureCheckBytes);
   2165             signatureInputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
   2166 
   2167             long chunkSize = signatureInputStream.readInt();
   2168             byte[] chunkType = new byte[4];
   2169             signatureInputStream.read(chunkType);
   2170 
   2171             if (!Arrays.equals(chunkType, HEIF_TYPE_FTYP)) {
   2172                 return false;
   2173             }
   2174 
   2175             long chunkDataOffset = 8;
   2176             if (chunkSize == 1) {
   2177                 // This indicates that the next 8 bytes represent the chunk size,
   2178                 // and chunk data comes after that.
   2179                 chunkSize = signatureInputStream.readLong();
   2180                 if (chunkSize < 16) {
   2181                     // The smallest valid chunk is 16 bytes long in this case.
   2182                     return false;
   2183                 }
   2184                 chunkDataOffset += 8;
   2185             }
   2186 
   2187             // only sniff up to signatureCheckBytes.length
   2188             if (chunkSize > signatureCheckBytes.length) {
   2189                 chunkSize = signatureCheckBytes.length;
   2190             }
   2191 
   2192             long chunkDataSize = chunkSize - chunkDataOffset;
   2193 
   2194             // It should at least have major brand (4-byte) and minor version (4-byte).
   2195             // The rest of the chunk (if any) is a list of (4-byte) compatible brands.
   2196             if (chunkDataSize < 8) {
   2197                 return false;
   2198             }
   2199 
   2200             byte[] brand = new byte[4];
   2201             boolean isMif1 = false;
   2202             boolean isHeic = false;
   2203             for (long i = 0; i < chunkDataSize / 4;  ++i) {
   2204                 if (signatureInputStream.read(brand) != brand.length) {
   2205                     return false;
   2206                 }
   2207                 if (i == 1) {
   2208                     // Skip this index, it refers to the minorVersion, not a brand.
   2209                     continue;
   2210                 }
   2211                 if (Arrays.equals(brand, HEIF_BRAND_MIF1)) {
   2212                     isMif1 = true;
   2213                 } else if (Arrays.equals(brand, HEIF_BRAND_HEIC)) {
   2214                     isHeic = true;
   2215                 }
   2216                 if (isMif1 && isHeic) {
   2217                     return true;
   2218                 }
   2219             }
   2220         } catch (Exception e) {
   2221             if (DEBUG) {
   2222                 Log.d(TAG, "Exception parsing HEIF file type box.", e);
   2223             }
   2224         } finally {
   2225             if (signatureInputStream != null) {
   2226                 signatureInputStream.close();
   2227                 signatureInputStream = null;
   2228             }
   2229         }
   2230         return false;
   2231     }
   2232 
   2233     /**
   2234      * ORF has a similar structure to TIFF but it contains a different signature at the TIFF Header.
   2235      * This method looks at the 2 bytes following the Byte Order bytes to determine if this file is
   2236      * an ORF file.
   2237      * There is no official specification for ORF files from Olympus, but there is an online archive
   2238      * of image file specifications:
   2239      * http://fileformats.archiveteam.org/wiki/Olympus_ORF
   2240      */
   2241     private boolean isOrfFormat(byte[] signatureCheckBytes) throws IOException {
   2242         ByteOrderedDataInputStream signatureInputStream =
   2243                 new ByteOrderedDataInputStream(signatureCheckBytes);
   2244         // Read byte order
   2245         mExifByteOrder = readByteOrder(signatureInputStream);
   2246         // Set byte order
   2247         signatureInputStream.setByteOrder(mExifByteOrder);
   2248 
   2249         short orfSignature = signatureInputStream.readShort();
   2250         if (orfSignature == ORF_SIGNATURE_1 || orfSignature == ORF_SIGNATURE_2) {
   2251             return true;
   2252         }
   2253         return false;
   2254     }
   2255 
   2256     /**
   2257      * RW2 is TIFF-based, but stores 0x55 signature byte instead of 0x42 at the header
   2258      * See http://lclevy.free.fr/raw/
   2259      */
   2260     private boolean isRw2Format(byte[] signatureCheckBytes) throws IOException {
   2261         ByteOrderedDataInputStream signatureInputStream =
   2262                 new ByteOrderedDataInputStream(signatureCheckBytes);
   2263         // Read byte order
   2264         mExifByteOrder = readByteOrder(signatureInputStream);
   2265         // Set byte order
   2266         signatureInputStream.setByteOrder(mExifByteOrder);
   2267 
   2268         short signatureByte = signatureInputStream.readShort();
   2269         if (signatureByte == RW2_SIGNATURE) {
   2270             return true;
   2271         }
   2272         return false;
   2273     }
   2274 
   2275     /**
   2276      * Loads EXIF attributes from a JPEG input stream.
   2277      *
   2278      * @param in The input stream that starts with the JPEG data.
   2279      * @param jpegOffset The offset value in input stream for JPEG data.
   2280      * @param imageType The image type from which to retrieve metadata. Use IFD_TYPE_PRIMARY for
   2281      *                   primary image, IFD_TYPE_PREVIEW for preview image, and
   2282      *                   IFD_TYPE_THUMBNAIL for thumbnail image.
   2283      * @throws IOException If the data contains invalid JPEG markers, offsets, or length values.
   2284      */
   2285     private void getJpegAttributes(ByteOrderedDataInputStream in, int jpegOffset, int imageType)
   2286             throws IOException {
   2287         // See JPEG File Interchange Format Specification, "JFIF Specification"
   2288         if (DEBUG) {
   2289             Log.d(TAG, "getJpegAttributes starting with: " + in);
   2290         }
   2291 
   2292         // JPEG uses Big Endian by default. See https://people.cs.umass.edu/~verts/cs32/endian.html
   2293         in.setByteOrder(ByteOrder.BIG_ENDIAN);
   2294 
   2295         // Skip to JPEG data
   2296         in.seek(jpegOffset);
   2297         int bytesRead = jpegOffset;
   2298 
   2299         byte marker;
   2300         if ((marker = in.readByte()) != MARKER) {
   2301             throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
   2302         }
   2303         ++bytesRead;
   2304         if (in.readByte() != MARKER_SOI) {
   2305             throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
   2306         }
   2307         ++bytesRead;
   2308         while (true) {
   2309             marker = in.readByte();
   2310             if (marker != MARKER) {
   2311                 throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff));
   2312             }
   2313             ++bytesRead;
   2314             marker = in.readByte();
   2315             if (DEBUG) {
   2316                 Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff));
   2317             }
   2318             ++bytesRead;
   2319 
   2320             // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and
   2321             // the image data will terminate right after.
   2322             if (marker == MARKER_EOI || marker == MARKER_SOS) {
   2323                 break;
   2324             }
   2325             int length = in.readUnsignedShort() - 2;
   2326             bytesRead += 2;
   2327             if (DEBUG) {
   2328                 Log.d(TAG, "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: "
   2329                         + (length + 2) + ")");
   2330             }
   2331             if (length < 0) {
   2332                 throw new IOException("Invalid length");
   2333             }
   2334             switch (marker) {
   2335                 case MARKER_APP1: {
   2336                     if (DEBUG) {
   2337                         Log.d(TAG, "MARKER_APP1");
   2338                     }
   2339                     if (length < 6) {
   2340                         // Skip if it's not an EXIF APP1 segment.
   2341                         break;
   2342                     }
   2343                     byte[] identifier = new byte[6];
   2344                     if (in.read(identifier) != 6) {
   2345                         throw new IOException("Invalid exif");
   2346                     }
   2347                     bytesRead += 6;
   2348                     length -= 6;
   2349                     if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
   2350                         // Skip if it's not an EXIF APP1 segment.
   2351                         break;
   2352                     }
   2353                     if (length <= 0) {
   2354                         throw new IOException("Invalid exif");
   2355                     }
   2356                     if (DEBUG) {
   2357                         Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")");
   2358                     }
   2359                     // Save offset values for createJpegThumbnailBitmap() function
   2360                     mExifOffset = bytesRead;
   2361 
   2362                     byte[] bytes = new byte[length];
   2363                     if (in.read(bytes) != length) {
   2364                         throw new IOException("Invalid exif");
   2365                     }
   2366                     bytesRead += length;
   2367                     length = 0;
   2368 
   2369                     readExifSegment(bytes, imageType);
   2370                     break;
   2371                 }
   2372 
   2373                 case MARKER_COM: {
   2374                     byte[] bytes = new byte[length];
   2375                     if (in.read(bytes) != length) {
   2376                         throw new IOException("Invalid exif");
   2377                     }
   2378                     length = 0;
   2379                     if (getAttribute(TAG_USER_COMMENT) == null) {
   2380                         mAttributes[IFD_TYPE_EXIF].put(TAG_USER_COMMENT, ExifAttribute.createString(
   2381                                 new String(bytes, ASCII)));
   2382                     }
   2383                     break;
   2384                 }
   2385 
   2386                 case MARKER_SOF0:
   2387                 case MARKER_SOF1:
   2388                 case MARKER_SOF2:
   2389                 case MARKER_SOF3:
   2390                 case MARKER_SOF5:
   2391                 case MARKER_SOF6:
   2392                 case MARKER_SOF7:
   2393                 case MARKER_SOF9:
   2394                 case MARKER_SOF10:
   2395                 case MARKER_SOF11:
   2396                 case MARKER_SOF13:
   2397                 case MARKER_SOF14:
   2398                 case MARKER_SOF15: {
   2399                     if (in.skipBytes(1) != 1) {
   2400                         throw new IOException("Invalid SOFx");
   2401                     }
   2402                     mAttributes[imageType].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong(
   2403                             in.readUnsignedShort(), mExifByteOrder));
   2404                     mAttributes[imageType].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong(
   2405                             in.readUnsignedShort(), mExifByteOrder));
   2406                     length -= 5;
   2407                     break;
   2408                 }
   2409 
   2410                 default: {
   2411                     break;
   2412                 }
   2413             }
   2414             if (length < 0) {
   2415                 throw new IOException("Invalid length");
   2416             }
   2417             if (in.skipBytes(length) != length) {
   2418                 throw new IOException("Invalid JPEG segment");
   2419             }
   2420             bytesRead += length;
   2421         }
   2422         // Restore original byte order
   2423         in.setByteOrder(mExifByteOrder);
   2424     }
   2425 
   2426     private void getRawAttributes(ByteOrderedDataInputStream in) throws IOException {
   2427         // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
   2428         parseTiffHeaders(in, in.available());
   2429 
   2430         // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6.
   2431         readImageFileDirectory(in, IFD_TYPE_PRIMARY);
   2432 
   2433         // Update ImageLength/Width tags for all image data.
   2434         updateImageSizeValues(in, IFD_TYPE_PRIMARY);
   2435         updateImageSizeValues(in, IFD_TYPE_PREVIEW);
   2436         updateImageSizeValues(in, IFD_TYPE_THUMBNAIL);
   2437 
   2438         // Check if each image data is in valid position.
   2439         validateImages(in);
   2440 
   2441         if (mMimeType == IMAGE_TYPE_PEF) {
   2442             // PEF files contain a MakerNote data, which contains the data for ColorSpace tag.
   2443             // See http://lclevy.free.fr/raw/ and piex.cc PefGetPreviewData()
   2444             ExifAttribute makerNoteAttribute =
   2445                     (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE);
   2446             if (makerNoteAttribute != null) {
   2447                 // Create an ordered DataInputStream for MakerNote
   2448                 ByteOrderedDataInputStream makerNoteDataInputStream =
   2449                         new ByteOrderedDataInputStream(makerNoteAttribute.bytes);
   2450                 makerNoteDataInputStream.setByteOrder(mExifByteOrder);
   2451 
   2452                 // Seek to MakerNote data
   2453                 makerNoteDataInputStream.seek(PEF_MAKER_NOTE_SKIP_SIZE);
   2454 
   2455                 // Read IFD data from MakerNote
   2456                 readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_PEF);
   2457 
   2458                 // Update ColorSpace tag
   2459                 ExifAttribute colorSpaceAttribute =
   2460                         (ExifAttribute) mAttributes[IFD_TYPE_PEF].get(TAG_COLOR_SPACE);
   2461                 if (colorSpaceAttribute != null) {
   2462                     mAttributes[IFD_TYPE_EXIF].put(TAG_COLOR_SPACE, colorSpaceAttribute);
   2463                 }
   2464             }
   2465         }
   2466     }
   2467 
   2468     /**
   2469      * RAF files contains a JPEG and a CFA data.
   2470      * The JPEG contains two images, a preview and a thumbnail, while the CFA contains a RAW image.
   2471      * This method looks at the first 160 bytes of a RAF file to retrieve the offset and length
   2472      * values for the JPEG and CFA data.
   2473      * Using that data, it parses the JPEG data to retrieve the preview and thumbnail image data,
   2474      * then parses the CFA metadata to retrieve the primary image length/width values.
   2475      * For data format details, see http://fileformats.archiveteam.org/wiki/Fujifilm_RAF
   2476      */
   2477     private void getRafAttributes(ByteOrderedDataInputStream in) throws IOException {
   2478         // Retrieve offset & length values
   2479         in.skipBytes(RAF_OFFSET_TO_JPEG_IMAGE_OFFSET);
   2480         byte[] jpegOffsetBytes = new byte[4];
   2481         byte[] cfaHeaderOffsetBytes = new byte[4];
   2482         in.read(jpegOffsetBytes);
   2483         // Skip JPEG length value since it is not needed
   2484         in.skipBytes(RAF_JPEG_LENGTH_VALUE_SIZE);
   2485         in.read(cfaHeaderOffsetBytes);
   2486         int rafJpegOffset = ByteBuffer.wrap(jpegOffsetBytes).getInt();
   2487         int rafCfaHeaderOffset = ByteBuffer.wrap(cfaHeaderOffsetBytes).getInt();
   2488 
   2489         // Retrieve JPEG image metadata
   2490         getJpegAttributes(in, rafJpegOffset, IFD_TYPE_PREVIEW);
   2491 
   2492         // Skip to CFA header offset.
   2493         in.seek(rafCfaHeaderOffset);
   2494 
   2495         // Retrieve primary image length/width values, if TAG_RAF_IMAGE_SIZE exists
   2496         in.setByteOrder(ByteOrder.BIG_ENDIAN);
   2497         int numberOfDirectoryEntry = in.readInt();
   2498         if (DEBUG) {
   2499             Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
   2500         }
   2501         // CFA stores some metadata about the RAW image. Since CFA uses proprietary tags, can only
   2502         // find and retrieve image size information tags, while skipping others.
   2503         // See piex.cc RafGetDimension()
   2504         for (int i = 0; i < numberOfDirectoryEntry; ++i) {
   2505             int tagNumber = in.readUnsignedShort();
   2506             int numberOfBytes = in.readUnsignedShort();
   2507             if (tagNumber == TAG_RAF_IMAGE_SIZE.number) {
   2508                 int imageLength = in.readShort();
   2509                 int imageWidth = in.readShort();
   2510                 ExifAttribute imageLengthAttribute =
   2511                         ExifAttribute.createUShort(imageLength, mExifByteOrder);
   2512                 ExifAttribute imageWidthAttribute =
   2513                         ExifAttribute.createUShort(imageWidth, mExifByteOrder);
   2514                 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, imageLengthAttribute);
   2515                 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, imageWidthAttribute);
   2516                 if (DEBUG) {
   2517                     Log.d(TAG, "Updated to length: " + imageLength + ", width: " + imageWidth);
   2518                 }
   2519                 return;
   2520             }
   2521             in.skipBytes(numberOfBytes);
   2522         }
   2523     }
   2524 
   2525     private void getHeifAttributes(ByteOrderedDataInputStream in) throws IOException {
   2526         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
   2527         try {
   2528             retriever.setDataSource(new MediaDataSource() {
   2529                 long mPosition;
   2530 
   2531                 @Override
   2532                 public void close() throws IOException {}
   2533 
   2534                 @Override
   2535                 public int readAt(long position, byte[] buffer, int offset, int size)
   2536                         throws IOException {
   2537                     if (size == 0) {
   2538                         return 0;
   2539                     }
   2540                     if (position < 0) {
   2541                         return -1;
   2542                     }
   2543                     try {
   2544                         if (mPosition != position) {
   2545                             in.seek(position);
   2546                             mPosition = position;
   2547                         }
   2548 
   2549                         int bytesRead = in.read(buffer, offset, size);
   2550                         if (bytesRead >= 0) {
   2551                             mPosition += bytesRead;
   2552                             return bytesRead;
   2553                         }
   2554                     } catch (IOException e) {}
   2555                     mPosition = -1; // need to seek on next read
   2556                     return -1;
   2557                 }
   2558 
   2559                 @Override
   2560                 public long getSize() throws IOException {
   2561                     return -1;
   2562                 }
   2563             });
   2564 
   2565             String exifOffsetStr = retriever.extractMetadata(
   2566                     MediaMetadataRetriever.METADATA_KEY_EXIF_OFFSET);
   2567             String exifLengthStr = retriever.extractMetadata(
   2568                     MediaMetadataRetriever.METADATA_KEY_EXIF_LENGTH);
   2569             String hasImage = retriever.extractMetadata(
   2570                     MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
   2571             String hasVideo = retriever.extractMetadata(
   2572                     MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
   2573 
   2574             String width = null;
   2575             String height = null;
   2576             String rotation = null;
   2577             final String METADATA_VALUE_YES = "yes";
   2578             // If the file has both image and video, prefer image info over video info.
   2579             // App querying ExifInterface is most likely using the bitmap path which
   2580             // picks the image first.
   2581             if (METADATA_VALUE_YES.equals(hasImage)) {
   2582                 width = retriever.extractMetadata(
   2583                         MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH);
   2584                 height = retriever.extractMetadata(
   2585                         MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT);
   2586                 rotation = retriever.extractMetadata(
   2587                         MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION);
   2588             } else if (METADATA_VALUE_YES.equals(hasVideo)) {
   2589                 width = retriever.extractMetadata(
   2590                         MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
   2591                 height = retriever.extractMetadata(
   2592                         MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
   2593                 rotation = retriever.extractMetadata(
   2594                         MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
   2595             }
   2596 
   2597             if (width != null) {
   2598                 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH,
   2599                         ExifAttribute.createUShort(Integer.parseInt(width), mExifByteOrder));
   2600             }
   2601 
   2602             if (height != null) {
   2603                 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH,
   2604                         ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder));
   2605             }
   2606 
   2607             if (rotation != null) {
   2608                 int orientation = ExifInterface.ORIENTATION_NORMAL;
   2609 
   2610                 // all rotation angles in CW
   2611                 switch (Integer.parseInt(rotation)) {
   2612                     case 90:
   2613                         orientation = ExifInterface.ORIENTATION_ROTATE_90;
   2614                         break;
   2615                     case 180:
   2616                         orientation = ExifInterface.ORIENTATION_ROTATE_180;
   2617                         break;
   2618                     case 270:
   2619                         orientation = ExifInterface.ORIENTATION_ROTATE_270;
   2620                         break;
   2621                 }
   2622 
   2623                 mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
   2624                         ExifAttribute.createUShort(orientation, mExifByteOrder));
   2625             }
   2626 
   2627             if (exifOffsetStr != null && exifLengthStr != null) {
   2628                 int offset = Integer.parseInt(exifOffsetStr);
   2629                 int length = Integer.parseInt(exifLengthStr);
   2630                 if (length <= 6) {
   2631                     throw new IOException("Invalid exif length");
   2632                 }
   2633                 in.seek(offset);
   2634                 byte[] identifier = new byte[6];
   2635                 if (in.read(identifier) != 6) {
   2636                     throw new IOException("Can't read identifier");
   2637                 }
   2638                 offset += 6;
   2639                 length -= 6;
   2640                 if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
   2641                     throw new IOException("Invalid identifier");
   2642                 }
   2643 
   2644                 byte[] bytes = new byte[length];
   2645                 if (in.read(bytes) != length) {
   2646                     throw new IOException("Can't read exif");
   2647                 }
   2648                 readExifSegment(bytes, IFD_TYPE_PRIMARY);
   2649             }
   2650 
   2651             if (DEBUG) {
   2652                 Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation);
   2653             }
   2654         } finally {
   2655             retriever.release();
   2656         }
   2657     }
   2658 
   2659     /**
   2660      * ORF files contains a primary image data and a MakerNote data that contains preview/thumbnail
   2661      * images. Both data takes the form of IFDs and can therefore be read with the
   2662      * readImageFileDirectory() method.
   2663      * This method reads all the necessary data and updates the primary/preview/thumbnail image
   2664      * information according to the GetOlympusPreviewImage() method in piex.cc.
   2665      * For data format details, see the following:
   2666      * http://fileformats.archiveteam.org/wiki/Olympus_ORF
   2667      * https://libopenraw.freedesktop.org/wiki/Olympus_ORF
   2668      */
   2669     private void getOrfAttributes(ByteOrderedDataInputStream in) throws IOException {
   2670         // Retrieve primary image data
   2671         // Other Exif data will be located in the Makernote.
   2672         getRawAttributes(in);
   2673 
   2674         // Additionally retrieve preview/thumbnail information from MakerNote tag, which contains
   2675         // proprietary tags and therefore does not have offical documentation
   2676         // See GetOlympusPreviewImage() in piex.cc & http://www.exiv2.org/tags-olympus.html
   2677         ExifAttribute makerNoteAttribute =
   2678                 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE);
   2679         if (makerNoteAttribute != null) {
   2680             // Create an ordered DataInputStream for MakerNote
   2681             ByteOrderedDataInputStream makerNoteDataInputStream =
   2682                     new ByteOrderedDataInputStream(makerNoteAttribute.bytes);
   2683             makerNoteDataInputStream.setByteOrder(mExifByteOrder);
   2684 
   2685             // There are two types of headers for Olympus MakerNotes
   2686             // See http://www.exiv2.org/makernote.html#R1
   2687             byte[] makerNoteHeader1Bytes = new byte[ORF_MAKER_NOTE_HEADER_1.length];
   2688             makerNoteDataInputStream.readFully(makerNoteHeader1Bytes);
   2689             makerNoteDataInputStream.seek(0);
   2690             byte[] makerNoteHeader2Bytes = new byte[ORF_MAKER_NOTE_HEADER_2.length];
   2691             makerNoteDataInputStream.readFully(makerNoteHeader2Bytes);
   2692             // Skip the corresponding amount of bytes for each header type
   2693             if (Arrays.equals(makerNoteHeader1Bytes, ORF_MAKER_NOTE_HEADER_1)) {
   2694                 makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_1_SIZE);
   2695             } else if (Arrays.equals(makerNoteHeader2Bytes, ORF_MAKER_NOTE_HEADER_2)) {
   2696                 makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_2_SIZE);
   2697             }
   2698 
   2699             // Read IFD data from MakerNote
   2700             readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_ORF_MAKER_NOTE);
   2701 
   2702             // Retrieve & update preview image offset & length values
   2703             ExifAttribute imageLengthAttribute = (ExifAttribute)
   2704                     mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_START);
   2705             ExifAttribute bitsPerSampleAttribute = (ExifAttribute)
   2706                     mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_LENGTH);
   2707 
   2708             if (imageLengthAttribute != null && bitsPerSampleAttribute != null) {
   2709                 mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT,
   2710                         imageLengthAttribute);
   2711                 mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
   2712                         bitsPerSampleAttribute);
   2713             }
   2714 
   2715             // TODO: Check this behavior in other ORF files
   2716             // Retrieve primary image length & width values
   2717             // See piex.cc GetOlympusPreviewImage()
   2718             ExifAttribute aspectFrameAttribute = (ExifAttribute)
   2719                     mAttributes[IFD_TYPE_ORF_IMAGE_PROCESSING].get(TAG_ORF_ASPECT_FRAME);
   2720             if (aspectFrameAttribute != null) {
   2721                 int[] aspectFrameValues = new int[4];
   2722                 aspectFrameValues = (int[]) aspectFrameAttribute.getValue(mExifByteOrder);
   2723                 if (aspectFrameValues[2] > aspectFrameValues[0] &&
   2724                         aspectFrameValues[3] > aspectFrameValues[1]) {
   2725                     int primaryImageWidth = aspectFrameValues[2] - aspectFrameValues[0] + 1;
   2726                     int primaryImageLength = aspectFrameValues[3] - aspectFrameValues[1] + 1;
   2727                     // Swap width & length values
   2728                     if (primaryImageWidth < primaryImageLength) {
   2729                         primaryImageWidth += primaryImageLength;
   2730                         primaryImageLength = primaryImageWidth - primaryImageLength;
   2731                         primaryImageWidth -= primaryImageLength;
   2732                     }
   2733                     ExifAttribute primaryImageWidthAttribute =
   2734                             ExifAttribute.createUShort(primaryImageWidth, mExifByteOrder);
   2735                     ExifAttribute primaryImageLengthAttribute =
   2736                             ExifAttribute.createUShort(primaryImageLength, mExifByteOrder);
   2737 
   2738                     mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, primaryImageWidthAttribute);
   2739                     mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, primaryImageLengthAttribute);
   2740                 }
   2741             }
   2742         }
   2743     }
   2744 
   2745     // RW2 contains the primary image data in IFD0 and the preview and/or thumbnail image data in
   2746     // the JpgFromRaw tag
   2747     // See https://libopenraw.freedesktop.org/wiki/Panasonic_RAW/ and piex.cc Rw2GetPreviewData()
   2748     private void getRw2Attributes(ByteOrderedDataInputStream in) throws IOException {
   2749         // Retrieve primary image data
   2750         getRawAttributes(in);
   2751 
   2752         // Retrieve preview and/or thumbnail image data
   2753         ExifAttribute jpgFromRawAttribute =
   2754                 (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_JPG_FROM_RAW);
   2755         if (jpgFromRawAttribute != null) {
   2756             getJpegAttributes(in, mRw2JpgFromRawOffset, IFD_TYPE_PREVIEW);
   2757         }
   2758 
   2759         // Set ISO tag value if necessary
   2760         ExifAttribute rw2IsoAttribute =
   2761                 (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_ISO);
   2762         ExifAttribute exifIsoAttribute =
   2763                 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_ISO_SPEED_RATINGS);
   2764         if (rw2IsoAttribute != null && exifIsoAttribute == null) {
   2765             // Place this attribute only if it doesn't exist
   2766             mAttributes[IFD_TYPE_EXIF].put(TAG_ISO_SPEED_RATINGS, rw2IsoAttribute);
   2767         }
   2768     }
   2769 
   2770     // Stores a new JPEG image with EXIF attributes into a given output stream.
   2771     private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream)
   2772             throws IOException {
   2773         // See JPEG File Interchange Format Specification, "JFIF Specification"
   2774         if (DEBUG) {
   2775             Log.d(TAG, "saveJpegAttributes starting with (inputStream: " + inputStream
   2776                     + ", outputStream: " + outputStream + ")");
   2777         }
   2778         DataInputStream dataInputStream = new DataInputStream(inputStream);
   2779         ByteOrderedDataOutputStream dataOutputStream =
   2780                 new ByteOrderedDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN);
   2781         if (dataInputStream.readByte() != MARKER) {
   2782             throw new IOException("Invalid marker");
   2783         }
   2784         dataOutputStream.writeByte(MARKER);
   2785         if (dataInputStream.readByte() != MARKER_SOI) {
   2786             throw new IOException("Invalid marker");
   2787         }
   2788         dataOutputStream.writeByte(MARKER_SOI);
   2789 
   2790         // Write EXIF APP1 segment
   2791         dataOutputStream.writeByte(MARKER);
   2792         dataOutputStream.writeByte(MARKER_APP1);
   2793         writeExifSegment(dataOutputStream, 6);
   2794 
   2795         byte[] bytes = new byte[4096];
   2796 
   2797         while (true) {
   2798             byte marker = dataInputStream.readByte();
   2799             if (marker != MARKER) {
   2800                 throw new IOException("Invalid marker");
   2801             }
   2802             marker = dataInputStream.readByte();
   2803             switch (marker) {
   2804                 case MARKER_APP1: {
   2805                     int length = dataInputStream.readUnsignedShort() - 2;
   2806                     if (length < 0) {
   2807                         throw new IOException("Invalid length");
   2808                     }
   2809                     byte[] identifier = new byte[6];
   2810                     if (length >= 6) {
   2811                         if (dataInputStream.read(identifier) != 6) {
   2812                             throw new IOException("Invalid exif");
   2813                         }
   2814                         if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
   2815                             // Skip the original EXIF APP1 segment.
   2816                             if (dataInputStream.skipBytes(length - 6) != length - 6) {
   2817                                 throw new IOException("Invalid length");
   2818                             }
   2819                             break;
   2820                         }
   2821                     }
   2822                     // Copy non-EXIF APP1 segment.
   2823                     dataOutputStream.writeByte(MARKER);
   2824                     dataOutputStream.writeByte(marker);
   2825                     dataOutputStream.writeUnsignedShort(length + 2);
   2826                     if (length >= 6) {
   2827                         length -= 6;
   2828                         dataOutputStream.write(identifier);
   2829                     }
   2830                     int read;
   2831                     while (length > 0 && (read = dataInputStream.read(
   2832                             bytes, 0, Math.min(length, bytes.length))) >= 0) {
   2833                         dataOutputStream.write(bytes, 0, read);
   2834                         length -= read;
   2835                     }
   2836                     break;
   2837                 }
   2838                 case MARKER_EOI:
   2839                 case MARKER_SOS: {
   2840                     dataOutputStream.writeByte(MARKER);
   2841                     dataOutputStream.writeByte(marker);
   2842                     // Copy all the remaining data
   2843                     Streams.copy(dataInputStream, dataOutputStream);
   2844                     return;
   2845                 }
   2846                 default: {
   2847                     // Copy JPEG segment
   2848                     dataOutputStream.writeByte(MARKER);
   2849                     dataOutputStream.writeByte(marker);
   2850                     int length = dataInputStream.readUnsignedShort();
   2851                     dataOutputStream.writeUnsignedShort(length);
   2852                     length -= 2;
   2853                     if (length < 0) {
   2854                         throw new IOException("Invalid length");
   2855                     }
   2856                     int read;
   2857                     while (length > 0 && (read = dataInputStream.read(
   2858                             bytes, 0, Math.min(length, bytes.length))) >= 0) {
   2859                         dataOutputStream.write(bytes, 0, read);
   2860                         length -= read;
   2861                     }
   2862                     break;
   2863                 }
   2864             }
   2865         }
   2866     }
   2867 
   2868     // Reads the given EXIF byte area and save its tag data into attributes.
   2869     private void readExifSegment(byte[] exifBytes, int imageType) throws IOException {
   2870         ByteOrderedDataInputStream dataInputStream =
   2871                 new ByteOrderedDataInputStream(exifBytes);
   2872 
   2873         // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
   2874         parseTiffHeaders(dataInputStream, exifBytes.length);
   2875 
   2876         // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6.
   2877         readImageFileDirectory(dataInputStream, imageType);
   2878     }
   2879 
   2880     private void addDefaultValuesForCompatibility() {
   2881         // If DATETIME tag has no value, then set the value to DATETIME_ORIGINAL tag's.
   2882         String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL);
   2883         if (valueOfDateTimeOriginal != null && getAttribute(TAG_DATETIME) == null) {
   2884             mAttributes[IFD_TYPE_PRIMARY].put(TAG_DATETIME,
   2885                     ExifAttribute.createString(valueOfDateTimeOriginal));
   2886         }
   2887 
   2888         // Add the default value.
   2889         if (getAttribute(TAG_IMAGE_WIDTH) == null) {
   2890             mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH,
   2891                     ExifAttribute.createULong(0, mExifByteOrder));
   2892         }
   2893         if (getAttribute(TAG_IMAGE_LENGTH) == null) {
   2894             mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH,
   2895                     ExifAttribute.createULong(0, mExifByteOrder));
   2896         }
   2897         if (getAttribute(TAG_ORIENTATION) == null) {
   2898             mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
   2899                     ExifAttribute.createUShort(0, mExifByteOrder));
   2900         }
   2901         if (getAttribute(TAG_LIGHT_SOURCE) == null) {
   2902             mAttributes[IFD_TYPE_EXIF].put(TAG_LIGHT_SOURCE,
   2903                     ExifAttribute.createULong(0, mExifByteOrder));
   2904         }
   2905     }
   2906 
   2907     private ByteOrder readByteOrder(ByteOrderedDataInputStream dataInputStream)
   2908             throws IOException {
   2909         // Read byte order.
   2910         short byteOrder = dataInputStream.readShort();
   2911         switch (byteOrder) {
   2912             case BYTE_ALIGN_II:
   2913                 if (DEBUG) {
   2914                     Log.d(TAG, "readExifSegment: Byte Align II");
   2915                 }
   2916                 return ByteOrder.LITTLE_ENDIAN;
   2917             case BYTE_ALIGN_MM:
   2918                 if (DEBUG) {
   2919                     Log.d(TAG, "readExifSegment: Byte Align MM");
   2920                 }
   2921                 return ByteOrder.BIG_ENDIAN;
   2922             default:
   2923                 throw new IOException("Invalid byte order: " + Integer.toHexString(byteOrder));
   2924         }
   2925     }
   2926 
   2927     private void parseTiffHeaders(ByteOrderedDataInputStream dataInputStream,
   2928             int exifBytesLength) throws IOException {
   2929         // Read byte order
   2930         mExifByteOrder = readByteOrder(dataInputStream);
   2931         // Set byte order
   2932         dataInputStream.setByteOrder(mExifByteOrder);
   2933 
   2934         // Check start code
   2935         int startCode = dataInputStream.readUnsignedShort();
   2936         if (mMimeType != IMAGE_TYPE_ORF && mMimeType != IMAGE_TYPE_RW2 && startCode != START_CODE) {
   2937             throw new IOException("Invalid start code: " + Integer.toHexString(startCode));
   2938         }
   2939 
   2940         // Read and skip to first ifd offset
   2941         int firstIfdOffset = dataInputStream.readInt();
   2942         if (firstIfdOffset < 8 || firstIfdOffset >= exifBytesLength) {
   2943             throw new IOException("Invalid first Ifd offset: " + firstIfdOffset);
   2944         }
   2945         firstIfdOffset -= 8;
   2946         if (firstIfdOffset > 0) {
   2947             if (dataInputStream.skipBytes(firstIfdOffset) != firstIfdOffset) {
   2948                 throw new IOException("Couldn't jump to first Ifd: " + firstIfdOffset);
   2949             }
   2950         }
   2951     }
   2952 
   2953     // Reads image file directory, which is a tag group in EXIF.
   2954     private void readImageFileDirectory(ByteOrderedDataInputStream dataInputStream,
   2955             @IfdType int ifdType) throws IOException {
   2956         if (dataInputStream.mPosition + 2 > dataInputStream.mLength) {
   2957             // Return if there is no data from the offset.
   2958             return;
   2959         }
   2960         // See TIFF 6.0 Section 2: TIFF Structure, Figure 1.
   2961         short numberOfDirectoryEntry = dataInputStream.readShort();
   2962         if (dataInputStream.mPosition + 12 * numberOfDirectoryEntry > dataInputStream.mLength
   2963                 || numberOfDirectoryEntry <= 0) {
   2964             // Return if the size of entries is either too big or negative.
   2965             return;
   2966         }
   2967 
   2968         if (DEBUG) {
   2969             Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
   2970         }
   2971 
   2972         // See TIFF 6.0 Section 2: TIFF Structure, "Image File Directory".
   2973         for (short i = 0; i < numberOfDirectoryEntry; ++i) {
   2974             int tagNumber = dataInputStream.readUnsignedShort();
   2975             int dataFormat = dataInputStream.readUnsignedShort();
   2976             int numberOfComponents = dataInputStream.readInt();
   2977             // Next four bytes is for data offset or value.
   2978             long nextEntryOffset = dataInputStream.peek() + 4;
   2979 
   2980             // Look up a corresponding tag from tag number
   2981             ExifTag tag = (ExifTag) sExifTagMapsForReading[ifdType].get(tagNumber);
   2982 
   2983             if (DEBUG) {
   2984                 Log.d(TAG, String.format("ifdType: %d, tagNumber: %d, tagName: %s, dataFormat: %d, "
   2985                         + "numberOfComponents: %d", ifdType, tagNumber,
   2986                         tag != null ? tag.name : null, dataFormat, numberOfComponents));
   2987             }
   2988 
   2989             long byteCount = 0;
   2990             boolean valid = false;
   2991             if (tag == null) {
   2992                 Log.w(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber);
   2993             } else if (dataFormat <= 0 || dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) {
   2994                 Log.w(TAG, "Skip the tag entry since data format is invalid: " + dataFormat);
   2995             } else {
   2996                 byteCount = (long) numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat];
   2997                 if (byteCount < 0 || byteCount > Integer.MAX_VALUE) {
   2998                     Log.w(TAG, "Skip the tag entry since the number of components is invalid: "
   2999                             + numberOfComponents);
   3000                 } else {
   3001                     valid = true;
   3002                 }
   3003             }
   3004             if (!valid) {
   3005                 dataInputStream.seek(nextEntryOffset);
   3006                 continue;
   3007             }
   3008 
   3009             // Read a value from data field or seek to the value offset which is stored in data
   3010             // field if the size of the entry value is bigger than 4.
   3011             if (byteCount > 4) {
   3012                 int offset = dataInputStream.readInt();
   3013                 if (DEBUG) {
   3014                     Log.d(TAG, "seek to data offset: " + offset);
   3015                 }
   3016                 if (mMimeType == IMAGE_TYPE_ORF) {
   3017                     if (tag.name == TAG_MAKER_NOTE) {
   3018                         // Save offset value for reading thumbnail
   3019                         mOrfMakerNoteOffset = offset;
   3020                     } else if (ifdType == IFD_TYPE_ORF_MAKER_NOTE
   3021                             && tag.name == TAG_ORF_THUMBNAIL_IMAGE) {
   3022                         // Retrieve & update values for thumbnail offset and length values for ORF
   3023                         mOrfThumbnailOffset = offset;
   3024                         mOrfThumbnailLength = numberOfComponents;
   3025 
   3026                         ExifAttribute compressionAttribute =
   3027                                 ExifAttribute.createUShort(DATA_JPEG, mExifByteOrder);
   3028                         ExifAttribute jpegInterchangeFormatAttribute =
   3029                                 ExifAttribute.createULong(mOrfThumbnailOffset, mExifByteOrder);
   3030                         ExifAttribute jpegInterchangeFormatLengthAttribute =
   3031                                 ExifAttribute.createULong(mOrfThumbnailLength, mExifByteOrder);
   3032 
   3033                         mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_COMPRESSION, compressionAttribute);
   3034                         mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT,
   3035                                 jpegInterchangeFormatAttribute);
   3036                         mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
   3037                                 jpegInterchangeFormatLengthAttribute);
   3038                     }
   3039                 } else if (mMimeType == IMAGE_TYPE_RW2) {
   3040                     if (tag.name == TAG_RW2_JPG_FROM_RAW) {
   3041                         mRw2JpgFromRawOffset = offset;
   3042                     }
   3043                 }
   3044                 if (offset + byteCount <= dataInputStream.mLength) {
   3045                     dataInputStream.seek(offset);
   3046                 } else {
   3047                     // Skip if invalid data offset.
   3048                     Log.w(TAG, "Skip the tag entry since data offset is invalid: " + offset);
   3049                     dataInputStream.seek(nextEntryOffset);
   3050                     continue;
   3051                 }
   3052             }
   3053 
   3054             // Recursively parse IFD when a IFD pointer tag appears.
   3055             Integer nextIfdType = sExifPointerTagMap.get(tagNumber);
   3056             if (DEBUG) {
   3057                 Log.d(TAG, "nextIfdType: " + nextIfdType + " byteCount: " + byteCount);
   3058             }
   3059 
   3060             if (nextIfdType != null) {
   3061                 long offset = -1L;
   3062                 // Get offset from data field
   3063                 switch (dataFormat) {
   3064                     case IFD_FORMAT_USHORT: {
   3065                         offset = dataInputStream.readUnsignedShort();
   3066                         break;
   3067                     }
   3068                     case IFD_FORMAT_SSHORT: {
   3069                         offset = dataInputStream.readShort();
   3070                         break;
   3071                     }
   3072                     case IFD_FORMAT_ULONG: {
   3073                         offset = dataInputStream.readUnsignedInt();
   3074                         break;
   3075                     }
   3076                     case IFD_FORMAT_SLONG:
   3077                     case IFD_FORMAT_IFD: {
   3078                         offset = dataInputStream.readInt();
   3079                         break;
   3080                     }
   3081                     default: {
   3082                         // Nothing to do
   3083                         break;
   3084                     }
   3085                 }
   3086                 if (DEBUG) {
   3087                     Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tag.name));
   3088                 }
   3089 
   3090                 // Check if the next IFD offset
   3091                 // 1. Exists within the boundaries of the input stream
   3092                 // 2. Does not point to a previously read IFD.
   3093                 if (offset > 0L && offset < dataInputStream.mLength) {
   3094                     if (!mAttributesOffsets.contains((int) offset)) {
   3095                         // Save offset of current IFD to prevent reading an IFD that is already read
   3096                         mAttributesOffsets.add(dataInputStream.mPosition);
   3097                         dataInputStream.seek(offset);
   3098                         readImageFileDirectory(dataInputStream, nextIfdType);
   3099                     } else {
   3100                         Log.w(TAG, "Skip jump into the IFD since it has already been read: "
   3101                                 + "IfdType " + nextIfdType + " (at " + offset + ")");
   3102                     }
   3103                 } else {
   3104                     Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
   3105                 }
   3106 
   3107                 dataInputStream.seek(nextEntryOffset);
   3108                 continue;
   3109             }
   3110 
   3111             byte[] bytes = new byte[(int) byteCount];
   3112             dataInputStream.readFully(bytes);
   3113             ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytes);
   3114             mAttributes[ifdType].put(tag.name, attribute);
   3115 
   3116             // DNG files have a DNG Version tag specifying the version of specifications that the
   3117             // image file is following.
   3118             // See http://fileformats.archiveteam.org/wiki/DNG
   3119             if (tag.name == TAG_DNG_VERSION) {
   3120                 mMimeType = IMAGE_TYPE_DNG;
   3121             }
   3122 
   3123             // PEF files have a Make or Model tag that begins with "PENTAX" or a compression tag
   3124             // that is 65535.
   3125             // See http://fileformats.archiveteam.org/wiki/Pentax_PEF
   3126             if (((tag.name == TAG_MAKE || tag.name == TAG_MODEL)
   3127                     && attribute.getStringValue(mExifByteOrder).contains(PEF_SIGNATURE))
   3128                     || (tag.name == TAG_COMPRESSION
   3129                     && attribute.getIntValue(mExifByteOrder) == 65535)) {
   3130                 mMimeType = IMAGE_TYPE_PEF;
   3131             }
   3132 
   3133             // Seek to next tag offset
   3134             if (dataInputStream.peek() != nextEntryOffset) {
   3135                 dataInputStream.seek(nextEntryOffset);
   3136             }
   3137         }
   3138 
   3139         if (dataInputStream.peek() + 4 <= dataInputStream.mLength) {
   3140             int nextIfdOffset = dataInputStream.readInt();
   3141             if (DEBUG) {
   3142                 Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset));
   3143             }
   3144             // Check if the next IFD offset
   3145             // 1. Exists within the boundaries of the input stream
   3146             // 2. Does not point to a previously read IFD.
   3147             if (nextIfdOffset > 0L && nextIfdOffset < dataInputStream.mLength) {
   3148                 if (!mAttributesOffsets.contains(nextIfdOffset)) {
   3149                     // Save offset of current IFD to prevent reading an IFD that is already read.
   3150                     mAttributesOffsets.add(dataInputStream.mPosition);
   3151                     dataInputStream.seek(nextIfdOffset);
   3152                     // Do not overwrite thumbnail IFD data if it alreay exists.
   3153                     if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
   3154                         readImageFileDirectory(dataInputStream, IFD_TYPE_THUMBNAIL);
   3155                     } else if (mAttributes[IFD_TYPE_PREVIEW].isEmpty()) {
   3156                         readImageFileDirectory(dataInputStream, IFD_TYPE_PREVIEW);
   3157                     }
   3158                 } else {
   3159                     Log.w(TAG, "Stop reading file since re-reading an IFD may cause an "
   3160                             + "infinite loop: " + nextIfdOffset);
   3161                 }
   3162             } else {
   3163                 Log.w(TAG, "Stop reading file since a wrong offset may cause an infinite loop: "
   3164                         + nextIfdOffset);
   3165             }
   3166         }
   3167     }
   3168 
   3169     /**
   3170      * JPEG compressed images do not contain IMAGE_LENGTH & IMAGE_WIDTH tags.
   3171      * This value uses JpegInterchangeFormat(JPEG data offset) value, and calls getJpegAttributes()
   3172      * to locate SOF(Start of Frame) marker and update the image length & width values.
   3173      * See JEITA CP-3451C Table 5 and Section 4.8.1. B.
   3174      */
   3175     private void retrieveJpegImageSize(ByteOrderedDataInputStream in, int imageType)
   3176             throws IOException {
   3177         // Check if image already has IMAGE_LENGTH & IMAGE_WIDTH values
   3178         ExifAttribute imageLengthAttribute =
   3179                 (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_LENGTH);
   3180         ExifAttribute imageWidthAttribute =
   3181                 (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_WIDTH);
   3182 
   3183         if (imageLengthAttribute == null || imageWidthAttribute == null) {
   3184             // Find if offset for JPEG data exists
   3185             ExifAttribute jpegInterchangeFormatAttribute =
   3186                     (ExifAttribute) mAttributes[imageType].get(TAG_JPEG_INTERCHANGE_FORMAT);
   3187             if (jpegInterchangeFormatAttribute != null) {
   3188                 int jpegInterchangeFormat =
   3189                         jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder);
   3190 
   3191                 // Searches for SOF marker in JPEG data and updates IMAGE_LENGTH & IMAGE_WIDTH tags
   3192                 getJpegAttributes(in, jpegInterchangeFormat, imageType);
   3193             }
   3194         }
   3195     }
   3196 
   3197     // Sets thumbnail offset & length attributes based on JpegInterchangeFormat or StripOffsets tags
   3198     private void setThumbnailData(ByteOrderedDataInputStream in) throws IOException {
   3199         HashMap thumbnailData = mAttributes[IFD_TYPE_THUMBNAIL];
   3200 
   3201         ExifAttribute compressionAttribute =
   3202                 (ExifAttribute) thumbnailData.get(TAG_COMPRESSION);
   3203         if (compressionAttribute != null) {
   3204             mThumbnailCompression = compressionAttribute.getIntValue(mExifByteOrder);
   3205             switch (mThumbnailCompression) {
   3206                 case DATA_JPEG: {
   3207                     handleThumbnailFromJfif(in, thumbnailData);
   3208                     break;
   3209                 }
   3210                 case DATA_UNCOMPRESSED:
   3211                 case DATA_JPEG_COMPRESSED: {
   3212                     if (isSupportedDataType(thumbnailData)) {
   3213                         handleThumbnailFromStrips(in, thumbnailData);
   3214                     }
   3215                     break;
   3216                 }
   3217             }
   3218         } else {
   3219             // Thumbnail data may not contain Compression tag value
   3220             handleThumbnailFromJfif(in, thumbnailData);
   3221         }
   3222     }
   3223 
   3224     // Check JpegInterchangeFormat(JFIF) tags to retrieve thumbnail offset & length values
   3225     // and reads the corresponding bytes if stream does not support seek function
   3226     private void handleThumbnailFromJfif(ByteOrderedDataInputStream in, HashMap thumbnailData)
   3227             throws IOException {
   3228         ExifAttribute jpegInterchangeFormatAttribute =
   3229                 (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT);
   3230         ExifAttribute jpegInterchangeFormatLengthAttribute =
   3231                 (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
   3232         if (jpegInterchangeFormatAttribute != null
   3233                 && jpegInterchangeFormatLengthAttribute != null) {
   3234             int thumbnailOffset = jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder);
   3235             int thumbnailLength = jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder);
   3236 
   3237             // The following code limits the size of thumbnail size not to overflow EXIF data area.
   3238             thumbnailLength = Math.min(thumbnailLength, in.available() - thumbnailOffset);
   3239             if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF
   3240                     || mMimeType == IMAGE_TYPE_RW2) {
   3241                 thumbnailOffset += mExifOffset;
   3242             } else if (mMimeType == IMAGE_TYPE_ORF) {
   3243                 // Update offset value since RAF files have IFD data preceding MakerNote data.
   3244                 thumbnailOffset += mOrfMakerNoteOffset;
   3245             }
   3246             if (DEBUG) {
   3247                 Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset
   3248                         + ", length: " + thumbnailLength);
   3249             }
   3250             if (thumbnailOffset > 0 && thumbnailLength > 0) {
   3251                 mHasThumbnail = true;
   3252                 mThumbnailOffset = thumbnailOffset;
   3253                 mThumbnailLength = thumbnailLength;
   3254                 mThumbnailCompression = DATA_JPEG;
   3255 
   3256                 if (mFilename == null && mAssetInputStream == null
   3257                         && mSeekableFileDescriptor == null) {
   3258                     // Save the thumbnail in memory if the input doesn't support reading again.
   3259                     byte[] thumbnailBytes = new byte[thumbnailLength];
   3260                     in.seek(thumbnailOffset);
   3261                     in.readFully(thumbnailBytes);
   3262                     mThumbnailBytes = thumbnailBytes;
   3263                 }
   3264             }
   3265         }
   3266     }
   3267 
   3268     // Check StripOffsets & StripByteCounts tags to retrieve thumbnail offset & length values
   3269     private void handleThumbnailFromStrips(ByteOrderedDataInputStream in, HashMap thumbnailData)
   3270             throws IOException {
   3271         ExifAttribute stripOffsetsAttribute =
   3272                 (ExifAttribute) thumbnailData.get(TAG_STRIP_OFFSETS);
   3273         ExifAttribute stripByteCountsAttribute =
   3274                 (ExifAttribute) thumbnailData.get(TAG_STRIP_BYTE_COUNTS);
   3275 
   3276         if (stripOffsetsAttribute != null && stripByteCountsAttribute != null) {
   3277             long[] stripOffsets =
   3278                     convertToLongArray(stripOffsetsAttribute.getValue(mExifByteOrder));
   3279             long[] stripByteCounts =
   3280                     convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder));
   3281 
   3282             if (stripOffsets == null) {
   3283                 Log.w(TAG, "stripOffsets should not be null.");
   3284                 return;
   3285             }
   3286             if (stripByteCounts == null) {
   3287                 Log.w(TAG, "stripByteCounts should not be null.");
   3288                 return;
   3289             }
   3290 
   3291             // Set thumbnail byte array data for non-consecutive strip bytes
   3292             byte[] totalStripBytes =
   3293                     new byte[(int) Arrays.stream(stripByteCounts).sum()];
   3294 
   3295             int bytesRead = 0;
   3296             int bytesAdded = 0;
   3297             for (int i = 0; i < stripOffsets.length; i++) {
   3298                 int stripOffset = (int) stripOffsets[i];
   3299                 int stripByteCount = (int) stripByteCounts[i];
   3300 
   3301                 // Skip to offset
   3302                 int skipBytes = stripOffset - bytesRead;
   3303                 if (skipBytes < 0) {
   3304                     Log.d(TAG, "Invalid strip offset value");
   3305                 }
   3306                 in.seek(skipBytes);
   3307                 bytesRead += skipBytes;
   3308 
   3309                 // Read strip bytes
   3310                 byte[] stripBytes = new byte[stripByteCount];
   3311                 in.read(stripBytes);
   3312                 bytesRead += stripByteCount;
   3313 
   3314                 // Add bytes to array
   3315                 System.arraycopy(stripBytes, 0, totalStripBytes, bytesAdded,
   3316                         stripBytes.length);
   3317                 bytesAdded += stripBytes.length;
   3318             }
   3319 
   3320             mHasThumbnail = true;
   3321             mThumbnailBytes = totalStripBytes;
   3322             mThumbnailLength = totalStripBytes.length;
   3323         }
   3324     }
   3325 
   3326     // Check if thumbnail data type is currently supported or not
   3327     private boolean isSupportedDataType(HashMap thumbnailData) throws IOException {
   3328         ExifAttribute bitsPerSampleAttribute =
   3329                 (ExifAttribute) thumbnailData.get(TAG_BITS_PER_SAMPLE);
   3330         if (bitsPerSampleAttribute != null) {
   3331             int[] bitsPerSampleValue = (int[]) bitsPerSampleAttribute.getValue(mExifByteOrder);
   3332 
   3333             if (Arrays.equals(BITS_PER_SAMPLE_RGB, bitsPerSampleValue)) {
   3334                 return true;
   3335             }
   3336 
   3337             // See DNG Specification 1.4.0.0. Section 3, Compression.
   3338             if (mMimeType == IMAGE_TYPE_DNG) {
   3339                 ExifAttribute photometricInterpretationAttribute =
   3340                         (ExifAttribute) thumbnailData.get(TAG_PHOTOMETRIC_INTERPRETATION);
   3341                 if (photometricInterpretationAttribute != null) {
   3342                     int photometricInterpretationValue
   3343                             = photometricInterpretationAttribute.getIntValue(mExifByteOrder);
   3344                     if ((photometricInterpretationValue == PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO
   3345                             && Arrays.equals(bitsPerSampleValue, BITS_PER_SAMPLE_GREYSCALE_2))
   3346                             || ((photometricInterpretationValue == PHOTOMETRIC_INTERPRETATION_YCBCR)
   3347                             && (Arrays.equals(bitsPerSampleValue, BITS_PER_SAMPLE_RGB)))) {
   3348                         return true;
   3349                     } else {
   3350                         // TODO: Add support for lossless Huffman JPEG data
   3351                     }
   3352                 }
   3353             }
   3354         }
   3355         if (DEBUG) {
   3356             Log.d(TAG, "Unsupported data type value");
   3357         }
   3358         return false;
   3359     }
   3360 
   3361     // Returns true if the image length and width values are <= 512.
   3362     // See Section 4.8 of http://standardsproposals.bsigroup.com/Home/getPDF/567
   3363     private boolean isThumbnail(HashMap map) throws IOException {
   3364         ExifAttribute imageLengthAttribute = (ExifAttribute) map.get(TAG_IMAGE_LENGTH);
   3365         ExifAttribute imageWidthAttribute = (ExifAttribute) map.get(TAG_IMAGE_WIDTH);
   3366 
   3367         if (imageLengthAttribute != null && imageWidthAttribute != null) {
   3368             int imageLengthValue = imageLengthAttribute.getIntValue(mExifByteOrder);
   3369             int imageWidthValue = imageWidthAttribute.getIntValue(mExifByteOrder);
   3370             if (imageLengthValue <= MAX_THUMBNAIL_SIZE && imageWidthValue <= MAX_THUMBNAIL_SIZE) {
   3371                 return true;
   3372             }
   3373         }
   3374         return false;
   3375     }
   3376 
   3377     // Validate primary, preview, thumbnail image data by comparing image size
   3378     private void validateImages(InputStream in) throws IOException {
   3379         // Swap images based on size (primary > preview > thumbnail)
   3380         swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_PREVIEW);
   3381         swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_THUMBNAIL);
   3382         swapBasedOnImageSize(IFD_TYPE_PREVIEW, IFD_TYPE_THUMBNAIL);
   3383 
   3384         // Check if image has PixelXDimension/PixelYDimension tags, which contain valid image
   3385         // sizes, excluding padding at the right end or bottom end of the image to make sure that
   3386         // the values are multiples of 64. See JEITA CP-3451C Table 5 and Section 4.8.1. B.
   3387         ExifAttribute pixelXDimAttribute =
   3388                 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_X_DIMENSION);
   3389         ExifAttribute pixelYDimAttribute =
   3390                 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_Y_DIMENSION);
   3391         if (pixelXDimAttribute != null && pixelYDimAttribute != null) {
   3392             mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, pixelXDimAttribute);
   3393             mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, pixelYDimAttribute);
   3394         }
   3395 
   3396         // Check whether thumbnail image exists and whether preview image satisfies the thumbnail
   3397         // image requirements
   3398         if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
   3399             if (isThumbnail(mAttributes[IFD_TYPE_PREVIEW])) {
   3400                 mAttributes[IFD_TYPE_THUMBNAIL] = mAttributes[IFD_TYPE_PREVIEW];
   3401                 mAttributes[IFD_TYPE_PREVIEW] = new HashMap();
   3402             }
   3403         }
   3404 
   3405         // Check if the thumbnail image satisfies the thumbnail size requirements
   3406         if (!isThumbnail(mAttributes[IFD_TYPE_THUMBNAIL])) {
   3407             Log.d(TAG, "No image meets the size requirements of a thumbnail image.");
   3408         }
   3409     }
   3410 
   3411     /**
   3412      * If image is uncompressed, ImageWidth/Length tags are used to store size info.
   3413      * However, uncompressed images often store extra pixels around the edges of the final image,
   3414      * which results in larger values for TAG_IMAGE_WIDTH and TAG_IMAGE_LENGTH tags.
   3415      * This method corrects those tag values by checking first the values of TAG_DEFAULT_CROP_SIZE
   3416      * See DNG Specification 1.4.0.0. Section 4. (DefaultCropSize)
   3417      *
   3418      * If image is a RW2 file, valid image sizes are stored in SensorBorder tags.
   3419      * See tiff_parser.cc GetFullDimension32()
   3420      * */
   3421     private void updateImageSizeValues(ByteOrderedDataInputStream in, int imageType)
   3422             throws IOException {
   3423         // Uncompressed image valid image size values
   3424         ExifAttribute defaultCropSizeAttribute =
   3425                 (ExifAttribute) mAttributes[imageType].get(TAG_DEFAULT_CROP_SIZE);
   3426         // RW2 image valid image size values
   3427         ExifAttribute topBorderAttribute =
   3428                 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_TOP_BORDER);
   3429         ExifAttribute leftBorderAttribute =
   3430                 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_LEFT_BORDER);
   3431         ExifAttribute bottomBorderAttribute =
   3432                 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_BOTTOM_BORDER);
   3433         ExifAttribute rightBorderAttribute =
   3434                 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_RIGHT_BORDER);
   3435 
   3436         if (defaultCropSizeAttribute != null) {
   3437             // Update for uncompressed image
   3438             ExifAttribute defaultCropSizeXAttribute, defaultCropSizeYAttribute;
   3439             if (defaultCropSizeAttribute.format == IFD_FORMAT_URATIONAL) {
   3440                 Rational[] defaultCropSizeValue =
   3441                         (Rational[]) defaultCropSizeAttribute.getValue(mExifByteOrder);
   3442                 defaultCropSizeXAttribute =
   3443                         ExifAttribute.createURational(defaultCropSizeValue[0], mExifByteOrder);
   3444                 defaultCropSizeYAttribute =
   3445                         ExifAttribute.createURational(defaultCropSizeValue[1], mExifByteOrder);
   3446             } else {
   3447                 int[] defaultCropSizeValue =
   3448                         (int[]) defaultCropSizeAttribute.getValue(mExifByteOrder);
   3449                 defaultCropSizeXAttribute =
   3450                         ExifAttribute.createUShort(defaultCropSizeValue[0], mExifByteOrder);
   3451                 defaultCropSizeYAttribute =
   3452                         ExifAttribute.createUShort(defaultCropSizeValue[1], mExifByteOrder);
   3453             }
   3454             mAttributes[imageType].put(TAG_IMAGE_WIDTH, defaultCropSizeXAttribute);
   3455             mAttributes[imageType].put(TAG_IMAGE_LENGTH, defaultCropSizeYAttribute);
   3456         } else if (topBorderAttribute != null && leftBorderAttribute != null &&
   3457                 bottomBorderAttribute != null && rightBorderAttribute != null) {
   3458             // Update for RW2 image
   3459             int topBorderValue = topBorderAttribute.getIntValue(mExifByteOrder);
   3460             int bottomBorderValue = bottomBorderAttribute.getIntValue(mExifByteOrder);
   3461             int rightBorderValue = rightBorderAttribute.getIntValue(mExifByteOrder);
   3462             int leftBorderValue = leftBorderAttribute.getIntValue(mExifByteOrder);
   3463             if (bottomBorderValue > topBorderValue && rightBorderValue > leftBorderValue) {
   3464                 int length = bottomBorderValue - topBorderValue;
   3465                 int width = rightBorderValue - leftBorderValue;
   3466                 ExifAttribute imageLengthAttribute =
   3467                         ExifAttribute.createUShort(length, mExifByteOrder);
   3468                 ExifAttribute imageWidthAttribute =
   3469                         ExifAttribute.createUShort(width, mExifByteOrder);
   3470                 mAttributes[imageType].put(TAG_IMAGE_LENGTH, imageLengthAttribute);
   3471                 mAttributes[imageType].put(TAG_IMAGE_WIDTH, imageWidthAttribute);
   3472             }
   3473         } else {
   3474             retrieveJpegImageSize(in, imageType);
   3475         }
   3476     }
   3477 
   3478     // Writes an Exif segment into the given output stream.
   3479     private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream,
   3480             int exifOffsetFromBeginning) throws IOException {
   3481         // The following variables are for calculating each IFD tag group size in bytes.
   3482         int[] ifdOffsets = new int[EXIF_TAGS.length];
   3483         int[] ifdDataSizes = new int[EXIF_TAGS.length];
   3484 
   3485         // Remove IFD pointer tags (we'll re-add it later.)
   3486         for (ExifTag tag : EXIF_POINTER_TAGS) {
   3487             removeAttribute(tag.name);
   3488         }
   3489         // Remove old thumbnail data
   3490         removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
   3491         removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
   3492 
   3493         // Remove null value tags.
   3494         for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
   3495             for (Object obj : mAttributes[ifdType].entrySet().toArray()) {
   3496                 final Map.Entry entry = (Map.Entry) obj;
   3497                 if (entry.getValue() == null) {
   3498                     mAttributes[ifdType].remove(entry.getKey());
   3499                 }
   3500             }
   3501         }
   3502 
   3503         // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD
   3504         // offset when there is one or more tags in the thumbnail IFD.
   3505         if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) {
   3506             mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name,
   3507                     ExifAttribute.createULong(0, mExifByteOrder));
   3508         }
   3509         if (!mAttributes[IFD_TYPE_GPS].isEmpty()) {
   3510             mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name,
   3511                     ExifAttribute.createULong(0, mExifByteOrder));
   3512         }
   3513         if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) {
   3514             mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name,
   3515                     ExifAttribute.createULong(0, mExifByteOrder));
   3516         }
   3517         if (mHasThumbnail) {
   3518             mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
   3519                     ExifAttribute.createULong(0, mExifByteOrder));
   3520             mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
   3521                     ExifAttribute.createULong(mThumbnailLength, mExifByteOrder));
   3522         }
   3523 
   3524         // Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
   3525         // value which has a bigger size than 4 bytes.
   3526         for (int i = 0; i < EXIF_TAGS.length; ++i) {
   3527             int sum = 0;
   3528             for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
   3529                 final ExifAttribute exifAttribute = (ExifAttribute) entry.getValue();
   3530                 final int size = exifAttribute.size();
   3531                 if (size > 4) {
   3532                     sum += size;
   3533                 }
   3534             }
   3535             ifdDataSizes[i] += sum;
   3536         }
   3537 
   3538         // Calculate IFD offsets.
   3539         int position = 8;
   3540         for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
   3541             if (!mAttributes[ifdType].isEmpty()) {
   3542                 ifdOffsets[ifdType] = position;
   3543                 position += 2 + mAttributes[ifdType].size() * 12 + 4 + ifdDataSizes[ifdType];
   3544             }
   3545         }
   3546         if (mHasThumbnail) {
   3547             int thumbnailOffset = position;
   3548             mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
   3549                     ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
   3550             mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset;
   3551             position += mThumbnailLength;
   3552         }
   3553 
   3554         // Calculate the total size
   3555         int totalSize = position + 8;  // eight bytes is for header part.
   3556         if (DEBUG) {
   3557             Log.d(TAG, "totalSize length: " + totalSize);
   3558             for (int i = 0; i < EXIF_TAGS.length; ++i) {
   3559                 Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d",
   3560                         i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i]));
   3561             }
   3562         }
   3563 
   3564         // Update IFD pointer tags with the calculated offsets.
   3565         if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) {
   3566             mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name,
   3567                     ExifAttribute.createULong(ifdOffsets[IFD_TYPE_EXIF], mExifByteOrder));
   3568         }
   3569         if (!mAttributes[IFD_TYPE_GPS].isEmpty()) {
   3570             mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name,
   3571                     ExifAttribute.createULong(ifdOffsets[IFD_TYPE_GPS], mExifByteOrder));
   3572         }
   3573         if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) {
   3574             mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name, ExifAttribute.createULong(
   3575                     ifdOffsets[IFD_TYPE_INTEROPERABILITY], mExifByteOrder));
   3576         }
   3577 
   3578         // Write TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
   3579         dataOutputStream.writeUnsignedShort(totalSize);
   3580         dataOutputStream.write(IDENTIFIER_EXIF_APP1);
   3581         dataOutputStream.writeShort(mExifByteOrder == ByteOrder.BIG_ENDIAN
   3582                 ? BYTE_ALIGN_MM : BYTE_ALIGN_II);
   3583         dataOutputStream.setByteOrder(mExifByteOrder);
   3584         dataOutputStream.writeUnsignedShort(START_CODE);
   3585         dataOutputStream.writeUnsignedInt(IFD_OFFSET);
   3586 
   3587         // Write IFD groups. See JEITA CP-3451C Section 4.5.8. Figure 9.
   3588         for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
   3589             if (!mAttributes[ifdType].isEmpty()) {
   3590                 // See JEITA CP-3451C Section 4.6.2: IFD structure.
   3591                 // Write entry count
   3592                 dataOutputStream.writeUnsignedShort(mAttributes[ifdType].size());
   3593 
   3594                 // Write entry info
   3595                 int dataOffset = ifdOffsets[ifdType] + 2 + mAttributes[ifdType].size() * 12 + 4;
   3596                 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[ifdType].entrySet()) {
   3597                     // Convert tag name to tag number.
   3598                     final ExifTag tag =
   3599                             (ExifTag) sExifTagMapsForWriting[ifdType].get(entry.getKey());
   3600                     final int tagNumber = tag.number;
   3601                     final ExifAttribute attribute = (ExifAttribute) entry.getValue();
   3602                     final int size = attribute.size();
   3603 
   3604                     dataOutputStream.writeUnsignedShort(tagNumber);
   3605                     dataOutputStream.writeUnsignedShort(attribute.format);
   3606                     dataOutputStream.writeInt(attribute.numberOfComponents);
   3607                     if (size > 4) {
   3608                         dataOutputStream.writeUnsignedInt(dataOffset);
   3609                         dataOffset += size;
   3610                     } else {
   3611                         dataOutputStream.write(attribute.bytes);
   3612                         // Fill zero up to 4 bytes
   3613                         if (size < 4) {
   3614                             for (int i = size; i < 4; ++i) {
   3615                                 dataOutputStream.writeByte(0);
   3616                             }
   3617                         }
   3618                     }
   3619                 }
   3620 
   3621                 // Write the next offset. It writes the offset of thumbnail IFD if there is one or
   3622                 // more tags in the thumbnail IFD when the current IFD is the primary image TIFF
   3623                 // IFD; Otherwise 0.
   3624                 if (ifdType == 0 && !mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
   3625                     dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_TYPE_THUMBNAIL]);
   3626                 } else {
   3627                     dataOutputStream.writeUnsignedInt(0);
   3628                 }
   3629 
   3630                 // Write values of data field exceeding 4 bytes after the next offset.
   3631                 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[ifdType].entrySet()) {
   3632                     ExifAttribute attribute = (ExifAttribute) entry.getValue();
   3633 
   3634                     if (attribute.bytes.length > 4) {
   3635                         dataOutputStream.write(attribute.bytes, 0, attribute.bytes.length);
   3636                     }
   3637                 }
   3638             }
   3639         }
   3640 
   3641         // Write thumbnail
   3642         if (mHasThumbnail) {
   3643             dataOutputStream.write(getThumbnailBytes());
   3644         }
   3645 
   3646         // Reset the byte order to big endian in order to write remaining parts of the JPEG file.
   3647         dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
   3648 
   3649         return totalSize;
   3650     }
   3651 
   3652     /**
   3653      * Determines the data format of EXIF entry value.
   3654      *
   3655      * @param entryValue The value to be determined.
   3656      * @return Returns two data formats gussed as a pair in integer. If there is no two candidate
   3657                data formats for the given entry value, returns {@code -1} in the second of the pair.
   3658      */
   3659     private static Pair<Integer, Integer> guessDataFormat(String entryValue) {
   3660         // See TIFF 6.0 Section 2, "Image File Directory".
   3661         // Take the first component if there are more than one component.
   3662         if (entryValue.contains(",")) {
   3663             String[] entryValues = entryValue.split(",");
   3664             Pair<Integer, Integer> dataFormat = guessDataFormat(entryValues[0]);
   3665             if (dataFormat.first == IFD_FORMAT_STRING) {
   3666                 return dataFormat;
   3667             }
   3668             for (int i = 1; i < entryValues.length; ++i) {
   3669                 final Pair<Integer, Integer> guessDataFormat = guessDataFormat(entryValues[i]);
   3670                 int first = -1, second = -1;
   3671                 if (guessDataFormat.first == dataFormat.first
   3672                         || guessDataFormat.second == dataFormat.first) {
   3673                     first = dataFormat.first;
   3674                 }
   3675                 if (dataFormat.second != -1 && (guessDataFormat.first == dataFormat.second
   3676                         || guessDataFormat.second == dataFormat.second)) {
   3677                     second = dataFormat.second;
   3678                 }
   3679                 if (first == -1 && second == -1) {
   3680                     return new Pair<>(IFD_FORMAT_STRING, -1);
   3681                 }
   3682                 if (first == -1) {
   3683                     dataFormat = new Pair<>(second, -1);
   3684                     continue;
   3685                 }
   3686                 if (second == -1) {
   3687                     dataFormat = new Pair<>(first, -1);
   3688                     continue;
   3689                 }
   3690             }
   3691             return dataFormat;
   3692         }
   3693 
   3694         if (entryValue.contains("/")) {
   3695             String[] rationalNumber = entryValue.split("/");
   3696             if (rationalNumber.length == 2) {
   3697                 try {
   3698                     long numerator = (long) Double.parseDouble(rationalNumber[0]);
   3699                     long denominator = (long) Double.parseDouble(rationalNumber[1]);
   3700                     if (numerator < 0L || denominator < 0L) {
   3701                         return new Pair<>(IFD_FORMAT_SRATIONAL, -1);
   3702                     }
   3703                     if (numerator > Integer.MAX_VALUE || denominator > Integer.MAX_VALUE) {
   3704                         return new Pair<>(IFD_FORMAT_URATIONAL, -1);
   3705                     }
   3706                     return new Pair<>(IFD_FORMAT_SRATIONAL, IFD_FORMAT_URATIONAL);
   3707                 } catch (NumberFormatException e)  {
   3708                     // Ignored
   3709                 }
   3710             }
   3711             return new Pair<>(IFD_FORMAT_STRING, -1);
   3712         }
   3713         try {
   3714             Long longValue = Long.parseLong(entryValue);
   3715             if (longValue >= 0 && longValue <= 65535) {
   3716                 return new Pair<>(IFD_FORMAT_USHORT, IFD_FORMAT_ULONG);
   3717             }
   3718             if (longValue < 0) {
   3719                 return new Pair<>(IFD_FORMAT_SLONG, -1);
   3720             }
   3721             return new Pair<>(IFD_FORMAT_ULONG, -1);
   3722         } catch (NumberFormatException e) {
   3723             // Ignored
   3724         }
   3725         try {
   3726             Double.parseDouble(entryValue);
   3727             return new Pair<>(IFD_FORMAT_DOUBLE, -1);
   3728         } catch (NumberFormatException e) {
   3729             // Ignored
   3730         }
   3731         return new Pair<>(IFD_FORMAT_STRING, -1);
   3732     }
   3733 
   3734     // An input stream to parse EXIF data area, which can be written in either little or big endian
   3735     // order.
   3736     private static class ByteOrderedDataInputStream extends InputStream implements DataInput {
   3737         private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN;
   3738         private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN;
   3739 
   3740         private DataInputStream mDataInputStream;
   3741         private InputStream mInputStream;
   3742         private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN;
   3743         private final int mLength;
   3744         private int mPosition;
   3745 
   3746         public ByteOrderedDataInputStream(InputStream in) throws IOException {
   3747             mInputStream = in;
   3748             mDataInputStream = new DataInputStream(in);
   3749             mLength = mDataInputStream.available();
   3750             mPosition = 0;
   3751             mDataInputStream.mark(mLength);
   3752         }
   3753 
   3754         public ByteOrderedDataInputStream(byte[] bytes) throws IOException {
   3755             this(new ByteArrayInputStream(bytes));
   3756         }
   3757 
   3758         public void setByteOrder(ByteOrder byteOrder) {
   3759             mByteOrder = byteOrder;
   3760         }
   3761 
   3762         public void seek(long byteCount) throws IOException {
   3763             if (mPosition > byteCount) {
   3764                 mPosition = 0;
   3765                 mDataInputStream.reset();
   3766                 mDataInputStream.mark(mLength);
   3767             } else {
   3768                 byteCount -= mPosition;
   3769             }
   3770 
   3771             if (skipBytes((int) byteCount) != (int) byteCount) {
   3772                 throw new IOException("Couldn't seek up to the byteCount");
   3773             }
   3774         }
   3775 
   3776         public int peek() {
   3777             return mPosition;
   3778         }
   3779 
   3780         @Override
   3781         public int available() throws IOException {
   3782             return mDataInputStream.available();
   3783         }
   3784 
   3785         @Override
   3786         public int read() throws IOException {
   3787             ++mPosition;
   3788             return mDataInputStream.read();
   3789         }
   3790 
   3791         @Override
   3792         public int readUnsignedByte() throws IOException {
   3793             ++mPosition;
   3794             return mDataInputStream.readUnsignedByte();
   3795         }
   3796 
   3797         @Override
   3798         public String readLine() throws IOException {
   3799             Log.d(TAG, "Currently unsupported");
   3800             return null;
   3801         }
   3802 
   3803         @Override
   3804         public boolean readBoolean() throws IOException {
   3805             ++mPosition;
   3806             return mDataInputStream.readBoolean();
   3807         }
   3808 
   3809         @Override
   3810         public char readChar() throws IOException {
   3811             mPosition += 2;
   3812             return mDataInputStream.readChar();
   3813         }
   3814 
   3815         @Override
   3816         public String readUTF() throws IOException {
   3817             mPosition += 2;
   3818             return mDataInputStream.readUTF();
   3819         }
   3820 
   3821         @Override
   3822         public void readFully(byte[] buffer, int offset, int length) throws IOException {
   3823             mPosition += length;
   3824             if (mPosition > mLength) {
   3825                 throw new EOFException();
   3826             }
   3827             if (mDataInputStream.read(buffer, offset, length) != length) {
   3828                 throw new IOException("Couldn't read up to the length of buffer");
   3829             }
   3830         }
   3831 
   3832         @Override
   3833         public void readFully(byte[] buffer) throws IOException {
   3834             mPosition += buffer.length;
   3835             if (mPosition > mLength) {
   3836                 throw new EOFException();
   3837             }
   3838             if (mDataInputStream.read(buffer, 0, buffer.length) != buffer.length) {
   3839                 throw new IOException("Couldn't read up to the length of buffer");
   3840             }
   3841         }
   3842 
   3843         @Override
   3844         public byte readByte() throws IOException {
   3845             ++mPosition;
   3846             if (mPosition > mLength) {
   3847                 throw new EOFException();
   3848             }
   3849             int ch = mDataInputStream.read();
   3850             if (ch < 0) {
   3851                 throw new EOFException();
   3852             }
   3853             return (byte) ch;
   3854         }
   3855 
   3856         @Override
   3857         public short readShort() throws IOException {
   3858             mPosition += 2;
   3859             if (mPosition > mLength) {
   3860                 throw new EOFException();
   3861             }
   3862             int ch1 = mDataInputStream.read();
   3863             int ch2 = mDataInputStream.read();
   3864             if ((ch1 | ch2) < 0) {
   3865                 throw new EOFException();
   3866             }
   3867             if (mByteOrder == LITTLE_ENDIAN) {
   3868                 return (short) ((ch2 << 8) + (ch1));
   3869             } else if (mByteOrder == BIG_ENDIAN) {
   3870                 return (short) ((ch1 << 8) + (ch2));
   3871             }
   3872             throw new IOException("Invalid byte order: " + mByteOrder);
   3873         }
   3874 
   3875         @Override
   3876         public int readInt() throws IOException {
   3877             mPosition += 4;
   3878             if (mPosition > mLength) {
   3879                 throw new EOFException();
   3880             }
   3881             int ch1 = mDataInputStream.read();
   3882             int ch2 = mDataInputStream.read();
   3883             int ch3 = mDataInputStream.read();
   3884             int ch4 = mDataInputStream.read();
   3885             if ((ch1 | ch2 | ch3 | ch4) < 0) {
   3886                 throw new EOFException();
   3887             }
   3888             if (mByteOrder == LITTLE_ENDIAN) {
   3889                 return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1);
   3890             } else if (mByteOrder == BIG_ENDIAN) {
   3891                 return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4);
   3892             }
   3893             throw new IOException("Invalid byte order: " + mByteOrder);
   3894         }
   3895 
   3896         @Override
   3897         public int skipBytes(int byteCount) throws IOException {
   3898             int totalSkip = Math.min(byteCount, mLength - mPosition);
   3899             int skipped = 0;
   3900             while (skipped < totalSkip) {
   3901                 skipped += mDataInputStream.skipBytes(totalSkip - skipped);
   3902             }
   3903             mPosition += skipped;
   3904             return skipped;
   3905         }
   3906 
   3907         public int readUnsignedShort() throws IOException {
   3908             mPosition += 2;
   3909             if (mPosition > mLength) {
   3910                 throw new EOFException();
   3911             }
   3912             int ch1 = mDataInputStream.read();
   3913             int ch2 = mDataInputStream.read();
   3914             if ((ch1 | ch2) < 0) {
   3915                 throw new EOFException();
   3916             }
   3917             if (mByteOrder == LITTLE_ENDIAN) {
   3918                 return ((ch2 << 8) + (ch1));
   3919             } else if (mByteOrder == BIG_ENDIAN) {
   3920                 return ((ch1 << 8) + (ch2));
   3921             }
   3922             throw new IOException("Invalid byte order: " + mByteOrder);
   3923         }
   3924 
   3925         public long readUnsignedInt() throws IOException {
   3926             return readInt() & 0xffffffffL;
   3927         }
   3928 
   3929         @Override
   3930         public long readLong() throws IOException {
   3931             mPosition += 8;
   3932             if (mPosition > mLength) {
   3933                 throw new EOFException();
   3934             }
   3935             int ch1 = mDataInputStream.read();
   3936             int ch2 = mDataInputStream.read();
   3937             int ch3 = mDataInputStream.read();
   3938             int ch4 = mDataInputStream.read();
   3939             int ch5 = mDataInputStream.read();
   3940             int ch6 = mDataInputStream.read();
   3941             int ch7 = mDataInputStream.read();
   3942             int ch8 = mDataInputStream.read();
   3943             if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) {
   3944                 throw new EOFException();
   3945             }
   3946             if (mByteOrder == LITTLE_ENDIAN) {
   3947                 return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40)
   3948                         + ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16)
   3949                         + ((long) ch2 << 8) + (long) ch1);
   3950             } else if (mByteOrder == BIG_ENDIAN) {
   3951                 return (((long) ch1 << 56) + ((long) ch2 << 48) + ((long) ch3 << 40)
   3952                         + ((long) ch4 << 32) + ((long) ch5 << 24) + ((long) ch6 << 16)
   3953                         + ((long) ch7 << 8) + (long) ch8);
   3954             }
   3955             throw new IOException("Invalid byte order: " + mByteOrder);
   3956         }
   3957 
   3958         @Override
   3959         public float readFloat() throws IOException {
   3960             return Float.intBitsToFloat(readInt());
   3961         }
   3962 
   3963         @Override
   3964         public double readDouble() throws IOException {
   3965             return Double.longBitsToDouble(readLong());
   3966         }
   3967     }
   3968 
   3969     // An output stream to write EXIF data area, which can be written in either little or big endian
   3970     // order.
   3971     private static class ByteOrderedDataOutputStream extends FilterOutputStream {
   3972         private final OutputStream mOutputStream;
   3973         private ByteOrder mByteOrder;
   3974 
   3975         public ByteOrderedDataOutputStream(OutputStream out, ByteOrder byteOrder) {
   3976             super(out);
   3977             mOutputStream = out;
   3978             mByteOrder = byteOrder;
   3979         }
   3980 
   3981         public void setByteOrder(ByteOrder byteOrder) {
   3982             mByteOrder = byteOrder;
   3983         }
   3984 
   3985         public void write(byte[] bytes) throws IOException {
   3986             mOutputStream.write(bytes);
   3987         }
   3988 
   3989         public void write(byte[] bytes, int offset, int length) throws IOException {
   3990             mOutputStream.write(bytes, offset, length);
   3991         }
   3992 
   3993         public void writeByte(int val) throws IOException {
   3994             mOutputStream.write(val);
   3995         }
   3996 
   3997         public void writeShort(short val) throws IOException {
   3998             if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
   3999                 mOutputStream.write((val >>> 0) & 0xFF);
   4000                 mOutputStream.write((val >>> 8) & 0xFF);
   4001             } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
   4002                 mOutputStream.write((val >>> 8) & 0xFF);
   4003                 mOutputStream.write((val >>> 0) & 0xFF);
   4004             }
   4005         }
   4006 
   4007         public void writeInt(int val) throws IOException {
   4008             if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
   4009                 mOutputStream.write((val >>> 0) & 0xFF);
   4010                 mOutputStream.write((val >>> 8) & 0xFF);
   4011                 mOutputStream.write((val >>> 16) & 0xFF);
   4012                 mOutputStream.write((val >>> 24) & 0xFF);
   4013             } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
   4014                 mOutputStream.write((val >>> 24) & 0xFF);
   4015                 mOutputStream.write((val >>> 16) & 0xFF);
   4016                 mOutputStream.write((val >>> 8) & 0xFF);
   4017                 mOutputStream.write((val >>> 0) & 0xFF);
   4018             }
   4019         }
   4020 
   4021         public void writeUnsignedShort(int val) throws IOException {
   4022             writeShort((short) val);
   4023         }
   4024 
   4025         public void writeUnsignedInt(long val) throws IOException {
   4026             writeInt((int) val);
   4027         }
   4028     }
   4029 
   4030     // Swaps image data based on image size
   4031     private void swapBasedOnImageSize(@IfdType int firstIfdType, @IfdType int secondIfdType)
   4032             throws IOException {
   4033         if (mAttributes[firstIfdType].isEmpty() || mAttributes[secondIfdType].isEmpty()) {
   4034             if (DEBUG) {
   4035                 Log.d(TAG, "Cannot perform swap since only one image data exists");
   4036             }
   4037             return;
   4038         }
   4039 
   4040         ExifAttribute firstImageLengthAttribute =
   4041                 (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_LENGTH);
   4042         ExifAttribute firstImageWidthAttribute =
   4043                 (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_WIDTH);
   4044         ExifAttribute secondImageLengthAttribute =
   4045                 (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_LENGTH);
   4046         ExifAttribute secondImageWidthAttribute =
   4047                 (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_WIDTH);
   4048 
   4049         if (firstImageLengthAttribute == null || firstImageWidthAttribute == null) {
   4050             if (DEBUG) {
   4051                 Log.d(TAG, "First image does not contain valid size information");
   4052             }
   4053         } else if (secondImageLengthAttribute == null || secondImageWidthAttribute == null) {
   4054             if (DEBUG) {
   4055                 Log.d(TAG, "Second image does not contain valid size information");
   4056             }
   4057         } else {
   4058             int firstImageLengthValue = firstImageLengthAttribute.getIntValue(mExifByteOrder);
   4059             int firstImageWidthValue = firstImageWidthAttribute.getIntValue(mExifByteOrder);
   4060             int secondImageLengthValue = secondImageLengthAttribute.getIntValue(mExifByteOrder);
   4061             int secondImageWidthValue = secondImageWidthAttribute.getIntValue(mExifByteOrder);
   4062 
   4063             if (firstImageLengthValue < secondImageLengthValue &&
   4064                     firstImageWidthValue < secondImageWidthValue) {
   4065                 HashMap tempMap = mAttributes[firstIfdType];
   4066                 mAttributes[firstIfdType] = mAttributes[secondIfdType];
   4067                 mAttributes[secondIfdType] = tempMap;
   4068             }
   4069         }
   4070     }
   4071 
   4072     // Checks if there is a match
   4073     private boolean containsMatch(byte[] mainBytes, byte[] findBytes) {
   4074         for (int i = 0; i < mainBytes.length - findBytes.length; i++) {
   4075             for (int j = 0; j < findBytes.length; j++) {
   4076                 if (mainBytes[i + j] != findBytes[j]) {
   4077                     break;
   4078                 }
   4079                 if (j == findBytes.length - 1) {
   4080                     return true;
   4081                 }
   4082             }
   4083         }
   4084         return false;
   4085     }
   4086 
   4087     /**
   4088      * Convert given int[] to long[]. If long[] is given, just return it.
   4089      * Return null for other types of input.
   4090      */
   4091     private static long[] convertToLongArray(Object inputObj) {
   4092         if (inputObj instanceof int[]) {
   4093             int[] input = (int[]) inputObj;
   4094             long[] result = new long[input.length];
   4095             for (int i = 0; i < input.length; i++) {
   4096                 result[i] = input[i];
   4097             }
   4098             return result;
   4099         } else if (inputObj instanceof long[]) {
   4100             return (long[]) inputObj;
   4101         }
   4102         return null;
   4103     }
   4104 }
   4105