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