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