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