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