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