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