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