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