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