ExifInterface.java revision c048c5f74a07ddf341533610436c7f1d30dd7714
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;
27
28import java.io.BufferedInputStream;
29import java.io.ByteArrayInputStream;
30import java.io.DataInputStream;
31import java.io.DataOutputStream;
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.IOException;
39import java.io.InputStream;
40import java.io.OutputStream;
41import java.nio.ByteOrder;
42import java.nio.charset.Charset;
43import java.text.ParsePosition;
44import java.text.SimpleDateFormat;
45import java.util.Arrays;
46import java.util.Date;
47import java.util.HashMap;
48import java.util.Map;
49import java.util.Set;
50import java.util.TimeZone;
51import java.util.regex.Pattern;
52
53import libcore.io.IoUtils;
54import libcore.io.Streams;
55
56/**
57 * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file.
58 * <p>
59 * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF and RAF.
60 * <p>
61 * Attribute mutation is supported for JPEG image files.
62 */
63public class ExifInterface {
64    private static final String TAG = "ExifInterface";
65    private static final boolean DEBUG = false;
66
67    // The Exif tag names
68    /** Type is String. */
69    public static final String TAG_ARTIST = "Artist";
70    /** Type is int. */
71    public static final String TAG_BITS_PER_SAMPLE = "BitsPerSample";
72    /** Type is int. */
73    public static final String TAG_COMPRESSION = "Compression";
74    /** Type is String. */
75    public static final String TAG_COPYRIGHT = "Copyright";
76    /** Type is String. */
77    public static final String TAG_DATETIME = "DateTime";
78    /** Type is String. */
79    public static final String TAG_IMAGE_DESCRIPTION = "ImageDescription";
80    /** Type is int. */
81    public static final String TAG_IMAGE_LENGTH = "ImageLength";
82    /** Type is int. */
83    public static final String TAG_IMAGE_WIDTH = "ImageWidth";
84    /** Type is int. */
85    public static final String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
86    /** Type is int. */
87    public static final String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
88    /** Type is String. */
89    public static final String TAG_MAKE = "Make";
90    /** Type is String. */
91    public static final String TAG_MODEL = "Model";
92    /** Type is int. */
93    public static final String TAG_ORIENTATION = "Orientation";
94    /** Type is int. */
95    public static final String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
96    /** Type is int. */
97    public static final String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
98    /** Type is rational. */
99    public static final String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
100    /** Type is rational. */
101    public static final String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
102    /** Type is int. */
103    public static final String TAG_RESOLUTION_UNIT = "ResolutionUnit";
104    /** Type is int. */
105    public static final String TAG_ROWS_PER_STRIP = "RowsPerStrip";
106    /** Type is int. */
107    public static final String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
108    /** Type is String. */
109    public static final String TAG_SOFTWARE = "Software";
110    /** Type is int. */
111    public static final String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
112    /** Type is int. */
113    public static final String TAG_STRIP_OFFSETS = "StripOffsets";
114    /** Type is int. */
115    public static final String TAG_TRANSFER_FUNCTION = "TransferFunction";
116    /** Type is rational. */
117    public static final String TAG_WHITE_POINT = "WhitePoint";
118    /** Type is rational. */
119    public static final String TAG_X_RESOLUTION = "XResolution";
120    /** Type is rational. */
121    public static final String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
122    /** Type is int. */
123    public static final String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
124    /** Type is int. */
125    public static final String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
126    /** Type is rational. */
127    public static final String TAG_Y_RESOLUTION = "YResolution";
128    /** Type is rational. */
129    public static final String TAG_APERTURE_VALUE = "ApertureValue";
130    /** Type is rational. */
131    public static final String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
132    /** Type is String. */
133    public static final String TAG_CFA_PATTERN = "CFAPattern";
134    /** Type is int. */
135    public static final String TAG_COLOR_SPACE = "ColorSpace";
136    /** Type is String. */
137    public static final String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
138    /** Type is rational. */
139    public static final String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
140    /** Type is int. */
141    public static final String TAG_CONTRAST = "Contrast";
142    /** Type is int. */
143    public static final String TAG_CUSTOM_RENDERED = "CustomRendered";
144    /** Type is String. */
145    public static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
146    /** Type is String. */
147    public static final String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
148    /** Type is String. */
149    public static final String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
150    /** Type is double. */
151    public static final String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
152    /** Type is String. */
153    public static final String TAG_EXIF_VERSION = "ExifVersion";
154    /** Type is double. */
155    public static final String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
156    /** Type is rational. */
157    public static final String TAG_EXPOSURE_INDEX = "ExposureIndex";
158    /** Type is int. */
159    public static final String TAG_EXPOSURE_MODE = "ExposureMode";
160    /** Type is int. */
161    public static final String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
162    /** Type is double. */
163    public static final String TAG_EXPOSURE_TIME = "ExposureTime";
164    /** Type is double. */
165    public static final String TAG_F_NUMBER = "FNumber";
166    /**
167     * Type is double.
168     *
169     * @deprecated use {@link #TAG_F_NUMBER} instead
170     */
171    @Deprecated
172    public static final String TAG_APERTURE = "FNumber";
173    /** Type is String. */
174    public static final String TAG_FILE_SOURCE = "FileSource";
175    /** Type is int. */
176    public static final String TAG_FLASH = "Flash";
177    /** Type is rational. */
178    public static final String TAG_FLASH_ENERGY = "FlashEnergy";
179    /** Type is String. */
180    public static final String TAG_FLASHPIX_VERSION = "FlashpixVersion";
181    /** Type is rational. */
182    public static final String TAG_FOCAL_LENGTH = "FocalLength";
183    /** Type is int. */
184    public static final String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
185    /** Type is int. */
186    public static final String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
187    /** Type is rational. */
188    public static final String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
189    /** Type is rational. */
190    public static final String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
191    /** Type is rational. */
192    public static final String TAG_GAIN_CONTROL = "GainControl";
193    /** Type is int. */
194    public static final String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
195    /**
196     * Type is int.
197     *
198     * @deprecated use {@link #TAG_ISO_SPEED_RATINGS} instead
199     */
200    @Deprecated
201    public static final String TAG_ISO = "ISOSpeedRatings";
202    /** Type is String. */
203    public static final String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
204    /** Type is int. */
205    public static final String TAG_LIGHT_SOURCE = "LightSource";
206    /** Type is String. */
207    public static final String TAG_MAKER_NOTE = "MakerNote";
208    /** Type is rational. */
209    public static final String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
210    /** Type is int. */
211    public static final String TAG_METERING_MODE = "MeteringMode";
212    /** Type is String. */
213    public static final String TAG_OECF = "OECF";
214    /** Type is int. */
215    public static final String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
216    /** Type is int. */
217    public static final String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
218    /** Type is String. */
219    public static final String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
220    /** Type is int. */
221    public static final String TAG_SATURATION = "Saturation";
222    /** Type is int. */
223    public static final String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
224    /** Type is String. */
225    public static final String TAG_SCENE_TYPE = "SceneType";
226    /** Type is int. */
227    public static final String TAG_SENSING_METHOD = "SensingMethod";
228    /** Type is int. */
229    public static final String TAG_SHARPNESS = "Sharpness";
230    /** Type is rational. */
231    public static final String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
232    /** Type is String. */
233    public static final String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
234    /** Type is String. */
235    public static final String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
236    /** Type is int. */
237    public static final String TAG_SUBSEC_TIME = "SubSecTime";
238    /** Type is int. */
239    public static final String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
240    /** Type is int. */
241    public static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
242    /** Type is int. */
243    public static final String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
244    /** Type is int. */
245    public static final String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
246    /** Type is int. */
247    public static final String TAG_SUBJECT_AREA = "SubjectArea";
248    /** Type is double. */
249    public static final String TAG_SUBJECT_DISTANCE = "SubjectDistance";
250    /** Type is int. */
251    public static final String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
252    /** Type is int. */
253    public static final String TAG_SUBJECT_LOCATION = "SubjectLocation";
254    /** Type is String. */
255    public static final String TAG_USER_COMMENT = "UserComment";
256    /** Type is int. */
257    public static final String TAG_WHITE_BALANCE = "WhiteBalance";
258    /**
259     * The altitude (in meters) based on the reference in TAG_GPS_ALTITUDE_REF.
260     * Type is rational.
261     */
262    public static final String TAG_GPS_ALTITUDE = "GPSAltitude";
263    /**
264     * 0 if the altitude is above sea level. 1 if the altitude is below sea
265     * level. Type is int.
266     */
267    public static final String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
268    /** Type is String. */
269    public static final String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
270    /** Type is rational. */
271    public static final String TAG_GPS_DOP = "GPSDOP";
272    /** Type is String. */
273    public static final String TAG_GPS_DATESTAMP = "GPSDateStamp";
274    /** Type is rational. */
275    public static final String TAG_GPS_DEST_BEARING = "GPSDestBearing";
276    /** Type is String. */
277    public static final String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
278    /** Type is rational. */
279    public static final String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
280    /** Type is String. */
281    public static final String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
282    /** Type is rational. */
283    public static final String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
284    /** Type is String. */
285    public static final String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
286    /** Type is rational. */
287    public static final String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
288    /** Type is String. */
289    public static final String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
290    /** Type is int. */
291    public static final String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
292    /** Type is rational. */
293    public static final String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
294    /** Type is String. */
295    public static final String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
296    /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
297    public static final String TAG_GPS_LATITUDE = "GPSLatitude";
298    /** Type is String. */
299    public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
300    /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
301    public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
302    /** Type is String. */
303    public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
304    /** Type is String. */
305    public static final String TAG_GPS_MAP_DATUM = "GPSMapDatum";
306    /** Type is String. */
307    public static final String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
308    /** Type is String. Name of GPS processing method used for location finding. */
309    public static final String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
310    /** Type is String. */
311    public static final String TAG_GPS_SATELLITES = "GPSSatellites";
312    /** Type is rational. */
313    public static final String TAG_GPS_SPEED = "GPSSpeed";
314    /** Type is String. */
315    public static final String TAG_GPS_SPEED_REF = "GPSSpeedRef";
316    /** Type is String. */
317    public static final String TAG_GPS_STATUS = "GPSStatus";
318    /** Type is String. */
319    public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
320    /** Type is rational. */
321    public static final String TAG_GPS_TRACK = "GPSTrack";
322    /** Type is String. */
323    public static final String TAG_GPS_TRACK_REF = "GPSTrackRef";
324    /** Type is String. */
325    public static final String TAG_GPS_VERSION_ID = "GPSVersionID";
326    /** Type is String. */
327    public static final String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
328    /** Type is int. */
329    public static final String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
330    /** Type is int. */
331    public static final String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
332
333    // Private tags used for pointing the other IFD offset. The types of the following tags are int.
334    private static final String TAG_EXIF_IFD_POINTER = "ExifIFDPointer";
335    private static final String TAG_GPS_INFO_IFD_POINTER = "GPSInfoIFDPointer";
336    private static final String TAG_INTEROPERABILITY_IFD_POINTER = "InteroperabilityIFDPointer";
337
338    // Private tags used for thumbnail information.
339    private static final String TAG_HAS_THUMBNAIL = "hasThumbnail";
340    private static final String TAG_THUMBNAIL_OFFSET = "thumbnailOffset";
341    private static final String TAG_THUMBNAIL_LENGTH = "thumbnailLength";
342    private static final String TAG_THUMBNAIL_DATA = "thumbnailData";
343
344    // Constants used for the Orientation Exif tag.
345    public static final int ORIENTATION_UNDEFINED = 0;
346    public static final int ORIENTATION_NORMAL = 1;
347    public static final int ORIENTATION_FLIP_HORIZONTAL = 2;  // left right reversed mirror
348    public static final int ORIENTATION_ROTATE_180 = 3;
349    public static final int ORIENTATION_FLIP_VERTICAL = 4;  // upside down mirror
350    // flipped about top-left <--> bottom-right axis
351    public static final int ORIENTATION_TRANSPOSE = 5;
352    public static final int ORIENTATION_ROTATE_90 = 6;  // rotate 90 cw to right it
353    // flipped about top-right <--> bottom-left axis
354    public static final int ORIENTATION_TRANSVERSE = 7;
355    public static final int ORIENTATION_ROTATE_270 = 8;  // rotate 270 to right it
356
357    // Constants used for white balance
358    public static final int WHITEBALANCE_AUTO = 0;
359    public static final int WHITEBALANCE_MANUAL = 1;
360
361    private static final byte[] JPEG_SIGNATURE = new byte[] {(byte) 0xff, (byte) 0xd8, (byte) 0xff};
362    private static final int JPEG_SIGNATURE_SIZE = 3;
363
364    private static SimpleDateFormat sFormatter;
365
366    // See Exchangeable image file format for digital still cameras: Exif version 2.2.
367    // The following values are for parsing EXIF data area. There are tag groups in EXIF data area.
368    // They are called "Image File Directory". They have multiple data formats to cover various
369    // image metadata from GPS longitude to camera model name.
370
371    // Types of Exif byte alignments (see JEITA CP-3451 page 10)
372    private static final short BYTE_ALIGN_II = 0x4949;  // II: Intel order
373    private static final short BYTE_ALIGN_MM = 0x4d4d;  // MM: Motorola order
374
375    // Formats for the value in IFD entry (See TIFF 6.0 spec Types page 15).
376    private static final int IFD_FORMAT_BYTE = 1;
377    private static final int IFD_FORMAT_STRING = 2;
378    private static final int IFD_FORMAT_USHORT = 3;
379    private static final int IFD_FORMAT_ULONG = 4;
380    private static final int IFD_FORMAT_URATIONAL = 5;
381    private static final int IFD_FORMAT_SBYTE = 6;
382    private static final int IFD_FORMAT_UNDEFINED = 7;
383    private static final int IFD_FORMAT_SSHORT = 8;
384    private static final int IFD_FORMAT_SLONG = 9;
385    private static final int IFD_FORMAT_SRATIONAL = 10;
386    private static final int IFD_FORMAT_SINGLE = 11;
387    private static final int IFD_FORMAT_DOUBLE = 12;
388    // Sizes of the components of each IFD value format
389    private static final int[] IFD_FORMAT_BYTES_PER_FORMAT = new int[] {
390            0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8
391    };
392    private static final byte[] EXIF_ASCII_PREFIX = new byte[] {
393            0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0
394    };
395
396    // A class for indicating EXIF tag.
397    private static class ExifTag {
398        public final int number;
399        public final String name;
400
401        private ExifTag(String name, int number) {
402            this.name = name;
403            this.number = number;
404        }
405    }
406
407    // Primary image IFD TIFF tags (See JEITA CP-3451 Table 14. page 54).
408    private static final ExifTag[] IFD_TIFF_TAGS = new ExifTag[] {
409            new ExifTag(TAG_IMAGE_WIDTH, 256),
410            new ExifTag(TAG_IMAGE_LENGTH, 257),
411            new ExifTag(TAG_BITS_PER_SAMPLE, 258),
412            new ExifTag(TAG_COMPRESSION, 259),
413            new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262),
414            new ExifTag(TAG_IMAGE_DESCRIPTION, 270),
415            new ExifTag(TAG_MAKE, 271),
416            new ExifTag(TAG_MODEL, 272),
417            new ExifTag(TAG_STRIP_OFFSETS, 273),
418            new ExifTag(TAG_ORIENTATION, 274),
419            new ExifTag(TAG_SAMPLES_PER_PIXEL, 277),
420            new ExifTag(TAG_ROWS_PER_STRIP, 278),
421            new ExifTag(TAG_STRIP_BYTE_COUNTS, 279),
422            new ExifTag(TAG_X_RESOLUTION, 282),
423            new ExifTag(TAG_Y_RESOLUTION, 283),
424            new ExifTag(TAG_PLANAR_CONFIGURATION, 284),
425            new ExifTag(TAG_RESOLUTION_UNIT, 296),
426            new ExifTag(TAG_TRANSFER_FUNCTION, 301),
427            new ExifTag(TAG_SOFTWARE, 305),
428            new ExifTag(TAG_DATETIME, 306),
429            new ExifTag(TAG_ARTIST, 315),
430            new ExifTag(TAG_WHITE_POINT, 318),
431            new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319),
432            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513),
433            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514),
434            new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529),
435            new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530),
436            new ExifTag(TAG_Y_CB_CR_POSITIONING, 531),
437            new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532),
438            new ExifTag(TAG_COPYRIGHT, 33432),
439            new ExifTag(TAG_EXIF_IFD_POINTER, 34665),
440            new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853),
441    };
442    // Primary image IFD Exif Private tags (See JEITA CP-3451 Table 15. page 55).
443    private static final ExifTag[] IFD_EXIF_TAGS = new ExifTag[] {
444            new ExifTag(TAG_EXPOSURE_TIME, 33434),
445            new ExifTag(TAG_F_NUMBER, 33437),
446            new ExifTag(TAG_EXPOSURE_PROGRAM, 34850),
447            new ExifTag(TAG_SPECTRAL_SENSITIVITY, 34852),
448            new ExifTag(TAG_ISO_SPEED_RATINGS, 34855),
449            new ExifTag(TAG_OECF, 34856),
450            new ExifTag(TAG_EXIF_VERSION, 36864),
451            new ExifTag(TAG_DATETIME_ORIGINAL, 36867),
452            new ExifTag(TAG_DATETIME_DIGITIZED, 36868),
453            new ExifTag(TAG_COMPONENTS_CONFIGURATION, 37121),
454            new ExifTag(TAG_COMPRESSED_BITS_PER_PIXEL, 37122),
455            new ExifTag(TAG_SHUTTER_SPEED_VALUE, 37377),
456            new ExifTag(TAG_APERTURE_VALUE, 37378),
457            new ExifTag(TAG_BRIGHTNESS_VALUE, 37379),
458            new ExifTag(TAG_EXPOSURE_BIAS_VALUE, 37380),
459            new ExifTag(TAG_MAX_APERTURE_VALUE, 37381),
460            new ExifTag(TAG_SUBJECT_DISTANCE, 37382),
461            new ExifTag(TAG_METERING_MODE, 37383),
462            new ExifTag(TAG_LIGHT_SOURCE, 37384),
463            new ExifTag(TAG_FLASH, 37385),
464            new ExifTag(TAG_FOCAL_LENGTH, 37386),
465            new ExifTag(TAG_SUBJECT_AREA, 37396),
466            new ExifTag(TAG_MAKER_NOTE, 37500),
467            new ExifTag(TAG_USER_COMMENT, 37510),
468            new ExifTag(TAG_SUBSEC_TIME, 37520),
469            new ExifTag(TAG_SUBSEC_TIME_ORIG, 37521),
470            new ExifTag(TAG_SUBSEC_TIME_DIG, 37522),
471            new ExifTag(TAG_FLASHPIX_VERSION, 40960),
472            new ExifTag(TAG_COLOR_SPACE, 40961),
473            new ExifTag(TAG_PIXEL_X_DIMENSION, 40962),
474            new ExifTag(TAG_PIXEL_Y_DIMENSION, 40963),
475            new ExifTag(TAG_RELATED_SOUND_FILE, 40964),
476            new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965),
477            new ExifTag(TAG_FLASH_ENERGY, 41483),
478            new ExifTag(TAG_SPATIAL_FREQUENCY_RESPONSE, 41484),
479            new ExifTag(TAG_FOCAL_PLANE_X_RESOLUTION, 41486),
480            new ExifTag(TAG_FOCAL_PLANE_Y_RESOLUTION, 41487),
481            new ExifTag(TAG_FOCAL_PLANE_RESOLUTION_UNIT, 41488),
482            new ExifTag(TAG_SUBJECT_LOCATION, 41492),
483            new ExifTag(TAG_EXPOSURE_INDEX, 41493),
484            new ExifTag(TAG_SENSING_METHOD, 41495),
485            new ExifTag(TAG_FILE_SOURCE, 41728),
486            new ExifTag(TAG_SCENE_TYPE, 41729),
487            new ExifTag(TAG_CFA_PATTERN, 41730),
488            new ExifTag(TAG_CUSTOM_RENDERED, 41985),
489            new ExifTag(TAG_EXPOSURE_MODE, 41986),
490            new ExifTag(TAG_WHITE_BALANCE, 41987),
491            new ExifTag(TAG_DIGITAL_ZOOM_RATIO, 41988),
492            new ExifTag(TAG_FOCAL_LENGTH_IN_35MM_FILM, 41989),
493            new ExifTag(TAG_SCENE_CAPTURE_TYPE, 41990),
494            new ExifTag(TAG_GAIN_CONTROL, 41991),
495            new ExifTag(TAG_CONTRAST, 41992),
496            new ExifTag(TAG_SATURATION, 41993),
497            new ExifTag(TAG_SHARPNESS, 41994),
498            new ExifTag(TAG_DEVICE_SETTING_DESCRIPTION, 41995),
499            new ExifTag(TAG_SUBJECT_DISTANCE_RANGE, 41996),
500            new ExifTag(TAG_IMAGE_UNIQUE_ID, 42016),
501    };
502    // Primary image IFD GPS Info tags (See JEITA CP-3451 Table 16. page 56).
503    private static final ExifTag[] IFD_GPS_TAGS = new ExifTag[] {
504            new ExifTag(TAG_GPS_VERSION_ID, 0),
505            new ExifTag(TAG_GPS_LATITUDE_REF, 1),
506            new ExifTag(TAG_GPS_LATITUDE, 2),
507            new ExifTag(TAG_GPS_LONGITUDE_REF, 3),
508            new ExifTag(TAG_GPS_LONGITUDE, 4),
509            new ExifTag(TAG_GPS_ALTITUDE_REF, 5),
510            new ExifTag(TAG_GPS_ALTITUDE, 6),
511            new ExifTag(TAG_GPS_TIMESTAMP, 7),
512            new ExifTag(TAG_GPS_SATELLITES, 8),
513            new ExifTag(TAG_GPS_STATUS, 9),
514            new ExifTag(TAG_GPS_MEASURE_MODE, 10),
515            new ExifTag(TAG_GPS_DOP, 11),
516            new ExifTag(TAG_GPS_SPEED_REF, 12),
517            new ExifTag(TAG_GPS_SPEED, 13),
518            new ExifTag(TAG_GPS_TRACK_REF, 14),
519            new ExifTag(TAG_GPS_TRACK, 15),
520            new ExifTag(TAG_GPS_IMG_DIRECTION_REF, 16),
521            new ExifTag(TAG_GPS_IMG_DIRECTION, 17),
522            new ExifTag(TAG_GPS_MAP_DATUM, 18),
523            new ExifTag(TAG_GPS_DEST_LATITUDE_REF, 19),
524            new ExifTag(TAG_GPS_DEST_LATITUDE, 20),
525            new ExifTag(TAG_GPS_DEST_LONGITUDE_REF, 21),
526            new ExifTag(TAG_GPS_DEST_LONGITUDE, 22),
527            new ExifTag(TAG_GPS_DEST_BEARING_REF, 23),
528            new ExifTag(TAG_GPS_DEST_BEARING, 24),
529            new ExifTag(TAG_GPS_DEST_DISTANCE_REF, 25),
530            new ExifTag(TAG_GPS_DEST_DISTANCE, 26),
531            new ExifTag(TAG_GPS_PROCESSING_METHOD, 27),
532            new ExifTag(TAG_GPS_AREA_INFORMATION, 28),
533            new ExifTag(TAG_GPS_DATESTAMP, 29),
534            new ExifTag(TAG_GPS_DIFFERENTIAL, 30),
535    };
536    // Primary image IFD Interoperability tag (See JEITA CP-3451 Table 17. page 56).
537    private static final ExifTag[] IFD_INTEROPERABILITY_TAGS = new ExifTag[] {
538            new ExifTag(TAG_INTEROPERABILITY_INDEX, 1),
539    };
540    // IFD Thumbnail tags (See JEITA CP-3451 Table 18. page 57).
541    private static final ExifTag[] IFD_THUMBNAIL_TAGS = new ExifTag[] {
542            new ExifTag(TAG_THUMBNAIL_IMAGE_WIDTH, 256),
543            new ExifTag(TAG_THUMBNAIL_IMAGE_LENGTH, 257),
544            new ExifTag(TAG_BITS_PER_SAMPLE, 258),
545            new ExifTag(TAG_COMPRESSION, 259),
546            new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262),
547            new ExifTag(TAG_IMAGE_DESCRIPTION, 270),
548            new ExifTag(TAG_MAKE, 271),
549            new ExifTag(TAG_MODEL, 272),
550            new ExifTag(TAG_STRIP_OFFSETS, 273),
551            new ExifTag(TAG_ORIENTATION, 274),
552            new ExifTag(TAG_SAMPLES_PER_PIXEL, 277),
553            new ExifTag(TAG_ROWS_PER_STRIP, 278),
554            new ExifTag(TAG_STRIP_BYTE_COUNTS, 279),
555            new ExifTag(TAG_X_RESOLUTION, 282),
556            new ExifTag(TAG_Y_RESOLUTION, 283),
557            new ExifTag(TAG_PLANAR_CONFIGURATION, 284),
558            new ExifTag(TAG_RESOLUTION_UNIT, 296),
559            new ExifTag(TAG_TRANSFER_FUNCTION, 301),
560            new ExifTag(TAG_SOFTWARE, 305),
561            new ExifTag(TAG_DATETIME, 306),
562            new ExifTag(TAG_ARTIST, 315),
563            new ExifTag(TAG_WHITE_POINT, 318),
564            new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319),
565            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513),
566            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514),
567            new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529),
568            new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530),
569            new ExifTag(TAG_Y_CB_CR_POSITIONING, 531),
570            new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532),
571            new ExifTag(TAG_COPYRIGHT, 33432),
572            new ExifTag(TAG_EXIF_IFD_POINTER, 34665),
573            new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853),
574    };
575
576    // See JEITA CP-3451 Figure 5. page 9.
577    // The following values are used for indicating pointers to the other Image File Directorys.
578
579    // Indices of Exif Ifd tag groups
580    private static final int IFD_TIFF_HINT = 0;
581    private static final int IFD_EXIF_HINT = 1;
582    private static final int IFD_GPS_HINT = 2;
583    private static final int IFD_INTEROPERABILITY_HINT = 3;
584    private static final int IFD_THUMBNAIL_HINT = 4;
585    // List of Exif tag groups
586    private static final ExifTag[][] EXIF_TAGS = new ExifTag[][] {
587            IFD_TIFF_TAGS, IFD_EXIF_TAGS, IFD_GPS_TAGS, IFD_INTEROPERABILITY_TAGS,
588            IFD_THUMBNAIL_TAGS
589    };
590    // List of tags for pointing to the other image file directory offset.
591    private static final ExifTag[] IFD_POINTER_TAGS = new ExifTag[] {
592            new ExifTag(TAG_EXIF_IFD_POINTER, 34665),
593            new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853),
594            new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965),
595    };
596    // List of indices of the indicated tag groups according to the IFD_POINTER_TAGS
597    private static final int[] IFD_POINTER_TAG_HINTS = new int[] {
598            IFD_EXIF_HINT, IFD_GPS_HINT, IFD_INTEROPERABILITY_HINT
599    };
600    // Tags for indicating the thumbnail offset and length
601    private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG =
602            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513);
603    private static final ExifTag JPEG_INTERCHANGE_FORMAT_LENGTH_TAG =
604            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514);
605
606    // Mappings from tag number to tag name and each item represents one IFD tag group.
607    private static final HashMap[] sExifTagMapsForReading = new HashMap[EXIF_TAGS.length];
608    // Mappings from tag name to tag number and each item represents one IFD tag group.
609    private static final HashMap[] sExifTagMapsForWriting = new HashMap[EXIF_TAGS.length];
610
611    // See JPEG File Interchange Format Version 1.02.
612    // The following values are defined for handling JPEG streams. In this implementation, we are
613    // not only getting information from EXIF but also from some JPEG special segments such as
614    // MARKER_COM for user comment and MARKER_SOFx for image width and height.
615
616    // Identifier for EXIF APP1 segment in JPEG
617    private static final byte[] IDENTIFIER_EXIF_APP1 =
618            "Exif\0\0".getBytes(Charset.forName("US-ASCII"));
619    // JPEG segment markers, that each marker consumes two bytes beginning with 0xff and ending with
620    // the indicator. There is no SOF4, SOF8, SOF16 markers in JPEG and SOFx markers indicates start
621    // of frame(baseline DCT) and the image size info exists in its beginning part.
622    private static final byte MARKER = (byte) 0xff;
623    private static final byte MARKER_SOI = (byte) 0xd8;
624    private static final byte MARKER_SOF0 = (byte) 0xc0;
625    private static final byte MARKER_SOF1 = (byte) 0xc1;
626    private static final byte MARKER_SOF2 = (byte) 0xc2;
627    private static final byte MARKER_SOF3 = (byte) 0xc3;
628    private static final byte MARKER_SOF5 = (byte) 0xc5;
629    private static final byte MARKER_SOF6 = (byte) 0xc6;
630    private static final byte MARKER_SOF7 = (byte) 0xc7;
631    private static final byte MARKER_SOF9 = (byte) 0xc9;
632    private static final byte MARKER_SOF10 = (byte) 0xca;
633    private static final byte MARKER_SOF11 = (byte) 0xcb;
634    private static final byte MARKER_SOF13 = (byte) 0xcd;
635    private static final byte MARKER_SOF14 = (byte) 0xce;
636    private static final byte MARKER_SOF15 = (byte) 0xcf;
637    private static final byte MARKER_SOS = (byte) 0xda;
638    private static final byte MARKER_APP1 = (byte) 0xe1;
639    private static final byte MARKER_COM = (byte) 0xfe;
640    private static final byte MARKER_EOI = (byte) 0xd9;
641
642    static {
643        System.loadLibrary("media_jni");
644        nativeInitRaw();
645        sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
646        sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
647
648        // Build up the hash tables to look up Exif tags for reading Exif tags.
649        for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
650            sExifTagMapsForReading[hint] = new HashMap();
651            sExifTagMapsForWriting[hint] = new HashMap();
652            for (ExifTag tag : EXIF_TAGS[hint]) {
653                sExifTagMapsForReading[hint].put(tag.number, tag.name);
654                sExifTagMapsForWriting[hint].put(tag.name, tag.number);
655            }
656        }
657    }
658
659    private final String mFilename;
660    private final FileDescriptor mSeekableFileDescriptor;
661    private final AssetManager.AssetInputStream mAssetInputStream;
662    private final boolean mIsInputStream;
663    private boolean mIsRaw;
664    private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
665    private boolean mHasThumbnail;
666    // The following values used for indicating a thumbnail position.
667    private int mThumbnailOffset;
668    private int mThumbnailLength;
669    private byte[] mThumbnailBytes;
670
671    // Pattern to check non zero timestamp
672    private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
673
674    /**
675     * Reads Exif tags from the specified image file.
676     */
677    public ExifInterface(String filename) throws IOException {
678        if (filename == null) {
679            throw new IllegalArgumentException("filename cannot be null");
680        }
681        FileInputStream in = null;
682        mAssetInputStream = null;
683        mFilename = filename;
684        mIsInputStream = false;
685        try {
686            in = new FileInputStream(filename);
687            if (isSeekableFD(in.getFD())) {
688                mSeekableFileDescriptor = in.getFD();
689            } else {
690                mSeekableFileDescriptor = null;
691            }
692            loadAttributes(in);
693        } finally {
694            IoUtils.closeQuietly(in);
695        }
696    }
697
698    /**
699     * Reads Exif tags from the specified image file descriptor. Attribute mutation is supported
700     * for writable and seekable file descriptors only. This constructor will not rewind the offset
701     * of the given file descriptor. Developers should close the file descriptor after use.
702     */
703    public ExifInterface(FileDescriptor fileDescriptor) throws IOException {
704        if (fileDescriptor == null) {
705            throw new IllegalArgumentException("fileDescriptor cannot be null");
706        }
707        mAssetInputStream = null;
708        mFilename = null;
709        if (isSeekableFD(fileDescriptor)) {
710            mSeekableFileDescriptor = fileDescriptor;
711            // Keep the original file descriptor in order to save attributes when it's seekable.
712            // Otherwise, just close the given file descriptor after reading it because the save
713            // feature won't be working.
714            try {
715                fileDescriptor = Os.dup(fileDescriptor);
716            } catch (ErrnoException e) {
717                throw e.rethrowAsIOException();
718            }
719        } else {
720            mSeekableFileDescriptor = null;
721        }
722        mIsInputStream = false;
723        FileInputStream in = null;
724        try {
725            in = new FileInputStream(fileDescriptor);
726            loadAttributes(in);
727        } finally {
728            IoUtils.closeQuietly(in);
729        }
730    }
731
732    /**
733     * Reads Exif tags from the specified image input stream. Attribute mutation is not supported
734     * for input streams. The given input stream will proceed its current position. Developers
735     * should close the input stream after use.
736     */
737    public ExifInterface(InputStream inputStream) throws IOException {
738        if (inputStream == null) {
739            throw new IllegalArgumentException("inputStream cannot be null");
740        }
741        mFilename = null;
742        if (inputStream instanceof AssetManager.AssetInputStream) {
743            mAssetInputStream = (AssetManager.AssetInputStream) inputStream;
744            mSeekableFileDescriptor = null;
745        } else if (inputStream instanceof FileInputStream
746                && isSeekableFD(((FileInputStream) inputStream).getFD())) {
747            mAssetInputStream = null;
748            mSeekableFileDescriptor = ((FileInputStream) inputStream).getFD();
749        } else {
750            mAssetInputStream = null;
751            mSeekableFileDescriptor = null;
752        }
753        mIsInputStream = true;
754        loadAttributes(inputStream);
755    }
756
757    /**
758     * Returns the value of the specified tag or {@code null} if there
759     * is no such tag in the image file.
760     *
761     * @param tag the name of the tag.
762     */
763    public String getAttribute(String tag) {
764        // Retrieves all tag groups. The value from primary image tag group has a higher priority
765        // than the value from the thumbnail tag group if there are more than one candidates.
766        for (int i = 0; i < EXIF_TAGS.length; ++i) {
767            Object value = mAttributes[i].get(tag);
768            if (value != null) {
769                return (String) value;
770            }
771        }
772        return null;
773    }
774
775    /**
776     * Returns the integer value of the specified tag. If there is no such tag
777     * in the image file or the value cannot be parsed as integer, return
778     * <var>defaultValue</var>.
779     *
780     * @param tag the name of the tag.
781     * @param defaultValue the value to return if the tag is not available.
782     */
783    public int getAttributeInt(String tag, int defaultValue) {
784        String value = getAttribute(tag);
785        if (value == null) return defaultValue;
786        try {
787            return Integer.valueOf(value);
788        } catch (NumberFormatException e) {
789            return defaultValue;
790        }
791    }
792
793    /**
794     * Returns the double value of the tag that is specified as rational or contains a
795     * double-formatted value. If there is no such tag in the image file or the value cannot be
796     * parsed as double, return <var>defaultValue</var>.
797     *
798     * @param tag the name of the tag.
799     * @param defaultValue the value to return if the tag is not available.
800     */
801    public double getAttributeDouble(String tag, double defaultValue) {
802        String value = getAttribute(tag);
803        if (value == null) return defaultValue;
804        try {
805            int index = value.indexOf("/");
806            if (index == -1) return Double.parseDouble(value);
807            double denom = Double.parseDouble(value.substring(index + 1));
808            if (denom == 0) return defaultValue;
809            double num = Double.parseDouble(value.substring(0, index));
810            return num / denom;
811        } catch (NumberFormatException e) {
812            return defaultValue;
813        }
814    }
815
816    /**
817     * Set the value of the specified tag.
818     *
819     * @param tag the name of the tag.
820     * @param value the value of the tag.
821     */
822    public void setAttribute(String tag, String value) {
823        for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
824            if (i == IFD_THUMBNAIL_HINT && !mHasThumbnail) {
825                continue;
826            }
827            if (sExifTagMapsForWriting[i].containsKey(tag)) {
828                mAttributes[i].put(tag, value);
829            }
830        }
831    }
832
833    /**
834     * Update the values of the tags in the tag groups if any value for the tag already was stored.
835     *
836     * @param tag the name of the tag.
837     * @param value the value of the tag.
838     * @return Returns {@code true} if updating is placed.
839     */
840    private boolean updateAttribute(String tag, String value) {
841        boolean updated = false;
842        for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
843            if (mAttributes[i].containsKey(tag)) {
844                mAttributes[i].put(tag, value);
845                updated = true;
846            }
847        }
848        return updated;
849    }
850
851    /**
852     * Remove any values of the specified tag.
853     *
854     * @param tag the name of the tag.
855     */
856    private void removeAttribute(String tag) {
857        for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
858            mAttributes[i].remove(tag);
859        }
860    }
861
862    /**
863     * This function decides which parser to read the image data according to the given input stream
864     * type and the content of the input stream. In each case, it reads the first three bytes to
865     * determine whether the image data format is JPEG or not.
866     */
867    private void loadAttributes(@NonNull InputStream in) throws IOException {
868        try {
869            // Initialize mAttributes.
870            for (int i = 0; i < EXIF_TAGS.length; ++i) {
871                mAttributes[i] = new HashMap();
872            }
873
874            // Process RAW input stream
875            if (mAssetInputStream != null) {
876                long asset = mAssetInputStream.getNativeAsset();
877                if (handleRawResult(nativeGetRawAttributesFromAsset(asset))) {
878                    return;
879                }
880            } else if (mSeekableFileDescriptor != null) {
881                if (handleRawResult(nativeGetRawAttributesFromFileDescriptor(
882                        mSeekableFileDescriptor))) {
883                    return;
884                }
885            } else {
886                in = new BufferedInputStream(in, JPEG_SIGNATURE_SIZE);
887                if (!isJpegInputStream((BufferedInputStream) in) && handleRawResult(
888                        nativeGetRawAttributesFromInputStream(in))) {
889                    return;
890                }
891            }
892
893            // Process JPEG input stream
894            getJpegAttributes(in);
895        } catch (IOException e) {
896            // Ignore exceptions in order to keep the compatibility with the old versions of
897            // ExifInterface.
898            Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file"
899                    + "(ExifInterface supports JPEG and some RAW image formats only) "
900                    + "or a corrupted JPEG file to ExifInterface.", e);
901        } finally {
902            if (DEBUG) {
903                printAttributes();
904            }
905        }
906    }
907
908    private static boolean isJpegInputStream(BufferedInputStream in) throws IOException {
909        in.mark(JPEG_SIGNATURE_SIZE);
910        byte[] signatureBytes = new byte[JPEG_SIGNATURE_SIZE];
911        if (in.read(signatureBytes) != JPEG_SIGNATURE_SIZE) {
912            throw new EOFException();
913        }
914        boolean isJpeg = Arrays.equals(JPEG_SIGNATURE, signatureBytes);
915        in.reset();
916        return isJpeg;
917    }
918
919    private boolean handleRawResult(HashMap map) {
920        if (map == null) {
921            return false;
922        }
923
924        // Mark for disabling the save feature.
925        mIsRaw = true;
926
927        String value = (String) map.remove(TAG_HAS_THUMBNAIL);
928        mHasThumbnail = value != null && value.equalsIgnoreCase("true");
929        value = (String) map.remove(TAG_THUMBNAIL_OFFSET);
930        if (value != null) {
931            mThumbnailOffset = Integer.parseInt(value);
932        }
933        value = (String) map.remove(TAG_THUMBNAIL_LENGTH);
934        if (value != null) {
935            mThumbnailLength = Integer.parseInt(value);
936        }
937        mThumbnailBytes = (byte[]) map.remove(TAG_THUMBNAIL_DATA);
938
939        for (Map.Entry entry : (Set<Map.Entry>) map.entrySet()) {
940            setAttribute((String) entry.getKey(), (String) entry.getValue());
941        }
942
943        return true;
944    }
945
946    private static boolean isSeekableFD(FileDescriptor fd) throws IOException {
947        try {
948            Os.lseek(fd, 0, OsConstants.SEEK_CUR);
949            return true;
950        } catch (ErrnoException e) {
951            return false;
952        }
953    }
954
955    // Prints out attributes for debugging.
956    private void printAttributes() {
957        for (int i = 0; i < mAttributes.length; ++i) {
958            Log.d(TAG, "The size of tag group[" + i + "]: " + mAttributes[i].size());
959            for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
960                Log.d(TAG, "tagName: " + entry.getKey() + ", tagValue: " + entry.getValue());
961            }
962        }
963    }
964
965    /**
966     * Save the tag data into the original image file. This is expensive because it involves
967     * copying all the data from one file to another and deleting the old file and renaming the
968     * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write
969     * and make a single call rather than multiple calls for each attribute.
970     */
971    public void saveAttributes() throws IOException {
972        if (mIsRaw) {
973            throw new UnsupportedOperationException(
974                    "ExifInterface does not support saving attributes on RAW formats.");
975        }
976        if (mIsInputStream || (mSeekableFileDescriptor == null && mFilename == null)) {
977            throw new UnsupportedOperationException(
978                    "ExifInterface does not support saving attributes for the current input.");
979        }
980
981        // Keep the thumbnail in memory
982        mThumbnailBytes = getThumbnail();
983
984        FileInputStream in = null;
985        FileOutputStream out = null;
986        File tempFile = null;
987        try {
988            // Move the original file to temporary file.
989            if (mFilename != null) {
990                tempFile = new File(mFilename + ".tmp");
991                File originalFile = new File(mFilename);
992                if (!originalFile.renameTo(tempFile)) {
993                    throw new IOException("Could'nt rename to " + tempFile.getAbsolutePath());
994                }
995            } else if (mSeekableFileDescriptor != null) {
996                tempFile = File.createTempFile("temp", "jpg");
997                Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
998                in = new FileInputStream(mSeekableFileDescriptor);
999                out = new FileOutputStream(tempFile);
1000                Streams.copy(in, out);
1001            }
1002        } catch (ErrnoException e) {
1003            throw e.rethrowAsIOException();
1004        } finally {
1005            IoUtils.closeQuietly(in);
1006            IoUtils.closeQuietly(out);
1007        }
1008
1009        in = null;
1010        out = null;
1011        try {
1012            // Save the new file.
1013            in = new FileInputStream(tempFile);
1014            if (mFilename != null) {
1015                out = new FileOutputStream(mFilename);
1016            } else if (mSeekableFileDescriptor != null) {
1017                Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
1018                out = new FileOutputStream(mSeekableFileDescriptor);
1019            }
1020            saveJpegAttributes(in, out);
1021        } catch (ErrnoException e) {
1022            throw e.rethrowAsIOException();
1023        } finally {
1024            IoUtils.closeQuietly(in);
1025            IoUtils.closeQuietly(out);
1026            tempFile.delete();
1027        }
1028
1029        // Discard the thumbnail in memory
1030        mThumbnailBytes = null;
1031    }
1032
1033    /**
1034     * Returns true if the image file has a thumbnail.
1035     */
1036    public boolean hasThumbnail() {
1037        return mHasThumbnail;
1038    }
1039
1040    /**
1041     * Returns the thumbnail inside the image file, or {@code null} if there is no thumbnail.
1042     * The returned data is in JPEG format and can be decoded using
1043     * {@link android.graphics.BitmapFactory#decodeByteArray(byte[],int,int)}
1044     */
1045    public byte[] getThumbnail() {
1046        if (!mHasThumbnail) {
1047            return null;
1048        }
1049        if (mThumbnailBytes != null) {
1050            return mThumbnailBytes;
1051        }
1052
1053        // Read the thumbnail.
1054        FileInputStream in = null;
1055        try {
1056            if (mAssetInputStream != null) {
1057                return nativeGetThumbnailFromAsset(
1058                        mAssetInputStream.getNativeAsset(), mThumbnailOffset, mThumbnailLength);
1059            } else if (mFilename != null) {
1060                in = new FileInputStream(mFilename);
1061            } else if (mSeekableFileDescriptor != null) {
1062                FileDescriptor fileDescriptor = Os.dup(mSeekableFileDescriptor);
1063                Os.lseek(fileDescriptor, 0, OsConstants.SEEK_SET);
1064                in = new FileInputStream(fileDescriptor);
1065            }
1066            if (in == null) {
1067                // Should not be reached this.
1068                throw new FileNotFoundException();
1069            }
1070            if (in.skip(mThumbnailOffset) != mThumbnailOffset) {
1071                throw new IOException("Corrupted image");
1072            }
1073            byte[] buffer = new byte[mThumbnailLength];
1074            if (in.read(buffer) != mThumbnailLength) {
1075                throw new IOException("Corrupted image");
1076            }
1077            return buffer;
1078        } catch (IOException | ErrnoException e) {
1079            // Couldn't get a thumbnail image.
1080        } finally {
1081            IoUtils.closeQuietly(in);
1082        }
1083        return null;
1084    }
1085
1086    /**
1087     * Returns the offset and length of thumbnail inside the image file, or
1088     * {@code null} if there is no thumbnail.
1089     *
1090     * @return two-element array, the offset in the first value, and length in
1091     *         the second, or {@code null} if no thumbnail was found.
1092     */
1093    public long[] getThumbnailRange() {
1094        if (!mHasThumbnail) {
1095            return null;
1096        }
1097
1098        long[] range = new long[2];
1099        range[0] = mThumbnailOffset;
1100        range[1] = mThumbnailLength;
1101
1102        return range;
1103    }
1104
1105    /**
1106     * Stores the latitude and longitude value in a float array. The first element is
1107     * the latitude, and the second element is the longitude. Returns false if the
1108     * Exif tags are not available.
1109     */
1110    public boolean getLatLong(float output[]) {
1111        String latValue = getAttribute(TAG_GPS_LATITUDE);
1112        String latRef = getAttribute(TAG_GPS_LATITUDE_REF);
1113        String lngValue = getAttribute(TAG_GPS_LONGITUDE);
1114        String lngRef = getAttribute(TAG_GPS_LONGITUDE_REF);
1115
1116        if (latValue != null && latRef != null && lngValue != null && lngRef != null) {
1117            try {
1118                output[0] = convertRationalLatLonToFloat(latValue, latRef);
1119                output[1] = convertRationalLatLonToFloat(lngValue, lngRef);
1120                return true;
1121            } catch (IllegalArgumentException e) {
1122                // if values are not parseable
1123            }
1124        }
1125
1126        return false;
1127    }
1128
1129    /**
1130     * Return the altitude in meters. If the exif tag does not exist, return
1131     * <var>defaultValue</var>.
1132     *
1133     * @param defaultValue the value to return if the tag is not available.
1134     */
1135    public double getAltitude(double defaultValue) {
1136        double altitude = getAttributeDouble(TAG_GPS_ALTITUDE, -1);
1137        int ref = getAttributeInt(TAG_GPS_ALTITUDE_REF, -1);
1138
1139        if (altitude >= 0 && ref >= 0) {
1140            return (altitude * ((ref == 1) ? -1 : 1));
1141        } else {
1142            return defaultValue;
1143        }
1144    }
1145
1146    /**
1147     * Returns number of milliseconds since Jan. 1, 1970, midnight local time.
1148     * Returns -1 if the date time information if not available.
1149     * @hide
1150     */
1151    public long getDateTime() {
1152        String dateTimeString = getAttribute(TAG_DATETIME);
1153        if (dateTimeString == null
1154                || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
1155
1156        ParsePosition pos = new ParsePosition(0);
1157        try {
1158            // The exif field is in local time. Parsing it as if it is UTC will yield time
1159            // since 1/1/1970 local time
1160            Date datetime = sFormatter.parse(dateTimeString, pos);
1161            if (datetime == null) return -1;
1162            long msecs = datetime.getTime();
1163
1164            String subSecs = getAttribute(TAG_SUBSEC_TIME);
1165            if (subSecs != null) {
1166                try {
1167                    long sub = Long.valueOf(subSecs);
1168                    while (sub > 1000) {
1169                        sub /= 10;
1170                    }
1171                    msecs += sub;
1172                } catch (NumberFormatException e) {
1173                    // Ignored
1174                }
1175            }
1176            return msecs;
1177        } catch (IllegalArgumentException e) {
1178            return -1;
1179        }
1180    }
1181
1182    /**
1183     * Returns number of milliseconds since Jan. 1, 1970, midnight UTC.
1184     * Returns -1 if the date time information if not available.
1185     * @hide
1186     */
1187    public long getGpsDateTime() {
1188        String date = getAttribute(TAG_GPS_DATESTAMP);
1189        String time = getAttribute(TAG_GPS_TIMESTAMP);
1190        if (date == null || time == null
1191                || (!sNonZeroTimePattern.matcher(date).matches()
1192                && !sNonZeroTimePattern.matcher(time).matches())) {
1193            return -1;
1194        }
1195
1196        String dateTimeString = date + ' ' + time;
1197
1198        ParsePosition pos = new ParsePosition(0);
1199        try {
1200            Date datetime = sFormatter.parse(dateTimeString, pos);
1201            if (datetime == null) return -1;
1202            return datetime.getTime();
1203        } catch (IllegalArgumentException e) {
1204            return -1;
1205        }
1206    }
1207
1208    private static float convertRationalLatLonToFloat(String rationalString, String ref) {
1209        try {
1210            String [] parts = rationalString.split(",");
1211
1212            String [] pair;
1213            pair = parts[0].split("/");
1214            double degrees = Double.parseDouble(pair[0].trim())
1215                    / Double.parseDouble(pair[1].trim());
1216
1217            pair = parts[1].split("/");
1218            double minutes = Double.parseDouble(pair[0].trim())
1219                    / Double.parseDouble(pair[1].trim());
1220
1221            pair = parts[2].split("/");
1222            double seconds = Double.parseDouble(pair[0].trim())
1223                    / Double.parseDouble(pair[1].trim());
1224
1225            double result = degrees + (minutes / 60.0) + (seconds / 3600.0);
1226            if ((ref.equals("S") || ref.equals("W"))) {
1227                return (float) -result;
1228            }
1229            return (float) result;
1230        } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
1231            // Not valid
1232            throw new IllegalArgumentException();
1233        }
1234    }
1235
1236    // Loads EXIF attributes from a JPEG input stream.
1237    private void getJpegAttributes(InputStream inputStream) throws IOException {
1238        // See JPEG File Interchange Format Specification page 5.
1239        if (DEBUG) {
1240            Log.d(TAG, "getJpegAttributes starting with: " + inputStream);
1241        }
1242        DataInputStream dataInputStream = new DataInputStream(inputStream);
1243        byte marker;
1244        int bytesRead = 0;
1245        if ((marker = dataInputStream.readByte()) != MARKER) {
1246            throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
1247        }
1248        ++bytesRead;
1249        if (dataInputStream.readByte() != MARKER_SOI) {
1250            throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
1251        }
1252        ++bytesRead;
1253        while (true) {
1254            marker = dataInputStream.readByte();
1255            if (marker != MARKER) {
1256                throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff));
1257            }
1258            ++bytesRead;
1259            marker = dataInputStream.readByte();
1260            if (DEBUG) {
1261                Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff));
1262            }
1263            ++bytesRead;
1264
1265            // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and
1266            // the image data will terminate right after.
1267            if (marker == MARKER_EOI || marker == MARKER_SOS) {
1268                break;
1269            }
1270            int length = dataInputStream.readUnsignedShort() - 2;
1271            bytesRead += 2;
1272            if (DEBUG) {
1273                Log.d(TAG, "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: "
1274                        + (length + 2) + ")");
1275            }
1276            if (length < 0) {
1277                throw new IOException("Invalid length");
1278            }
1279            switch (marker) {
1280                case MARKER_APP1: {
1281                    if (DEBUG) {
1282                        Log.d(TAG, "MARKER_APP1");
1283                    }
1284                    if (length < 6) {
1285                        // Skip if it's not an EXIF APP1 segment.
1286                        break;
1287                    }
1288                    byte[] identifier = new byte[6];
1289                    if (inputStream.read(identifier) != 6) {
1290                        throw new IOException("Invalid exif");
1291                    }
1292                    bytesRead += 6;
1293                    length -= 6;
1294                    if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
1295                        // Skip if it's not an EXIF APP1 segment.
1296                        break;
1297                    }
1298                    if (length <= 0) {
1299                        throw new IOException("Invalid exif");
1300                    }
1301                    if (DEBUG) {
1302                        Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")");
1303                    }
1304                    byte[] bytes = new byte[length];
1305                    if (dataInputStream.read(bytes) != length) {
1306                        throw new IOException("Invalid exif");
1307                    }
1308                    readExifSegment(bytes, bytesRead);
1309                    bytesRead += length;
1310                    length = 0;
1311                    break;
1312                }
1313
1314                case MARKER_COM: {
1315                    byte[] bytes = new byte[length];
1316                    if (dataInputStream.read(bytes) != length) {
1317                        throw new IOException("Invalid exif");
1318                    }
1319                    length = 0;
1320                    mAttributes[IFD_EXIF_HINT].put(TAG_USER_COMMENT,
1321                            new String(bytes, Charset.forName("US-ASCII")));
1322                    break;
1323                }
1324
1325                case MARKER_SOF0:
1326                case MARKER_SOF1:
1327                case MARKER_SOF2:
1328                case MARKER_SOF3:
1329                case MARKER_SOF5:
1330                case MARKER_SOF6:
1331                case MARKER_SOF7:
1332                case MARKER_SOF9:
1333                case MARKER_SOF10:
1334                case MARKER_SOF11:
1335                case MARKER_SOF13:
1336                case MARKER_SOF14:
1337                case MARKER_SOF15: {
1338                    if (dataInputStream.skipBytes(1) != 1) {
1339                        throw new IOException("Invalid SOFx");
1340                    }
1341                    mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH,
1342                            String.valueOf(dataInputStream.readUnsignedShort()));
1343                    mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH,
1344                            String.valueOf(dataInputStream.readUnsignedShort()));
1345                    length -= 5;
1346                    break;
1347                }
1348
1349                default: {
1350                    break;
1351                }
1352            }
1353            if (length < 0) {
1354                throw new IOException("Invalid length");
1355            }
1356            if (dataInputStream.skipBytes(length) != length) {
1357                throw new IOException("Invalid JPEG segment");
1358            }
1359            bytesRead += length;
1360        }
1361    }
1362
1363    // Stores a new JPEG image with EXIF attributes into a given output stream.
1364    private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream)
1365            throws IOException {
1366        // See JPEG File Interchange Format Specification page 5.
1367        if (DEBUG) {
1368            Log.d(TAG, "saveJpegAttributes starting with (inputStream: " + inputStream
1369                    + ", outputStream: " + outputStream + ")");
1370        }
1371        DataInputStream dataInputStream = new DataInputStream(inputStream);
1372        ExifDataOutputStream dataOutputStream = new ExifDataOutputStream(outputStream);
1373        if (dataInputStream.readByte() != MARKER) {
1374            throw new IOException("Invalid marker");
1375        }
1376        dataOutputStream.writeByte(MARKER);
1377        if (dataInputStream.readByte() != MARKER_SOI) {
1378            throw new IOException("Invalid marker");
1379        }
1380        dataOutputStream.writeByte(MARKER_SOI);
1381
1382        // Write EXIF APP1 segment
1383        dataOutputStream.writeByte(MARKER);
1384        dataOutputStream.writeByte(MARKER_APP1);
1385        writeExifSegment(dataOutputStream, 6);
1386
1387        byte[] bytes = new byte[4096];
1388
1389        while (true) {
1390            byte marker = dataInputStream.readByte();
1391            if (marker != MARKER) {
1392                throw new IOException("Invalid marker");
1393            }
1394            marker = dataInputStream.readByte();
1395            switch (marker) {
1396                case MARKER_APP1: {
1397                    int length = dataInputStream.readUnsignedShort() - 2;
1398                    if (length < 0) {
1399                        throw new IOException("Invalid length");
1400                    }
1401                    byte[] identifier = new byte[6];
1402                    if (length >= 6) {
1403                        if (dataInputStream.read(identifier) != 6) {
1404                            throw new IOException("Invalid exif");
1405                        }
1406                        if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
1407                            // Skip the original EXIF APP1 segment.
1408                            if (dataInputStream.skip(length - 6) != length - 6) {
1409                                throw new IOException("Invalid length");
1410                            }
1411                            break;
1412                        }
1413                    }
1414                    // Copy non-EXIF APP1 segment.
1415                    dataOutputStream.writeByte(MARKER);
1416                    dataOutputStream.writeByte(marker);
1417                    dataOutputStream.writeUnsignedShort(length + 2);
1418                    if (length >= 6) {
1419                        length -= 6;
1420                        dataOutputStream.write(identifier);
1421                    }
1422                    int read;
1423                    while (length > 0 && (read = dataInputStream.read(
1424                            bytes, 0, Math.min(length, bytes.length))) >= 0) {
1425                        dataOutputStream.write(bytes, 0, read);
1426                        length -= read;
1427                    }
1428                    break;
1429                }
1430                case MARKER_EOI:
1431                case MARKER_SOS: {
1432                    dataOutputStream.writeByte(MARKER);
1433                    dataOutputStream.writeByte(marker);
1434                    // Copy all the remaining data
1435                    Streams.copy(dataInputStream, dataOutputStream);
1436                    return;
1437                }
1438                default: {
1439                    // Copy JPEG segment
1440                    dataOutputStream.writeByte(MARKER);
1441                    dataOutputStream.writeByte(marker);
1442                    int length = dataInputStream.readUnsignedShort();
1443                    dataOutputStream.writeUnsignedShort(length);
1444                    length -= 2;
1445                    if (length < 0) {
1446                        throw new IOException("Invalid length");
1447                    }
1448                    int read;
1449                    while (length > 0 && (read = dataInputStream.read(
1450                            bytes, 0, Math.min(length, bytes.length))) >= 0) {
1451                        dataOutputStream.write(bytes, 0, read);
1452                        length -= read;
1453                    }
1454                    break;
1455                }
1456            }
1457        }
1458    }
1459
1460    // Reads the given EXIF byte area and save its tag data into attributes.
1461    private void readExifSegment(byte[] exifBytes, int exifOffsetFromBeginning) throws IOException {
1462        // Parse TIFF Headers. See JEITA CP-3451C Table 1. page 10.
1463        ByteOrderAwarenessDataInputStream dataInputStream =
1464                new ByteOrderAwarenessDataInputStream(exifBytes);
1465
1466        // Read byte align
1467        short byteOrder = dataInputStream.readShort();
1468        switch (byteOrder) {
1469            case BYTE_ALIGN_II:
1470                if (DEBUG) {
1471                    Log.d(TAG, "readExifSegment: Byte Align II");
1472                }
1473                dataInputStream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
1474                break;
1475            case BYTE_ALIGN_MM:
1476                if (DEBUG) {
1477                    Log.d(TAG, "readExifSegment: Byte Align MM");
1478                }
1479                dataInputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
1480                break;
1481            default:
1482                throw new IOException("Invalid byte order: " + Integer.toHexString(byteOrder));
1483        }
1484
1485        int startCode = dataInputStream.readUnsignedShort();
1486        if (startCode != 0x2a) {
1487            throw new IOException("Invalid exif start: " + Integer.toHexString(startCode));
1488        }
1489
1490        // Read first ifd offset
1491        long firstIfdOffset = dataInputStream.readUnsignedInt();
1492        if (firstIfdOffset < 8 || firstIfdOffset >= exifBytes.length) {
1493            throw new IOException("Invalid first Ifd offset: " + firstIfdOffset);
1494        }
1495        firstIfdOffset -= 8;
1496        if (firstIfdOffset > 0) {
1497            if (dataInputStream.skip(firstIfdOffset) != firstIfdOffset) {
1498                throw new IOException("Couldn't jump to first Ifd: " + firstIfdOffset);
1499            }
1500        }
1501
1502        // Read primary image TIFF image file directory.
1503        readImageFileDirectory(dataInputStream, IFD_TIFF_HINT);
1504
1505        // Process thumbnail.
1506        String jpegInterchangeFormatString = getAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
1507        String jpegInterchangeFormatLengthString =
1508                getAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
1509        if (jpegInterchangeFormatString != null && jpegInterchangeFormatLengthString != null) {
1510            try {
1511                int jpegInterchangeFormat = Integer.parseInt(jpegInterchangeFormatString);
1512                int jpegInterchangeFormatLength = Integer
1513                        .parseInt(jpegInterchangeFormatLengthString);
1514                // The following code limits the size of thumbnail size not to overflow EXIF data area.
1515                jpegInterchangeFormatLength = Math.min(jpegInterchangeFormat
1516                        + jpegInterchangeFormatLength, exifOffsetFromBeginning + exifBytes.length)
1517                        - jpegInterchangeFormat;
1518                if (jpegInterchangeFormat > 0 && jpegInterchangeFormatLength > 0) {
1519                    mHasThumbnail = true;
1520                    mThumbnailOffset = exifOffsetFromBeginning + jpegInterchangeFormat;
1521                    mThumbnailLength = jpegInterchangeFormatLength;
1522
1523                    if (mFilename == null && mAssetInputStream == null
1524                            && mSeekableFileDescriptor == null) {
1525                        // Save the thumbnail in memory if the input doesn't support reading again.
1526                        byte[] thumbnailBytes = new byte[jpegInterchangeFormatLength];
1527                        dataInputStream.seek(jpegInterchangeFormat);
1528                        dataInputStream.readFully(thumbnailBytes);
1529                        mThumbnailBytes = thumbnailBytes;
1530
1531                        if (DEBUG) {
1532                            Bitmap bitmap = BitmapFactory.decodeByteArray(
1533                                    thumbnailBytes, 0, thumbnailBytes.length);
1534                            Log.d(TAG, "Thumbnail offset: " + mThumbnailOffset + ", length: "
1535                                    + mThumbnailLength + ", width: " + bitmap.getWidth()
1536                                    + ", height: "
1537                                    + bitmap.getHeight());
1538                        }
1539                    }
1540                }
1541            } catch (NumberFormatException e) {
1542                // Ignored the corrupted image.
1543            }
1544        }
1545
1546        // For compatibility, keep data formats as follows.
1547        convertToInt(TAG_IMAGE_WIDTH);
1548        convertToInt(TAG_IMAGE_LENGTH);
1549        convertToInt(TAG_ORIENTATION);
1550        convertToInt(TAG_FLASH);
1551        convertToRational(TAG_FOCAL_LENGTH);
1552        convertToDouble(TAG_DIGITAL_ZOOM_RATIO);
1553        convertToDouble(TAG_EXPOSURE_TIME);
1554        convertToDouble(TAG_F_NUMBER);
1555        convertToDouble(TAG_SUBJECT_DISTANCE);
1556        convertToInt(TAG_ISO_SPEED_RATINGS);
1557        convertToDouble(TAG_EXPOSURE_BIAS_VALUE);
1558        convertToInt(TAG_WHITE_BALANCE);
1559        convertToInt(TAG_LIGHT_SOURCE);
1560        convertToInt(TAG_METERING_MODE);
1561        convertToInt(TAG_EXPOSURE_PROGRAM);
1562        convertToInt(TAG_EXPOSURE_MODE);
1563        convertToRational(TAG_GPS_ALTITUDE);
1564        convertToInt(TAG_GPS_ALTITUDE_REF);
1565        convertToRational(TAG_GPS_LONGITUDE);
1566        convertToRational(TAG_GPS_LATITUDE);
1567        convertToTimestamp(TAG_GPS_TIMESTAMP);
1568
1569        // The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag.
1570        String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL);
1571        if (valueOfDateTimeOriginal != null) {
1572            mAttributes[IFD_TIFF_HINT].put(TAG_DATETIME, valueOfDateTimeOriginal);
1573        }
1574
1575        // Add the default value.
1576        if (getAttribute(TAG_IMAGE_WIDTH) == null) {
1577            mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, "0");
1578        }
1579        if (getAttribute(TAG_IMAGE_LENGTH) == null) {
1580            mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, "0");
1581        }
1582        if (getAttribute(TAG_ORIENTATION) == null) {
1583            mAttributes[IFD_TIFF_HINT].put(TAG_ORIENTATION, "0");
1584        }
1585        if (getAttribute(TAG_LIGHT_SOURCE) == null) {
1586            mAttributes[IFD_EXIF_HINT].put(TAG_LIGHT_SOURCE, "0");
1587        }
1588    }
1589
1590    // Converts the tag value to timestamp; Otherwise deletes the given tag.
1591    private void convertToTimestamp(String tagName) {
1592        String entryValue = getAttribute(tagName);
1593        if (entryValue == null) return;
1594        int dataFormat = getDataFormatOfExifEntryValue(entryValue);
1595        String[] components = entryValue.split(",");
1596        if (dataFormat == IFD_FORMAT_SRATIONAL && components.length == 3) {
1597            StringBuilder stringBuilder = new StringBuilder();
1598            for (String component : components) {
1599                if (stringBuilder.length() > 0) {
1600                    stringBuilder.append(":");
1601                }
1602                String[] rationalNumber = component.split("/");
1603                int numerator = Integer.parseInt(rationalNumber[0]);
1604                int denominator = Integer.parseInt(rationalNumber[1]);
1605                if (denominator == 0) {
1606                    numerator = 0;
1607                    denominator = 1;
1608                }
1609                int value = numerator / denominator;
1610                stringBuilder.append(String.format("%02d", value));
1611            }
1612            updateAttribute(tagName, stringBuilder.toString());
1613        } else if (dataFormat != IFD_FORMAT_STRING) {
1614            removeAttribute(tagName);
1615        }
1616    }
1617
1618    // Checks the tag value of a given tag formatted in double type; Otherwise try to convert it to
1619    // double type or delete it.
1620    private void convertToDouble(String tagName) {
1621        String entryValue = getAttribute(tagName);
1622        if (entryValue == null) return;
1623        int dataFormat = getDataFormatOfExifEntryValue(entryValue);
1624        switch (dataFormat) {
1625            case IFD_FORMAT_SRATIONAL: {
1626                StringBuilder stringBuilder = new StringBuilder();
1627                String[] components = entryValue.split(",");
1628                for (String component : components) {
1629                    if (stringBuilder.length() > 0) {
1630                        stringBuilder.append(",");
1631                    }
1632                    String[] rationalNumber = component.split("/");
1633                    int numerator = Integer.parseInt(rationalNumber[0]);
1634                    int denominator = Integer.parseInt(rationalNumber[1]);
1635                    if (denominator == 0) {
1636                        numerator = 0;
1637                        denominator = 1;
1638                    }
1639                    stringBuilder.append((double) numerator / denominator);
1640                }
1641                updateAttribute(tagName, stringBuilder.toString());
1642                break;
1643            }
1644            case IFD_FORMAT_DOUBLE:
1645                // Keep it as is.
1646                break;
1647            default:
1648                removeAttribute(tagName);
1649                break;
1650        }
1651    }
1652
1653    // Checks the tag value of a given tag formatted in int type; Otherwise deletes the tag value.
1654    private void convertToRational(String tagName) {
1655        String entryValue = getAttribute(tagName);
1656        if (entryValue == null) return;
1657        int dataFormat = getDataFormatOfExifEntryValue(entryValue);
1658        switch (dataFormat) {
1659            case IFD_FORMAT_SLONG:
1660            case IFD_FORMAT_DOUBLE: {
1661                StringBuilder stringBuilder = new StringBuilder();
1662                String[] components = entryValue.split(",");
1663                for (String component : components) {
1664                    if (stringBuilder.length() > 0) {
1665                        stringBuilder.append(",");
1666                    }
1667                    double doubleValue = Double.parseDouble(component);
1668                    stringBuilder.append((int) (doubleValue * 10000.0)).append("/").append(10000);
1669                }
1670                updateAttribute(tagName, stringBuilder.toString());
1671                break;
1672            }
1673            case IFD_FORMAT_SRATIONAL:
1674                // Keep it as is.
1675                break;
1676            default:
1677                removeAttribute(tagName);
1678                break;
1679        }
1680    }
1681
1682    // Checks the tag value of a given tag formatted in int type; Otherwise deletes the tag value.
1683    private void convertToInt(String tagName) {
1684        String entryValue = getAttribute(tagName);
1685        if (entryValue == null) return;
1686        int dataFormat = getDataFormatOfExifEntryValue(entryValue);
1687        if (dataFormat != IFD_FORMAT_SLONG) {
1688            removeAttribute(tagName);
1689        }
1690    }
1691
1692    // Reads image file directory, which is a tag group in EXIF.
1693    private void readImageFileDirectory(ByteOrderAwarenessDataInputStream dataInputStream, int hint)
1694            throws IOException {
1695        if (dataInputStream.peek() + 2 > dataInputStream.mLength) {
1696            // Return if there is no data from the offset.
1697            return;
1698        }
1699        // See JEITA CP-3451 Figure 5. page 9.
1700        short numberOfDirectoryEntry = dataInputStream.readShort();
1701        if (dataInputStream.peek() + 12 * numberOfDirectoryEntry > dataInputStream.mLength) {
1702            // Return if the size of entries is too big.
1703            return;
1704        }
1705
1706        if (DEBUG) {
1707            Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
1708        }
1709
1710        for (short i = 0; i < numberOfDirectoryEntry; ++i) {
1711            int tagNumber = dataInputStream.readUnsignedShort();
1712            int dataFormat = dataInputStream.readUnsignedShort();
1713            int numberOfComponents = dataInputStream.readInt();
1714            long nextEntryOffset = dataInputStream.peek() + 4;  // next four bytes is for data
1715                                                                // offset or value.
1716            // Look up a corresponding tag from tag number
1717            String tagName = (String) sExifTagMapsForReading[hint].get(tagNumber);
1718
1719            if (DEBUG) {
1720                Log.d(TAG, String.format("hint: %d, tagNumber: %d, tagName: %s, dataFormat: %d, " +
1721                        "numberOfComponents: %d", hint, tagNumber, tagName, dataFormat,
1722                        numberOfComponents));
1723            }
1724
1725            if (tagName == null || dataFormat <= 0 ||
1726                    dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) {
1727                // Skip if the parsed tag number is not defined or invalid data format.
1728                if (tagName == null) {
1729                    Log.w(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber);
1730                } else {
1731                    Log.w(TAG, "Skip the tag entry since data format is invalid: " + dataFormat);
1732                }
1733                dataInputStream.seek(nextEntryOffset);
1734                continue;
1735            }
1736
1737            // Read a value from data field or seek to the value offset which is stored in data
1738            // field if the size of the entry value is bigger than 4.
1739            int byteCount = numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat];
1740            if (byteCount > 4) {
1741                long offset = dataInputStream.readUnsignedInt();
1742                if (DEBUG) {
1743                    Log.d(TAG, "seek to data offset: " + offset);
1744                }
1745                if (offset + byteCount <= dataInputStream.mLength) {
1746                    dataInputStream.seek(offset);
1747                } else {
1748                     // Skip if invalid data offset.
1749                    Log.w(TAG, "Skip the tag entry since data offset is invalid: " + offset);
1750                    dataInputStream.seek(nextEntryOffset);
1751                    continue;
1752                }
1753            }
1754
1755            // Recursively parse IFD when a IFD pointer tag appears.
1756            int innerIfdHint = getIfdHintFromTagNumber(tagNumber);
1757            if (DEBUG) {
1758                Log.d(TAG, "innerIfdHint: " + innerIfdHint + " byteCount: " + byteCount);
1759            }
1760            if (innerIfdHint >= 0) {
1761                long offset = -1L;
1762                // Get offset from data field
1763                switch (dataFormat) {
1764                    case IFD_FORMAT_USHORT: {
1765                        offset = dataInputStream.readUnsignedShort();
1766                        break;
1767                    }
1768                    case IFD_FORMAT_SSHORT: {
1769                        offset = dataInputStream.readShort();
1770                        break;
1771                    }
1772                    case IFD_FORMAT_ULONG: {
1773                        offset = dataInputStream.readUnsignedInt();
1774                        break;
1775                    }
1776                    case IFD_FORMAT_SLONG: {
1777                        offset = dataInputStream.readInt();
1778                        break;
1779                    }
1780                    default: {
1781                        // Nothing to do
1782                        break;
1783                    }
1784                }
1785                if (DEBUG) {
1786                    Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tagName));
1787                }
1788                if (offset > 0L && offset < dataInputStream.mLength) {
1789                    dataInputStream.seek(offset);
1790                    readImageFileDirectory(dataInputStream, innerIfdHint);
1791                } else {
1792                    Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
1793                }
1794
1795                dataInputStream.seek(nextEntryOffset);
1796                continue;
1797            }
1798
1799            if (numberOfComponents == 1 || dataFormat == IFD_FORMAT_STRING
1800                    || dataFormat == IFD_FORMAT_UNDEFINED) {
1801                String entryValue = readExifEntryValue(
1802                        dataInputStream, dataFormat, numberOfComponents);
1803                if (entryValue != null) {
1804                    mAttributes[hint].put(tagName, entryValue);
1805                }
1806            } else {
1807                StringBuilder entryValueBuilder = new StringBuilder();
1808                for (int c = 0; c < numberOfComponents; ++c) {
1809                    if (entryValueBuilder.length() > 0) {
1810                        entryValueBuilder.append(",");
1811                    }
1812                    entryValueBuilder.append(readExifEntryValue(
1813                            dataInputStream, dataFormat, numberOfComponents));
1814                }
1815                mAttributes[hint].put(tagName, entryValueBuilder.toString());
1816            }
1817
1818            if (dataInputStream.peek() != nextEntryOffset) {
1819                dataInputStream.seek(nextEntryOffset);
1820            }
1821        }
1822
1823        if (dataInputStream.peek() + 4 <= dataInputStream.mLength) {
1824            long nextIfdOffset = dataInputStream.readUnsignedInt();
1825            if (DEBUG) {
1826                Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset));
1827            }
1828            // The next IFD offset needs to be bigger than 8
1829            // since the first IFD offset is at least 8.
1830            if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) {
1831                dataInputStream.seek(nextIfdOffset);
1832                readImageFileDirectory(dataInputStream, IFD_THUMBNAIL_HINT);
1833            }
1834        }
1835    }
1836
1837    // Reads a value from where the entry value are stored.
1838    private String readExifEntryValue(ByteOrderAwarenessDataInputStream dataInputStream,
1839            int dataFormat, int numberOfComponents) throws IOException {
1840        // See TIFF 6.0 spec Types. page 15.
1841        switch (dataFormat) {
1842            case IFD_FORMAT_BYTE: {
1843                return String.valueOf(dataInputStream.readByte());
1844            }
1845            case IFD_FORMAT_SBYTE: {
1846                return String.valueOf(dataInputStream.readByte() & 0xff);
1847            }
1848            case IFD_FORMAT_USHORT: {
1849                return String.valueOf(dataInputStream.readUnsignedShort());
1850            }
1851            case IFD_FORMAT_SSHORT: {
1852                return String.valueOf(dataInputStream.readUnsignedInt());
1853            }
1854            case IFD_FORMAT_ULONG: {
1855                return String.valueOf(dataInputStream.readInt());
1856            }
1857            case IFD_FORMAT_SLONG: {
1858                return String.valueOf(dataInputStream.readInt());
1859            }
1860            case IFD_FORMAT_URATIONAL:
1861            case IFD_FORMAT_SRATIONAL: {
1862                int numerator = dataInputStream.readInt();
1863                int denominator = dataInputStream.readInt();
1864                return numerator + "/" + denominator;
1865            }
1866            case IFD_FORMAT_SINGLE: {
1867                return String.valueOf(dataInputStream.readFloat());
1868            }
1869            case IFD_FORMAT_DOUBLE: {
1870                return String.valueOf(dataInputStream.readDouble());
1871            }
1872            case IFD_FORMAT_UNDEFINED:  // Usually UNDEFINED format is ASCII.
1873            case IFD_FORMAT_STRING: {
1874                byte[] bytes = new byte[numberOfComponents];
1875                dataInputStream.readFully(bytes);
1876                int index = 0;
1877                if (numberOfComponents >= EXIF_ASCII_PREFIX.length) {
1878                    boolean same = true;
1879                    for (int i = 0; i < EXIF_ASCII_PREFIX.length; ++i) {
1880                        if (bytes[i] != EXIF_ASCII_PREFIX[i]) {
1881                            same = false;
1882                            break;
1883                        }
1884                    }
1885                    if (same) {
1886                        index = EXIF_ASCII_PREFIX.length;
1887                    }
1888                }
1889
1890                StringBuilder stringBuilder = new StringBuilder();
1891                while (index < numberOfComponents) {
1892                    int ch = bytes[index];
1893                    if (ch == 0) {
1894                        break;
1895                    }
1896                    if (ch >= 32) {
1897                        stringBuilder.append((char) ch);
1898                    }
1899                    else {
1900                        stringBuilder.append('?');
1901                    }
1902                    ++index;
1903                }
1904                return stringBuilder.toString();
1905            }
1906            default: {
1907                // Nothing to do
1908                return null;
1909            }
1910        }
1911    }
1912
1913    // Gets the corresponding IFD group index of the given tag number for writing Exif Tags.
1914    private static int getIfdHintFromTagNumber(int tagNumber) {
1915        for (int i = 0; i < IFD_POINTER_TAG_HINTS.length; ++i) {
1916            if (IFD_POINTER_TAGS[i].number == tagNumber) {
1917                return IFD_POINTER_TAG_HINTS[i];
1918            }
1919        }
1920        return -1;
1921    }
1922
1923    // Writes an Exif segment into the given output stream.
1924    private int writeExifSegment(ExifDataOutputStream dataOutputStream, int exifOffsetFromBeginning)
1925            throws IOException {
1926        // The following variables are for calculating each IFD tag group size in bytes.
1927        int[] ifdOffsets = new int[EXIF_TAGS.length];
1928        int[] ifdDataSizes = new int[EXIF_TAGS.length];
1929
1930        // Remove IFD pointer tags (we'll re-add it later.)
1931        for (ExifTag tag : IFD_POINTER_TAGS) {
1932            removeAttribute(tag.name);
1933        }
1934        // Remove old thumbnail data
1935        removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
1936        removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
1937
1938        // Remove null value tags.
1939        for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
1940            for (Object obj : mAttributes[hint].entrySet().toArray()) {
1941                Map.Entry entry = (Map.Entry) obj;
1942                if (entry.getValue() == null) {
1943                    mAttributes[hint].remove(entry.getKey());
1944                }
1945            }
1946        }
1947
1948        // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD
1949        // offset when there is one or more tags in the thumbnail IFD.
1950        if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) {
1951            mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].name, "0");
1952        }
1953        if (!mAttributes[IFD_EXIF_HINT].isEmpty()) {
1954            mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].name, "0");
1955        }
1956        if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
1957            mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name, "0");
1958        }
1959        if (mHasThumbnail) {
1960            mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name, "0");
1961            mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
1962                    String.valueOf(mThumbnailLength));
1963        }
1964
1965        // Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
1966        // value which has a bigger size than 4 bytes.
1967        for (int i = 0; i < 5; ++i) {
1968            int sum = 0;
1969            for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
1970                String entryValue = (String) ((Map.Entry) entry).getValue();
1971                int dataFormat = getDataFormatOfExifEntryValue(entryValue);
1972                int size = getSizeOfExifEntryValue(dataFormat, entryValue);
1973                if (size > 4) {
1974                    sum += size;
1975                }
1976            }
1977            ifdDataSizes[i] += sum;
1978        }
1979
1980        // Calculate IFD offsets.
1981        int position = 8;
1982        for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
1983            if (!mAttributes[hint].isEmpty()) {
1984                ifdOffsets[hint] = position;
1985                position += 2 + mAttributes[hint].size() * 12 + 4 + ifdDataSizes[hint];
1986            }
1987        }
1988        if (mHasThumbnail) {
1989            int thumbnailOffset = position;
1990            mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
1991                    String.valueOf(thumbnailOffset));
1992            mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
1993                    String.valueOf(mThumbnailLength));
1994            mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset;
1995            position += mThumbnailLength;
1996        }
1997
1998        // Calculate the total size
1999        int totalSize = position + 8;  // eight bytes is for header part.
2000        if (DEBUG) {
2001            Log.d(TAG, "totalSize length: " + totalSize);
2002            for (int i = 0; i < 5; ++i) {
2003                Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d",
2004                        i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i]));
2005            }
2006        }
2007
2008        // Update IFD pointer tags with the calculated offsets.
2009        if (!mAttributes[IFD_EXIF_HINT].isEmpty()) {
2010            mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].name,
2011                    String.valueOf(ifdOffsets[IFD_EXIF_HINT]));
2012        }
2013        if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
2014            mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name,
2015                    String.valueOf(ifdOffsets[IFD_GPS_HINT]));
2016        }
2017        if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) {
2018            mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].name,
2019                    String.valueOf(ifdOffsets[IFD_INTEROPERABILITY_HINT]));
2020        }
2021
2022        // Write TIFF Headers. See JEITA CP-3451C Table 1. page 10.
2023        dataOutputStream.writeUnsignedShort(totalSize);
2024        dataOutputStream.write(IDENTIFIER_EXIF_APP1);
2025        dataOutputStream.writeShort(BYTE_ALIGN_MM);
2026        dataOutputStream.writeUnsignedShort(0x2a);
2027        dataOutputStream.writeUnsignedInt(8);
2028
2029        // Write IFD groups. See JEITA CP-3451C Figure 7. page 12.
2030        for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
2031            if (!mAttributes[hint].isEmpty()) {
2032                // See JEITA CP-3451C 4.6.2 IFD structure. page 13.
2033                // Write entry count
2034                dataOutputStream.writeUnsignedShort(mAttributes[hint].size());
2035
2036                // Write entry info
2037                int dataOffset = ifdOffsets[hint] + 2 + mAttributes[hint].size() * 12 + 4;
2038                for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) {
2039                    // Convert tag name to tag number.
2040                    int tagNumber = (int) sExifTagMapsForWriting[hint].get(entry.getKey());
2041                    String entryValue = (String) entry.getValue();
2042
2043                    int dataFormat = getDataFormatOfExifEntryValue(entryValue);
2044                    int numberOfComponents = getNumberOfComponentsInExifEntryValue(dataFormat,
2045                            entryValue);
2046                    int byteCount = getSizeOfExifEntryValue(dataFormat, entryValue);
2047
2048                    dataOutputStream.writeUnsignedShort(tagNumber);
2049                    dataOutputStream.writeUnsignedShort(dataFormat);
2050                    dataOutputStream.writeInt(numberOfComponents);
2051                    if (byteCount > 4) {
2052                        dataOutputStream.writeUnsignedInt(dataOffset);
2053                        dataOffset += byteCount;
2054                    } else {
2055                        int bytesWritten = writeExifEntryValue(dataOutputStream, entryValue);
2056                        // Fill zero up to 4 bytes
2057                        if (bytesWritten < 4) {
2058                            for (int i = bytesWritten; i < 4; ++i) {
2059                                dataOutputStream.write(0);
2060                            }
2061                        }
2062                    }
2063                }
2064
2065                // Write the next offset. It writes the offset of thumbnail IFD if there is one or
2066                // more tags in the thumbnail IFD when the current IFD is the primary image TIFF
2067                // IFD; Otherwise 0.
2068                if (hint == 0 && !mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) {
2069                    dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_THUMBNAIL_HINT]);
2070                } else {
2071                    dataOutputStream.writeUnsignedInt(0);
2072                }
2073
2074                // Write values of data field exceeding 4 bytes after the next offset.
2075                for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) {
2076                    String entryValue = (String) entry.getValue();
2077
2078                    int dataFormat = getDataFormatOfExifEntryValue(entryValue);
2079                    int byteCount = getSizeOfExifEntryValue(dataFormat, entryValue);
2080                    if (byteCount > 4) {
2081                        writeExifEntryValue(dataOutputStream, entryValue);
2082                    }
2083                }
2084            }
2085        }
2086
2087        // Write thumbnail
2088        if (mHasThumbnail) {
2089            dataOutputStream.write(getThumbnail());
2090        }
2091
2092        return totalSize;
2093    }
2094
2095    // Writes EXIF entry value and its entry value type will be automatically determined.
2096    private static int writeExifEntryValue(ExifDataOutputStream dataOutputStream, String entryValue)
2097            throws IOException {
2098        int bytesWritten = 0;
2099        int dataFormat = getDataFormatOfExifEntryValue(entryValue);
2100
2101        if (dataFormat == IFD_FORMAT_STRING) {
2102            byte[] asciiArray = (entryValue + '\0').getBytes(Charset.forName("US-ASCII"));
2103            dataOutputStream.write(asciiArray);
2104            return asciiArray.length;
2105        }
2106
2107        // Values can be composed of several components. Each component is separated by char ','.
2108        String[] components = entryValue.split(",");
2109        for (String component : components) {
2110            switch (dataFormat) {
2111                case IFD_FORMAT_SLONG:
2112                    dataOutputStream.writeInt(Integer.parseInt(component));
2113                    bytesWritten += 4;
2114                    break;
2115                case IFD_FORMAT_DOUBLE:
2116                    dataOutputStream.writeDouble(Double.parseDouble(component));
2117                    bytesWritten += 8;
2118                    break;
2119                case IFD_FORMAT_SRATIONAL:
2120                    String[] rationalNumber = component.split("/");
2121                    dataOutputStream.writeInt(Integer.parseInt(rationalNumber[0]));
2122                    dataOutputStream.writeInt(Integer.parseInt(rationalNumber[1]));
2123                    bytesWritten += 8;
2124                    break;
2125                default:
2126                    throw new IllegalArgumentException();
2127            }
2128        }
2129        return bytesWritten;
2130    }
2131
2132    // Determines the data format of EXIF entry value.
2133    private static int getDataFormatOfExifEntryValue(String entryValue) {
2134        // See TIFF 6.0 spec Types. page 15.
2135        // Take the first component if there are more than one component.
2136        if (entryValue.contains(",")) {
2137            String[] entryValues = entryValue.split(",");
2138            int dataFormat = getDataFormatOfExifEntryValue(entryValues[0]);
2139            if (dataFormat == IFD_FORMAT_STRING) {
2140                return IFD_FORMAT_STRING;
2141            }
2142            for (int i = 1; i < entryValues.length; ++i) {
2143                if (getDataFormatOfExifEntryValue(entryValues[i]) != dataFormat) {
2144                    return IFD_FORMAT_STRING;
2145                }
2146            }
2147            return dataFormat;
2148        }
2149
2150        if (entryValue.contains("/")) {
2151            String[] rationalNumber = entryValue.split("/");
2152            if (rationalNumber.length == 2) {
2153                try {
2154                    Integer.parseInt(rationalNumber[0]);
2155                    Integer.parseInt(rationalNumber[1]);
2156                    return IFD_FORMAT_SRATIONAL;
2157                } catch (NumberFormatException e)  {
2158                    // Ignored
2159                }
2160            }
2161            return IFD_FORMAT_STRING;
2162        }
2163        try {
2164            Integer.parseInt(entryValue);
2165            return IFD_FORMAT_SLONG;
2166        } catch (NumberFormatException e) {
2167            // Ignored
2168        }
2169        try {
2170            Double.parseDouble(entryValue);
2171            return IFD_FORMAT_DOUBLE;
2172        } catch (NumberFormatException e) {
2173            // Ignored
2174        }
2175        return IFD_FORMAT_STRING;
2176    }
2177
2178    // Determines the size of EXIF entry value.
2179    private static int getSizeOfExifEntryValue(int dataFormat, String entryValue) {
2180        // See TIFF 6.0 spec Types page 15.
2181        if (dataFormat == IFD_FORMAT_STRING) {
2182            return (entryValue + '\0').getBytes(Charset.forName("US-ASCII")).length;
2183        }
2184        int bytesEstimated = 0;
2185        String[] components = entryValue.split(",");
2186        for (String component : components) {
2187            switch (dataFormat) {
2188                case IFD_FORMAT_SLONG:
2189                    bytesEstimated += 4;
2190                    break;
2191                case IFD_FORMAT_DOUBLE:
2192                    bytesEstimated += 8;
2193                    break;
2194                case IFD_FORMAT_SRATIONAL:
2195                    bytesEstimated += 8;
2196                    break;
2197                default:
2198                    throw new IllegalArgumentException();
2199            }
2200        }
2201        return bytesEstimated;
2202    }
2203
2204    // Determines the number of components of EXIF entry value.
2205    private static int getNumberOfComponentsInExifEntryValue(int dataFormat, String entryValue) {
2206        if (dataFormat == IFD_FORMAT_STRING) {
2207            return (entryValue + '\0').getBytes(Charset.forName("US-ASCII")).length;
2208        }
2209        int count = 1;
2210        for (int i = 0; i < entryValue.length(); ++i) {
2211            if (entryValue.charAt(i) == ',') {
2212                ++count;
2213            }
2214        }
2215        return count;
2216    }
2217
2218    // An input stream to parse EXIF data area, which can be written in either little or big endian
2219    // order.
2220    private static class ByteOrderAwarenessDataInputStream extends ByteArrayInputStream {
2221        private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN;
2222        private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN;
2223
2224        private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN;
2225        private final long mLength;
2226        private long mPosition;
2227
2228        public ByteOrderAwarenessDataInputStream(byte[] bytes) {
2229            super(bytes);
2230            mLength = bytes.length;
2231            mPosition = 0L;
2232        }
2233
2234        public void setByteOrder(ByteOrder byteOrder) {
2235            mByteOrder = byteOrder;
2236        }
2237
2238        public void seek(long byteCount) throws IOException {
2239            mPosition = 0L;
2240            reset();
2241            if (skip(byteCount) != byteCount) {
2242                throw new IOException("Couldn't seek up to the byteCount");
2243            }
2244        }
2245
2246        public long peek() {
2247            return mPosition;
2248        }
2249
2250        public void readFully(byte[] buffer) throws IOException {
2251            mPosition += buffer.length;
2252            if (mPosition > mLength) {
2253                throw new EOFException();
2254            }
2255            if (super.read(buffer, 0, buffer.length) != buffer.length) {
2256                throw new IOException("Couldn't read up to the length of buffer");
2257            }
2258        }
2259
2260        public byte readByte() throws IOException {
2261            ++mPosition;
2262            if (mPosition > mLength) {
2263                throw new EOFException();
2264            }
2265            int ch = super.read();
2266            if (ch < 0) {
2267                throw new EOFException();
2268            }
2269            return (byte) ch;
2270        }
2271
2272        public short readShort() throws IOException {
2273            mPosition += 2;
2274            if (mPosition > mLength) {
2275                throw new EOFException();
2276            }
2277            int ch1 = super.read();
2278            int ch2 = super.read();
2279            if ((ch1 | ch2) < 0) {
2280                throw new EOFException();
2281            }
2282            if (mByteOrder == LITTLE_ENDIAN) {
2283                return (short) ((ch2 << 8) + (ch1));
2284            } else if (mByteOrder == BIG_ENDIAN) {
2285                return (short) ((ch1 << 8) + (ch2));
2286            }
2287            throw new IOException("Invalid byte order: " + mByteOrder);
2288        }
2289
2290        public int readInt() throws IOException {
2291            mPosition += 4;
2292            if (mPosition > mLength) {
2293                throw new EOFException();
2294            }
2295            int ch1 = super.read();
2296            int ch2 = super.read();
2297            int ch3 = super.read();
2298            int ch4 = super.read();
2299            if ((ch1 | ch2 | ch3 | ch4) < 0) {
2300                throw new EOFException();
2301            }
2302            if (mByteOrder == LITTLE_ENDIAN) {
2303                return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1);
2304            } else if (mByteOrder == BIG_ENDIAN) {
2305                return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4);
2306            }
2307            throw new IOException("Invalid byte order: " + mByteOrder);
2308        }
2309
2310        @Override
2311        public long skip(long byteCount) {
2312            long skipped = super.skip(Math.min(byteCount, mLength - mPosition));
2313            mPosition += skipped;
2314            return skipped;
2315        }
2316
2317        public int readUnsignedShort() throws IOException {
2318            mPosition += 2;
2319            if (mPosition > mLength) {
2320                throw new EOFException();
2321            }
2322            int ch1 = super.read();
2323            int ch2 = super.read();
2324            if ((ch1 | ch2) < 0) {
2325                throw new EOFException();
2326            }
2327            if (mByteOrder == LITTLE_ENDIAN) {
2328                return ((ch2 << 8) + (ch1));
2329            } else if (mByteOrder == BIG_ENDIAN) {
2330                return ((ch1 << 8) + (ch2));
2331            }
2332            throw new IOException("Invalid byte order: " + mByteOrder);
2333        }
2334
2335        public long readUnsignedInt() throws IOException {
2336            return readInt() & 0xffffffffL;
2337        }
2338
2339        public long readLong() throws IOException {
2340            mPosition += 8;
2341            if (mPosition > mLength) {
2342                throw new EOFException();
2343            }
2344            int ch1 = super.read();
2345            int ch2 = super.read();
2346            int ch3 = super.read();
2347            int ch4 = super.read();
2348            int ch5 = super.read();
2349            int ch6 = super.read();
2350            int ch7 = super.read();
2351            int ch8 = super.read();
2352            if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) {
2353                throw new EOFException();
2354            }
2355            if (mByteOrder == LITTLE_ENDIAN) {
2356                return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40)
2357                        + ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16)
2358                        + ((long) ch2 << 8) + (long) ch1);
2359            } else if (mByteOrder == BIG_ENDIAN) {
2360                return (((long) ch1 << 56) + ((long) ch2 << 48) + ((long) ch3 << 40)
2361                        + ((long) ch4 << 32) + ((long) ch5 << 24) + ((long) ch6 << 16)
2362                        + ((long) ch7 << 8) + (long) ch8);
2363            }
2364            throw new IOException("Invalid byte order: " + mByteOrder);
2365        }
2366
2367        public float readFloat() throws IOException {
2368            return Float.intBitsToFloat(readInt());
2369        }
2370
2371        public double readDouble() throws IOException {
2372            return Double.longBitsToDouble(readLong());
2373        }
2374    }
2375
2376    // An output stream to write EXIF data area, that will be written in big endian byte order.
2377    private static class ExifDataOutputStream extends DataOutputStream {
2378        public ExifDataOutputStream(OutputStream out) {
2379            super(out);
2380        }
2381
2382        public void writeUnsignedShort(int val) throws IOException {
2383            writeShort((short) val);
2384        }
2385
2386        public void writeUnsignedInt(long val) throws IOException {
2387            writeInt((int) val);
2388        }
2389    }
2390
2391    // JNI methods for RAW formats.
2392    private static native void nativeInitRaw();
2393    private static native byte[] nativeGetThumbnailFromAsset(
2394            long asset, int thumbnailOffset, int thumbnailLength);
2395    private static native HashMap nativeGetRawAttributesFromAsset(long asset);
2396    private static native HashMap nativeGetRawAttributesFromFileDescriptor(FileDescriptor fd);
2397    private static native HashMap nativeGetRawAttributesFromInputStream(InputStream in);
2398}
2399