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