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