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