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 
17 package android.media;
18 
19 import android.annotation.NonNull;
20 import android.content.res.AssetManager;
21 import android.graphics.Bitmap;
22 import android.graphics.BitmapFactory;
23 import android.system.ErrnoException;
24 import android.system.Os;
25 import android.system.OsConstants;
26 import android.util.Log;
27 import android.util.Pair;
28 
29 import java.io.BufferedInputStream;
30 import java.io.ByteArrayInputStream;
31 import java.io.DataInputStream;
32 import java.io.EOFException;
33 import java.io.File;
34 import java.io.FileDescriptor;
35 import java.io.FileInputStream;
36 import java.io.FileNotFoundException;
37 import java.io.FileOutputStream;
38 import java.io.FilterOutputStream;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.io.OutputStream;
42 import java.nio.ByteBuffer;
43 import java.nio.ByteOrder;
44 import java.nio.charset.Charset;
45 import java.text.ParsePosition;
46 import java.text.SimpleDateFormat;
47 import java.util.Arrays;
48 import java.util.Date;
49 import java.util.HashMap;
50 import java.util.HashSet;
51 import java.util.Map;
52 import java.util.Set;
53 import java.util.TimeZone;
54 import java.util.regex.Matcher;
55 import java.util.regex.Pattern;
56 
57 import libcore.io.IoUtils;
58 import 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  */
67 public 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 
Rational(long numerator, long denominator)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
toString()430         public String toString() {
431             return numerator + "/" + denominator;
432         }
433 
calculate()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 
ExifAttribute(int format, int numberOfComponents, byte[] bytes)445         private ExifAttribute(int format, int numberOfComponents, byte[] bytes) {
446             this.format = format;
447             this.numberOfComponents = numberOfComponents;
448             this.bytes = bytes;
449         }
450 
createUShort(int[] values, ByteOrder byteOrder)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 
createUShort(int value, ByteOrder byteOrder)461         public static ExifAttribute createUShort(int value, ByteOrder byteOrder) {
462             return createUShort(new int[] {value}, byteOrder);
463         }
464 
createULong(long[] values, ByteOrder byteOrder)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 
createULong(long value, ByteOrder byteOrder)475         public static ExifAttribute createULong(long value, ByteOrder byteOrder) {
476             return createULong(new long[] {value}, byteOrder);
477         }
478 
createSLong(int[] values, ByteOrder byteOrder)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 
createSLong(int value, ByteOrder byteOrder)489         public static ExifAttribute createSLong(int value, ByteOrder byteOrder) {
490             return createSLong(new int[] {value}, byteOrder);
491         }
492 
createByte(String value)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 
createString(String value)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 
createURational(Rational[] values, ByteOrder byteOrder)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 
createURational(Rational value, ByteOrder byteOrder)519         public static ExifAttribute createURational(Rational value, ByteOrder byteOrder) {
520             return createURational(new Rational[] {value}, byteOrder);
521         }
522 
createSRational(Rational[] values, ByteOrder byteOrder)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 
createSRational(Rational value, ByteOrder byteOrder)534         public static ExifAttribute createSRational(Rational value, ByteOrder byteOrder) {
535             return createSRational(new Rational[] {value}, byteOrder);
536         }
537 
createDouble(double[] values, ByteOrder byteOrder)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 
createDouble(double value, ByteOrder byteOrder)548         public static ExifAttribute createDouble(double value, ByteOrder byteOrder) {
549             return createDouble(new double[] {value}, byteOrder);
550         }
551 
552         @Override
toString()553         public String toString() {
554             return "(" + IFD_FORMAT_NAMES[format] + ", data length:" + bytes.length + ")";
555         }
556 
getValue(ByteOrder byteOrder)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 
getDoubleValue(ByteOrder byteOrder)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 (double) 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 (double) 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 
getIntValue(ByteOrder byteOrder)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 
getStringValue(ByteOrder byteOrder)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 
size()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 
ExifTag(String name, int number, int format)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 
ExifTag(String name, int number, int primaryFormat, int secondaryFormat)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");
nativeInitRaw()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]) {
put(tag.number, tag)1068                 sExifTagMapsForReading[hint].put(tag.number, tag);
put(tag.name, 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 
1087     // Pattern to check non zero timestamp
1088     private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
1089     // Pattern to check gps timestamp
1090     private static final Pattern sGpsTimestampPattern =
1091             Pattern.compile("^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$");
1092 
1093     /**
1094      * Reads Exif tags from the specified image file.
1095      */
ExifInterface(String filename)1096     public ExifInterface(String filename) throws IOException {
1097         if (filename == null) {
1098             throw new IllegalArgumentException("filename cannot be null");
1099         }
1100         FileInputStream in = null;
1101         mAssetInputStream = null;
1102         mFilename = filename;
1103         mIsInputStream = false;
1104         try {
1105             in = new FileInputStream(filename);
1106             if (isSeekableFD(in.getFD())) {
1107                 mSeekableFileDescriptor = in.getFD();
1108             } else {
1109                 mSeekableFileDescriptor = null;
1110             }
1111             loadAttributes(in);
1112         } finally {
1113             IoUtils.closeQuietly(in);
1114         }
1115     }
1116 
1117     /**
1118      * Reads Exif tags from the specified image file descriptor. Attribute mutation is supported
1119      * for writable and seekable file descriptors only. This constructor will not rewind the offset
1120      * of the given file descriptor. Developers should close the file descriptor after use.
1121      */
ExifInterface(FileDescriptor fileDescriptor)1122     public ExifInterface(FileDescriptor fileDescriptor) throws IOException {
1123         if (fileDescriptor == null) {
1124             throw new IllegalArgumentException("fileDescriptor cannot be null");
1125         }
1126         mAssetInputStream = null;
1127         mFilename = null;
1128         if (isSeekableFD(fileDescriptor)) {
1129             mSeekableFileDescriptor = fileDescriptor;
1130             // Keep the original file descriptor in order to save attributes when it's seekable.
1131             // Otherwise, just close the given file descriptor after reading it because the save
1132             // feature won't be working.
1133             try {
1134                 fileDescriptor = Os.dup(fileDescriptor);
1135             } catch (ErrnoException e) {
1136                 throw e.rethrowAsIOException();
1137             }
1138         } else {
1139             mSeekableFileDescriptor = null;
1140         }
1141         mIsInputStream = false;
1142         FileInputStream in = null;
1143         try {
1144             in = new FileInputStream(fileDescriptor);
1145             loadAttributes(in);
1146         } finally {
1147             IoUtils.closeQuietly(in);
1148         }
1149     }
1150 
1151     /**
1152      * Reads Exif tags from the specified image input stream. Attribute mutation is not supported
1153      * for input streams. The given input stream will proceed its current position. Developers
1154      * should close the input stream after use.
1155      */
ExifInterface(InputStream inputStream)1156     public ExifInterface(InputStream inputStream) throws IOException {
1157         if (inputStream == null) {
1158             throw new IllegalArgumentException("inputStream cannot be null");
1159         }
1160         mFilename = null;
1161         if (inputStream instanceof AssetManager.AssetInputStream) {
1162             mAssetInputStream = (AssetManager.AssetInputStream) inputStream;
1163             mSeekableFileDescriptor = null;
1164         } else if (inputStream instanceof FileInputStream
1165                 && isSeekableFD(((FileInputStream) inputStream).getFD())) {
1166             mAssetInputStream = null;
1167             mSeekableFileDescriptor = ((FileInputStream) inputStream).getFD();
1168         } else {
1169             mAssetInputStream = null;
1170             mSeekableFileDescriptor = null;
1171         }
1172         mIsInputStream = true;
1173         loadAttributes(inputStream);
1174     }
1175 
1176     /**
1177      * Returns the EXIF attribute of the specified tag or {@code null} if there is no such tag in
1178      * the image file.
1179      *
1180      * @param tag the name of the tag.
1181      */
getExifAttribute(String tag)1182     private ExifAttribute getExifAttribute(String tag) {
1183         // Retrieves all tag groups. The value from primary image tag group has a higher priority
1184         // than the value from the thumbnail tag group if there are more than one candidates.
1185         for (int i = 0; i < EXIF_TAGS.length; ++i) {
1186             Object value = mAttributes[i].get(tag);
1187             if (value != null) {
1188                 return (ExifAttribute) value;
1189             }
1190         }
1191         return null;
1192     }
1193 
1194     /**
1195      * Returns the value of the specified tag or {@code null} if there
1196      * is no such tag in the image file.
1197      *
1198      * @param tag the name of the tag.
1199      */
getAttribute(String tag)1200     public String getAttribute(String tag) {
1201         ExifAttribute attribute = getExifAttribute(tag);
1202         if (attribute != null) {
1203             if (!sTagSetForCompatibility.contains(tag)) {
1204                 return attribute.getStringValue(mExifByteOrder);
1205             }
1206             if (tag.equals(TAG_GPS_TIMESTAMP)) {
1207                 // Convert the rational values to the custom formats for backwards compatibility.
1208                 if (attribute.format != IFD_FORMAT_URATIONAL
1209                         && attribute.format != IFD_FORMAT_SRATIONAL) {
1210                     return null;
1211                 }
1212                 Rational[] array = (Rational[]) attribute.getValue(mExifByteOrder);
1213                 if (array.length != 3) {
1214                     return null;
1215                 }
1216                 return String.format("%02d:%02d:%02d",
1217                         (int) ((float) array[0].numerator / array[0].denominator),
1218                         (int) ((float) array[1].numerator / array[1].denominator),
1219                         (int) ((float) array[2].numerator / array[2].denominator));
1220             }
1221             try {
1222                 return Double.toString(attribute.getDoubleValue(mExifByteOrder));
1223             } catch (NumberFormatException e) {
1224                 return null;
1225             }
1226         }
1227         return null;
1228     }
1229 
1230     /**
1231      * Returns the integer value of the specified tag. If there is no such tag
1232      * in the image file or the value cannot be parsed as integer, return
1233      * <var>defaultValue</var>.
1234      *
1235      * @param tag the name of the tag.
1236      * @param defaultValue the value to return if the tag is not available.
1237      */
getAttributeInt(String tag, int defaultValue)1238     public int getAttributeInt(String tag, int defaultValue) {
1239         ExifAttribute exifAttribute = getExifAttribute(tag);
1240         if (exifAttribute == null) {
1241             return defaultValue;
1242         }
1243 
1244         try {
1245             return exifAttribute.getIntValue(mExifByteOrder);
1246         } catch (NumberFormatException e) {
1247             return defaultValue;
1248         }
1249     }
1250 
1251     /**
1252      * Returns the double value of the tag that is specified as rational or contains a
1253      * double-formatted value. If there is no such tag in the image file or the value cannot be
1254      * parsed as double, return <var>defaultValue</var>.
1255      *
1256      * @param tag the name of the tag.
1257      * @param defaultValue the value to return if the tag is not available.
1258      */
getAttributeDouble(String tag, double defaultValue)1259     public double getAttributeDouble(String tag, double defaultValue) {
1260         ExifAttribute exifAttribute = getExifAttribute(tag);
1261         if (exifAttribute == null) {
1262             return defaultValue;
1263         }
1264 
1265         try {
1266             return exifAttribute.getDoubleValue(mExifByteOrder);
1267         } catch (NumberFormatException e) {
1268             return defaultValue;
1269         }
1270     }
1271 
1272     /**
1273      * Set the value of the specified tag.
1274      *
1275      * @param tag the name of the tag.
1276      * @param value the value of the tag.
1277      */
setAttribute(String tag, String value)1278     public void setAttribute(String tag, String value) {
1279         // Convert the given value to rational values for backwards compatibility.
1280         if (value != null && sTagSetForCompatibility.contains(tag)) {
1281             if (tag.equals(TAG_GPS_TIMESTAMP)) {
1282                 Matcher m = sGpsTimestampPattern.matcher(value);
1283                 if (!m.find()) {
1284                     Log.w(TAG, "Invalid value for " + tag + " : " + value);
1285                     return;
1286                 }
1287                 value = Integer.parseInt(m.group(1)) + "/1," + Integer.parseInt(m.group(2)) + "/1,"
1288                         + Integer.parseInt(m.group(3)) + "/1";
1289             } else {
1290                 try {
1291                     double doubleValue = Double.parseDouble(value);
1292                     value = (long) (doubleValue * 10000L) + "/10000";
1293                 } catch (NumberFormatException e) {
1294                     Log.w(TAG, "Invalid value for " + tag + " : " + value);
1295                     return;
1296                 }
1297             }
1298         }
1299 
1300         for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1301             if (i == IFD_THUMBNAIL_HINT && !mHasThumbnail) {
1302                 continue;
1303             }
1304             final Object obj = sExifTagMapsForWriting[i].get(tag);
1305             if (obj != null) {
1306                 if (value == null) {
1307                     mAttributes[i].remove(tag);
1308                     continue;
1309                 }
1310                 final ExifTag exifTag = (ExifTag) obj;
1311                 Pair<Integer, Integer> guess = guessDataFormat(value);
1312                 int dataFormat;
1313                 if (exifTag.primaryFormat == guess.first || exifTag.primaryFormat == guess.second) {
1314                     dataFormat = exifTag.primaryFormat;
1315                 } else if (exifTag.secondaryFormat != -1 && (exifTag.secondaryFormat == guess.first
1316                         || exifTag.secondaryFormat == guess.second)) {
1317                     dataFormat = exifTag.secondaryFormat;
1318                 } else if (exifTag.primaryFormat == IFD_FORMAT_BYTE
1319                         || exifTag.primaryFormat == IFD_FORMAT_UNDEFINED
1320                         || exifTag.primaryFormat == IFD_FORMAT_STRING) {
1321                     dataFormat = exifTag.primaryFormat;
1322                 } else {
1323                     Log.w(TAG, "Given tag (" + tag + ") value didn't match with one of expected "
1324                             + "formats: " + IFD_FORMAT_NAMES[exifTag.primaryFormat]
1325                             + (exifTag.secondaryFormat == -1 ? "" : ", "
1326                             + IFD_FORMAT_NAMES[exifTag.secondaryFormat]) + " (guess: "
1327                             + IFD_FORMAT_NAMES[guess.first] + (guess.second == -1 ? "" : ", "
1328                             + IFD_FORMAT_NAMES[guess.second]) + ")");
1329                     continue;
1330                 }
1331                 switch (dataFormat) {
1332                     case IFD_FORMAT_BYTE: {
1333                         mAttributes[i].put(tag, ExifAttribute.createByte(value));
1334                         break;
1335                     }
1336                     case IFD_FORMAT_UNDEFINED:
1337                     case IFD_FORMAT_STRING: {
1338                         mAttributes[i].put(tag, ExifAttribute.createString(value));
1339                         break;
1340                     }
1341                     case IFD_FORMAT_USHORT: {
1342                         final String[] values = value.split(",");
1343                         final int[] intArray = new int[values.length];
1344                         for (int j = 0; j < values.length; ++j) {
1345                             intArray[j] = Integer.parseInt(values[j]);
1346                         }
1347                         mAttributes[i].put(tag,
1348                                 ExifAttribute.createUShort(intArray, mExifByteOrder));
1349                         break;
1350                     }
1351                     case IFD_FORMAT_SLONG: {
1352                         final String[] values = value.split(",");
1353                         final int[] intArray = new int[values.length];
1354                         for (int j = 0; j < values.length; ++j) {
1355                             intArray[j] = Integer.parseInt(values[j]);
1356                         }
1357                         mAttributes[i].put(tag,
1358                                 ExifAttribute.createSLong(intArray, mExifByteOrder));
1359                         break;
1360                     }
1361                     case IFD_FORMAT_ULONG: {
1362                         final String[] values = value.split(",");
1363                         final long[] longArray = new long[values.length];
1364                         for (int j = 0; j < values.length; ++j) {
1365                             longArray[j] = Long.parseLong(values[j]);
1366                         }
1367                         mAttributes[i].put(tag,
1368                                 ExifAttribute.createULong(longArray, mExifByteOrder));
1369                         break;
1370                     }
1371                     case IFD_FORMAT_URATIONAL: {
1372                         final String[] values = value.split(",");
1373                         final Rational[] rationalArray = new Rational[values.length];
1374                         for (int j = 0; j < values.length; ++j) {
1375                             final String[] numbers = values[j].split("/");
1376                             rationalArray[j] = new Rational(Long.parseLong(numbers[0]),
1377                                     Long.parseLong(numbers[1]));
1378                         }
1379                         mAttributes[i].put(tag,
1380                                 ExifAttribute.createURational(rationalArray, mExifByteOrder));
1381                         break;
1382                     }
1383                     case IFD_FORMAT_SRATIONAL: {
1384                         final String[] values = value.split(",");
1385                         final Rational[] rationalArray = new Rational[values.length];
1386                         for (int j = 0; j < values.length; ++j) {
1387                             final String[] numbers = values[j].split("/");
1388                             rationalArray[j] = new Rational(Long.parseLong(numbers[0]),
1389                                     Long.parseLong(numbers[1]));
1390                         }
1391                         mAttributes[i].put(tag,
1392                                 ExifAttribute.createSRational(rationalArray, mExifByteOrder));
1393                         break;
1394                     }
1395                     case IFD_FORMAT_DOUBLE: {
1396                         final String[] values = value.split(",");
1397                         final double[] doubleArray = new double[values.length];
1398                         for (int j = 0; j < values.length; ++j) {
1399                             doubleArray[j] = Double.parseDouble(values[j]);
1400                         }
1401                         mAttributes[i].put(tag,
1402                                 ExifAttribute.createDouble(doubleArray, mExifByteOrder));
1403                         break;
1404                     }
1405                     default:
1406                         Log.w(TAG, "Data format isn't one of expected formats: " + dataFormat);
1407                         continue;
1408                 }
1409             }
1410         }
1411     }
1412 
1413     /**
1414      * Update the values of the tags in the tag groups if any value for the tag already was stored.
1415      *
1416      * @param tag the name of the tag.
1417      * @param value the value of the tag in a form of {@link ExifAttribute}.
1418      * @return Returns {@code true} if updating is placed.
1419      */
updateAttribute(String tag, ExifAttribute value)1420     private boolean updateAttribute(String tag, ExifAttribute value) {
1421         boolean updated = false;
1422         for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1423             if (mAttributes[i].containsKey(tag)) {
1424                 mAttributes[i].put(tag, value);
1425                 updated = true;
1426             }
1427         }
1428         return updated;
1429     }
1430 
1431     /**
1432      * Remove any values of the specified tag.
1433      *
1434      * @param tag the name of the tag.
1435      */
removeAttribute(String tag)1436     private void removeAttribute(String tag) {
1437         for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1438             mAttributes[i].remove(tag);
1439         }
1440     }
1441 
1442     /**
1443      * This function decides which parser to read the image data according to the given input stream
1444      * type and the content of the input stream. In each case, it reads the first three bytes to
1445      * determine whether the image data format is JPEG or not.
1446      */
loadAttributes(@onNull InputStream in)1447     private void loadAttributes(@NonNull InputStream in) throws IOException {
1448         try {
1449             // Initialize mAttributes.
1450             for (int i = 0; i < EXIF_TAGS.length; ++i) {
1451                 mAttributes[i] = new HashMap();
1452             }
1453 
1454             // Process RAW input stream
1455             if (mAssetInputStream != null) {
1456                 long asset = mAssetInputStream.getNativeAsset();
1457                 if (handleRawResult(nativeGetRawAttributesFromAsset(asset))) {
1458                     return;
1459                 }
1460             } else if (mSeekableFileDescriptor != null) {
1461                 if (handleRawResult(nativeGetRawAttributesFromFileDescriptor(
1462                         mSeekableFileDescriptor))) {
1463                     return;
1464                 }
1465             } else {
1466                 in = new BufferedInputStream(in, JPEG_SIGNATURE_SIZE);
1467                 if (!isJpegInputStream((BufferedInputStream) in) && handleRawResult(
1468                         nativeGetRawAttributesFromInputStream(in))) {
1469                     return;
1470                 }
1471             }
1472 
1473             // Process JPEG input stream
1474             getJpegAttributes(in);
1475         } catch (IOException e) {
1476             // Ignore exceptions in order to keep the compatibility with the old versions of
1477             // ExifInterface.
1478             Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file"
1479                     + "(ExifInterface supports JPEG and some RAW image formats only) "
1480                     + "or a corrupted JPEG file to ExifInterface.", e);
1481         } finally {
1482             addDefaultValuesForCompatibility();
1483 
1484             if (DEBUG) {
1485                 printAttributes();
1486             }
1487         }
1488     }
1489 
isJpegInputStream(BufferedInputStream in)1490     private static boolean isJpegInputStream(BufferedInputStream in) throws IOException {
1491         in.mark(JPEG_SIGNATURE_SIZE);
1492         byte[] signatureBytes = new byte[JPEG_SIGNATURE_SIZE];
1493         if (in.read(signatureBytes) != JPEG_SIGNATURE_SIZE) {
1494             throw new EOFException();
1495         }
1496         boolean isJpeg = Arrays.equals(JPEG_SIGNATURE, signatureBytes);
1497         in.reset();
1498         return isJpeg;
1499     }
1500 
handleRawResult(HashMap map)1501     private boolean handleRawResult(HashMap map) {
1502         if (map == null) {
1503             return false;
1504         }
1505 
1506         // Mark for disabling the save feature.
1507         mIsRaw = true;
1508 
1509         String value = (String) map.remove(TAG_HAS_THUMBNAIL);
1510         mHasThumbnail = value != null && value.equalsIgnoreCase("true");
1511         value = (String) map.remove(TAG_THUMBNAIL_OFFSET);
1512         if (value != null) {
1513             mThumbnailOffset = Integer.parseInt(value);
1514         }
1515         value = (String) map.remove(TAG_THUMBNAIL_LENGTH);
1516         if (value != null) {
1517             mThumbnailLength = Integer.parseInt(value);
1518         }
1519         mThumbnailBytes = (byte[]) map.remove(TAG_THUMBNAIL_DATA);
1520 
1521         for (Map.Entry entry : (Set<Map.Entry>) map.entrySet()) {
1522             setAttribute((String) entry.getKey(), (String) entry.getValue());
1523         }
1524 
1525         return true;
1526     }
1527 
isSeekableFD(FileDescriptor fd)1528     private static boolean isSeekableFD(FileDescriptor fd) throws IOException {
1529         try {
1530             Os.lseek(fd, 0, OsConstants.SEEK_CUR);
1531             return true;
1532         } catch (ErrnoException e) {
1533             return false;
1534         }
1535     }
1536 
1537     // Prints out attributes for debugging.
printAttributes()1538     private void printAttributes() {
1539         for (int i = 0; i < mAttributes.length; ++i) {
1540             Log.d(TAG, "The size of tag group[" + i + "]: " + mAttributes[i].size());
1541             for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
1542                 final ExifAttribute tagValue = (ExifAttribute) entry.getValue();
1543                 Log.d(TAG, "tagName: " + entry.getKey() + ", tagType: " + tagValue.toString()
1544                         + ", tagValue: '" + tagValue.getStringValue(mExifByteOrder) + "'");
1545             }
1546         }
1547     }
1548 
1549     /**
1550      * Save the tag data into the original image file. This is expensive because it involves
1551      * copying all the data from one file to another and deleting the old file and renaming the
1552      * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write
1553      * and make a single call rather than multiple calls for each attribute.
1554      */
saveAttributes()1555     public void saveAttributes() throws IOException {
1556         if (mIsRaw) {
1557             throw new UnsupportedOperationException(
1558                     "ExifInterface does not support saving attributes on RAW formats.");
1559         }
1560         if (mIsInputStream || (mSeekableFileDescriptor == null && mFilename == null)) {
1561             throw new UnsupportedOperationException(
1562                     "ExifInterface does not support saving attributes for the current input.");
1563         }
1564 
1565         // Keep the thumbnail in memory
1566         mThumbnailBytes = getThumbnail();
1567 
1568         FileInputStream in = null;
1569         FileOutputStream out = null;
1570         File tempFile = null;
1571         try {
1572             // Move the original file to temporary file.
1573             if (mFilename != null) {
1574                 tempFile = new File(mFilename + ".tmp");
1575                 File originalFile = new File(mFilename);
1576                 if (!originalFile.renameTo(tempFile)) {
1577                     throw new IOException("Could'nt rename to " + tempFile.getAbsolutePath());
1578                 }
1579             } else if (mSeekableFileDescriptor != null) {
1580                 tempFile = File.createTempFile("temp", "jpg");
1581                 Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
1582                 in = new FileInputStream(mSeekableFileDescriptor);
1583                 out = new FileOutputStream(tempFile);
1584                 Streams.copy(in, out);
1585             }
1586         } catch (ErrnoException e) {
1587             throw e.rethrowAsIOException();
1588         } finally {
1589             IoUtils.closeQuietly(in);
1590             IoUtils.closeQuietly(out);
1591         }
1592 
1593         in = null;
1594         out = null;
1595         try {
1596             // Save the new file.
1597             in = new FileInputStream(tempFile);
1598             if (mFilename != null) {
1599                 out = new FileOutputStream(mFilename);
1600             } else if (mSeekableFileDescriptor != null) {
1601                 Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
1602                 out = new FileOutputStream(mSeekableFileDescriptor);
1603             }
1604             saveJpegAttributes(in, out);
1605         } catch (ErrnoException e) {
1606             throw e.rethrowAsIOException();
1607         } finally {
1608             IoUtils.closeQuietly(in);
1609             IoUtils.closeQuietly(out);
1610             tempFile.delete();
1611         }
1612 
1613         // Discard the thumbnail in memory
1614         mThumbnailBytes = null;
1615     }
1616 
1617     /**
1618      * Returns true if the image file has a thumbnail.
1619      */
hasThumbnail()1620     public boolean hasThumbnail() {
1621         return mHasThumbnail;
1622     }
1623 
1624     /**
1625      * Returns the thumbnail inside the image file, or {@code null} if there is no thumbnail.
1626      * The returned data is in JPEG format and can be decoded using
1627      * {@link android.graphics.BitmapFactory#decodeByteArray(byte[],int,int)}
1628      */
getThumbnail()1629     public byte[] getThumbnail() {
1630         if (!mHasThumbnail) {
1631             return null;
1632         }
1633         if (mThumbnailBytes != null) {
1634             return mThumbnailBytes;
1635         }
1636 
1637         // Read the thumbnail.
1638         FileInputStream in = null;
1639         try {
1640             if (mAssetInputStream != null) {
1641                 return nativeGetThumbnailFromAsset(
1642                         mAssetInputStream.getNativeAsset(), mThumbnailOffset, mThumbnailLength);
1643             } else if (mFilename != null) {
1644                 in = new FileInputStream(mFilename);
1645             } else if (mSeekableFileDescriptor != null) {
1646                 FileDescriptor fileDescriptor = Os.dup(mSeekableFileDescriptor);
1647                 Os.lseek(fileDescriptor, 0, OsConstants.SEEK_SET);
1648                 in = new FileInputStream(fileDescriptor);
1649             }
1650             if (in == null) {
1651                 // Should not be reached this.
1652                 throw new FileNotFoundException();
1653             }
1654             if (in.skip(mThumbnailOffset) != mThumbnailOffset) {
1655                 throw new IOException("Corrupted image");
1656             }
1657             byte[] buffer = new byte[mThumbnailLength];
1658             if (in.read(buffer) != mThumbnailLength) {
1659                 throw new IOException("Corrupted image");
1660             }
1661             return buffer;
1662         } catch (IOException | ErrnoException e) {
1663             // Couldn't get a thumbnail image.
1664         } finally {
1665             IoUtils.closeQuietly(in);
1666         }
1667         return null;
1668     }
1669 
1670     /**
1671      * Returns the offset and length of thumbnail inside the image file, or
1672      * {@code null} if there is no thumbnail.
1673      *
1674      * @return two-element array, the offset in the first value, and length in
1675      *         the second, or {@code null} if no thumbnail was found.
1676      */
getThumbnailRange()1677     public long[] getThumbnailRange() {
1678         if (!mHasThumbnail) {
1679             return null;
1680         }
1681 
1682         long[] range = new long[2];
1683         range[0] = mThumbnailOffset;
1684         range[1] = mThumbnailLength;
1685 
1686         return range;
1687     }
1688 
1689     /**
1690      * Stores the latitude and longitude value in a float array. The first element is
1691      * the latitude, and the second element is the longitude. Returns false if the
1692      * Exif tags are not available.
1693      */
getLatLong(float output[])1694     public boolean getLatLong(float output[]) {
1695         String latValue = getAttribute(TAG_GPS_LATITUDE);
1696         String latRef = getAttribute(TAG_GPS_LATITUDE_REF);
1697         String lngValue = getAttribute(TAG_GPS_LONGITUDE);
1698         String lngRef = getAttribute(TAG_GPS_LONGITUDE_REF);
1699 
1700         if (latValue != null && latRef != null && lngValue != null && lngRef != null) {
1701             try {
1702                 output[0] = convertRationalLatLonToFloat(latValue, latRef);
1703                 output[1] = convertRationalLatLonToFloat(lngValue, lngRef);
1704                 return true;
1705             } catch (IllegalArgumentException e) {
1706                 // if values are not parseable
1707             }
1708         }
1709 
1710         return false;
1711     }
1712 
1713     /**
1714      * Return the altitude in meters. If the exif tag does not exist, return
1715      * <var>defaultValue</var>.
1716      *
1717      * @param defaultValue the value to return if the tag is not available.
1718      */
getAltitude(double defaultValue)1719     public double getAltitude(double defaultValue) {
1720         double altitude = getAttributeDouble(TAG_GPS_ALTITUDE, -1);
1721         int ref = getAttributeInt(TAG_GPS_ALTITUDE_REF, -1);
1722 
1723         if (altitude >= 0 && ref >= 0) {
1724             return (altitude * ((ref == 1) ? -1 : 1));
1725         } else {
1726             return defaultValue;
1727         }
1728     }
1729 
1730     /**
1731      * Returns number of milliseconds since Jan. 1, 1970, midnight local time.
1732      * Returns -1 if the date time information if not available.
1733      * @hide
1734      */
getDateTime()1735     public long getDateTime() {
1736         String dateTimeString = getAttribute(TAG_DATETIME);
1737         if (dateTimeString == null
1738                 || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
1739 
1740         ParsePosition pos = new ParsePosition(0);
1741         try {
1742             // The exif field is in local time. Parsing it as if it is UTC will yield time
1743             // since 1/1/1970 local time
1744             Date datetime = sFormatter.parse(dateTimeString, pos);
1745             if (datetime == null) return -1;
1746             long msecs = datetime.getTime();
1747 
1748             String subSecs = getAttribute(TAG_SUBSEC_TIME);
1749             if (subSecs != null) {
1750                 try {
1751                     long sub = Long.valueOf(subSecs);
1752                     while (sub > 1000) {
1753                         sub /= 10;
1754                     }
1755                     msecs += sub;
1756                 } catch (NumberFormatException e) {
1757                     // Ignored
1758                 }
1759             }
1760             return msecs;
1761         } catch (IllegalArgumentException e) {
1762             return -1;
1763         }
1764     }
1765 
1766     /**
1767      * Returns number of milliseconds since Jan. 1, 1970, midnight UTC.
1768      * Returns -1 if the date time information if not available.
1769      * @hide
1770      */
getGpsDateTime()1771     public long getGpsDateTime() {
1772         String date = getAttribute(TAG_GPS_DATESTAMP);
1773         String time = getAttribute(TAG_GPS_TIMESTAMP);
1774         if (date == null || time == null
1775                 || (!sNonZeroTimePattern.matcher(date).matches()
1776                 && !sNonZeroTimePattern.matcher(time).matches())) {
1777             return -1;
1778         }
1779 
1780         String dateTimeString = date + ' ' + time;
1781 
1782         ParsePosition pos = new ParsePosition(0);
1783         try {
1784             Date datetime = sFormatter.parse(dateTimeString, pos);
1785             if (datetime == null) return -1;
1786             return datetime.getTime();
1787         } catch (IllegalArgumentException e) {
1788             return -1;
1789         }
1790     }
1791 
convertRationalLatLonToFloat(String rationalString, String ref)1792     private static float convertRationalLatLonToFloat(String rationalString, String ref) {
1793         try {
1794             String [] parts = rationalString.split(",");
1795 
1796             String [] pair;
1797             pair = parts[0].split("/");
1798             double degrees = Double.parseDouble(pair[0].trim())
1799                     / Double.parseDouble(pair[1].trim());
1800 
1801             pair = parts[1].split("/");
1802             double minutes = Double.parseDouble(pair[0].trim())
1803                     / Double.parseDouble(pair[1].trim());
1804 
1805             pair = parts[2].split("/");
1806             double seconds = Double.parseDouble(pair[0].trim())
1807                     / Double.parseDouble(pair[1].trim());
1808 
1809             double result = degrees + (minutes / 60.0) + (seconds / 3600.0);
1810             if ((ref.equals("S") || ref.equals("W"))) {
1811                 return (float) -result;
1812             }
1813             return (float) result;
1814         } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
1815             // Not valid
1816             throw new IllegalArgumentException();
1817         }
1818     }
1819 
1820     // Loads EXIF attributes from a JPEG input stream.
getJpegAttributes(InputStream inputStream)1821     private void getJpegAttributes(InputStream inputStream) throws IOException {
1822         // See JPEG File Interchange Format Specification page 5.
1823         if (DEBUG) {
1824             Log.d(TAG, "getJpegAttributes starting with: " + inputStream);
1825         }
1826         DataInputStream dataInputStream = new DataInputStream(inputStream);
1827         byte marker;
1828         int bytesRead = 0;
1829         if ((marker = dataInputStream.readByte()) != MARKER) {
1830             throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
1831         }
1832         ++bytesRead;
1833         if (dataInputStream.readByte() != MARKER_SOI) {
1834             throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
1835         }
1836         ++bytesRead;
1837         while (true) {
1838             marker = dataInputStream.readByte();
1839             if (marker != MARKER) {
1840                 throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff));
1841             }
1842             ++bytesRead;
1843             marker = dataInputStream.readByte();
1844             if (DEBUG) {
1845                 Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff));
1846             }
1847             ++bytesRead;
1848 
1849             // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and
1850             // the image data will terminate right after.
1851             if (marker == MARKER_EOI || marker == MARKER_SOS) {
1852                 break;
1853             }
1854             int length = dataInputStream.readUnsignedShort() - 2;
1855             bytesRead += 2;
1856             if (DEBUG) {
1857                 Log.d(TAG, "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: "
1858                         + (length + 2) + ")");
1859             }
1860             if (length < 0) {
1861                 throw new IOException("Invalid length");
1862             }
1863             switch (marker) {
1864                 case MARKER_APP1: {
1865                     if (DEBUG) {
1866                         Log.d(TAG, "MARKER_APP1");
1867                     }
1868                     if (length < 6) {
1869                         // Skip if it's not an EXIF APP1 segment.
1870                         break;
1871                     }
1872                     byte[] identifier = new byte[6];
1873                     if (inputStream.read(identifier) != 6) {
1874                         throw new IOException("Invalid exif");
1875                     }
1876                     bytesRead += 6;
1877                     length -= 6;
1878                     if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
1879                         // Skip if it's not an EXIF APP1 segment.
1880                         break;
1881                     }
1882                     if (length <= 0) {
1883                         throw new IOException("Invalid exif");
1884                     }
1885                     if (DEBUG) {
1886                         Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")");
1887                     }
1888                     byte[] bytes = new byte[length];
1889                     if (dataInputStream.read(bytes) != length) {
1890                         throw new IOException("Invalid exif");
1891                     }
1892                     readExifSegment(bytes, bytesRead);
1893                     bytesRead += length;
1894                     length = 0;
1895                     break;
1896                 }
1897 
1898                 case MARKER_COM: {
1899                     byte[] bytes = new byte[length];
1900                     if (dataInputStream.read(bytes) != length) {
1901                         throw new IOException("Invalid exif");
1902                     }
1903                     length = 0;
1904                     if (getAttribute(TAG_USER_COMMENT) == null) {
1905                         mAttributes[IFD_EXIF_HINT].put(TAG_USER_COMMENT, ExifAttribute.createString(
1906                                 new String(bytes, ASCII)));
1907                     }
1908                     break;
1909                 }
1910 
1911                 case MARKER_SOF0:
1912                 case MARKER_SOF1:
1913                 case MARKER_SOF2:
1914                 case MARKER_SOF3:
1915                 case MARKER_SOF5:
1916                 case MARKER_SOF6:
1917                 case MARKER_SOF7:
1918                 case MARKER_SOF9:
1919                 case MARKER_SOF10:
1920                 case MARKER_SOF11:
1921                 case MARKER_SOF13:
1922                 case MARKER_SOF14:
1923                 case MARKER_SOF15: {
1924                     if (dataInputStream.skipBytes(1) != 1) {
1925                         throw new IOException("Invalid SOFx");
1926                     }
1927                     mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong(
1928                             dataInputStream.readUnsignedShort(), mExifByteOrder));
1929                     mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong(
1930                             dataInputStream.readUnsignedShort(), mExifByteOrder));
1931                     length -= 5;
1932                     break;
1933                 }
1934 
1935                 default: {
1936                     break;
1937                 }
1938             }
1939             if (length < 0) {
1940                 throw new IOException("Invalid length");
1941             }
1942             if (dataInputStream.skipBytes(length) != length) {
1943                 throw new IOException("Invalid JPEG segment");
1944             }
1945             bytesRead += length;
1946         }
1947     }
1948 
1949     // Stores a new JPEG image with EXIF attributes into a given output stream.
saveJpegAttributes(InputStream inputStream, OutputStream outputStream)1950     private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream)
1951             throws IOException {
1952         // See JPEG File Interchange Format Specification page 5.
1953         if (DEBUG) {
1954             Log.d(TAG, "saveJpegAttributes starting with (inputStream: " + inputStream
1955                     + ", outputStream: " + outputStream + ")");
1956         }
1957         DataInputStream dataInputStream = new DataInputStream(inputStream);
1958         ByteOrderAwarenessDataOutputStream dataOutputStream =
1959                 new ByteOrderAwarenessDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN);
1960         if (dataInputStream.readByte() != MARKER) {
1961             throw new IOException("Invalid marker");
1962         }
1963         dataOutputStream.writeByte(MARKER);
1964         if (dataInputStream.readByte() != MARKER_SOI) {
1965             throw new IOException("Invalid marker");
1966         }
1967         dataOutputStream.writeByte(MARKER_SOI);
1968 
1969         // Write EXIF APP1 segment
1970         dataOutputStream.writeByte(MARKER);
1971         dataOutputStream.writeByte(MARKER_APP1);
1972         writeExifSegment(dataOutputStream, 6);
1973 
1974         byte[] bytes = new byte[4096];
1975 
1976         while (true) {
1977             byte marker = dataInputStream.readByte();
1978             if (marker != MARKER) {
1979                 throw new IOException("Invalid marker");
1980             }
1981             marker = dataInputStream.readByte();
1982             switch (marker) {
1983                 case MARKER_APP1: {
1984                     int length = dataInputStream.readUnsignedShort() - 2;
1985                     if (length < 0) {
1986                         throw new IOException("Invalid length");
1987                     }
1988                     byte[] identifier = new byte[6];
1989                     if (length >= 6) {
1990                         if (dataInputStream.read(identifier) != 6) {
1991                             throw new IOException("Invalid exif");
1992                         }
1993                         if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
1994                             // Skip the original EXIF APP1 segment.
1995                             if (dataInputStream.skip(length - 6) != length - 6) {
1996                                 throw new IOException("Invalid length");
1997                             }
1998                             break;
1999                         }
2000                     }
2001                     // Copy non-EXIF APP1 segment.
2002                     dataOutputStream.writeByte(MARKER);
2003                     dataOutputStream.writeByte(marker);
2004                     dataOutputStream.writeUnsignedShort(length + 2);
2005                     if (length >= 6) {
2006                         length -= 6;
2007                         dataOutputStream.write(identifier);
2008                     }
2009                     int read;
2010                     while (length > 0 && (read = dataInputStream.read(
2011                             bytes, 0, Math.min(length, bytes.length))) >= 0) {
2012                         dataOutputStream.write(bytes, 0, read);
2013                         length -= read;
2014                     }
2015                     break;
2016                 }
2017                 case MARKER_EOI:
2018                 case MARKER_SOS: {
2019                     dataOutputStream.writeByte(MARKER);
2020                     dataOutputStream.writeByte(marker);
2021                     // Copy all the remaining data
2022                     Streams.copy(dataInputStream, dataOutputStream);
2023                     return;
2024                 }
2025                 default: {
2026                     // Copy JPEG segment
2027                     dataOutputStream.writeByte(MARKER);
2028                     dataOutputStream.writeByte(marker);
2029                     int length = dataInputStream.readUnsignedShort();
2030                     dataOutputStream.writeUnsignedShort(length);
2031                     length -= 2;
2032                     if (length < 0) {
2033                         throw new IOException("Invalid length");
2034                     }
2035                     int read;
2036                     while (length > 0 && (read = dataInputStream.read(
2037                             bytes, 0, Math.min(length, bytes.length))) >= 0) {
2038                         dataOutputStream.write(bytes, 0, read);
2039                         length -= read;
2040                     }
2041                     break;
2042                 }
2043             }
2044         }
2045     }
2046 
2047     // Reads the given EXIF byte area and save its tag data into attributes.
readExifSegment(byte[] exifBytes, int exifOffsetFromBeginning)2048     private void readExifSegment(byte[] exifBytes, int exifOffsetFromBeginning) throws IOException {
2049         // Parse TIFF Headers. See JEITA CP-3451C Table 1. page 10.
2050         ByteOrderAwarenessDataInputStream dataInputStream =
2051                 new ByteOrderAwarenessDataInputStream(exifBytes);
2052 
2053         // Read byte align
2054         short byteOrder = dataInputStream.readShort();
2055         switch (byteOrder) {
2056             case BYTE_ALIGN_II:
2057                 if (DEBUG) {
2058                     Log.d(TAG, "readExifSegment: Byte Align II");
2059                 }
2060                 mExifByteOrder = ByteOrder.LITTLE_ENDIAN;
2061                 break;
2062             case BYTE_ALIGN_MM:
2063                 if (DEBUG) {
2064                     Log.d(TAG, "readExifSegment: Byte Align MM");
2065                 }
2066                 mExifByteOrder = ByteOrder.BIG_ENDIAN;
2067                 break;
2068             default:
2069                 throw new IOException("Invalid byte order: " + Integer.toHexString(byteOrder));
2070         }
2071 
2072         // Set byte order.
2073         dataInputStream.setByteOrder(mExifByteOrder);
2074 
2075         int startCode = dataInputStream.readUnsignedShort();
2076         if (startCode != 0x2a) {
2077             throw new IOException("Invalid exif start: " + Integer.toHexString(startCode));
2078         }
2079 
2080         // Read first ifd offset
2081         long firstIfdOffset = dataInputStream.readUnsignedInt();
2082         if (firstIfdOffset < 8 || firstIfdOffset >= exifBytes.length) {
2083             throw new IOException("Invalid first Ifd offset: " + firstIfdOffset);
2084         }
2085         firstIfdOffset -= 8;
2086         if (firstIfdOffset > 0) {
2087             if (dataInputStream.skip(firstIfdOffset) != firstIfdOffset) {
2088                 throw new IOException("Couldn't jump to first Ifd: " + firstIfdOffset);
2089             }
2090         }
2091 
2092         // Read primary image TIFF image file directory.
2093         readImageFileDirectory(dataInputStream, IFD_TIFF_HINT);
2094 
2095         // Process thumbnail.
2096         String jpegInterchangeFormatString = getAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
2097         String jpegInterchangeFormatLengthString =
2098                 getAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
2099         if (jpegInterchangeFormatString != null && jpegInterchangeFormatLengthString != null) {
2100             try {
2101                 int jpegInterchangeFormat = Integer.parseInt(jpegInterchangeFormatString);
2102                 int jpegInterchangeFormatLength = Integer
2103                         .parseInt(jpegInterchangeFormatLengthString);
2104                 // The following code limits the size of thumbnail size not to overflow EXIF data area.
2105                 jpegInterchangeFormatLength = Math.min(jpegInterchangeFormat
2106                         + jpegInterchangeFormatLength, exifBytes.length) - jpegInterchangeFormat;
2107                 if (jpegInterchangeFormat > 0 && jpegInterchangeFormatLength > 0) {
2108                     mHasThumbnail = true;
2109                     mThumbnailOffset = exifOffsetFromBeginning + jpegInterchangeFormat;
2110                     mThumbnailLength = jpegInterchangeFormatLength;
2111 
2112                     if (mFilename == null && mAssetInputStream == null
2113                             && mSeekableFileDescriptor == null) {
2114                         // Save the thumbnail in memory if the input doesn't support reading again.
2115                         byte[] thumbnailBytes = new byte[jpegInterchangeFormatLength];
2116                         dataInputStream.seek(jpegInterchangeFormat);
2117                         dataInputStream.readFully(thumbnailBytes);
2118                         mThumbnailBytes = thumbnailBytes;
2119 
2120                         if (DEBUG) {
2121                             Bitmap bitmap = BitmapFactory.decodeByteArray(
2122                                     thumbnailBytes, 0, thumbnailBytes.length);
2123                             Log.d(TAG, "Thumbnail offset: " + mThumbnailOffset + ", length: "
2124                                     + mThumbnailLength + ", width: " + bitmap.getWidth()
2125                                     + ", height: "
2126                                     + bitmap.getHeight());
2127                         }
2128                     }
2129                 }
2130             } catch (NumberFormatException e) {
2131                 // Ignored the corrupted image.
2132             }
2133         }
2134     }
2135 
addDefaultValuesForCompatibility()2136     private void addDefaultValuesForCompatibility() {
2137         // The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag.
2138         String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL);
2139         if (valueOfDateTimeOriginal != null) {
2140             mAttributes[IFD_TIFF_HINT].put(TAG_DATETIME,
2141                     ExifAttribute.createString(valueOfDateTimeOriginal));
2142         }
2143 
2144         // Add the default value.
2145         if (getAttribute(TAG_IMAGE_WIDTH) == null) {
2146             mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH,
2147                     ExifAttribute.createULong(0, mExifByteOrder));
2148         }
2149         if (getAttribute(TAG_IMAGE_LENGTH) == null) {
2150             mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH,
2151                     ExifAttribute.createULong(0, mExifByteOrder));
2152         }
2153         if (getAttribute(TAG_ORIENTATION) == null) {
2154             mAttributes[IFD_TIFF_HINT].put(TAG_ORIENTATION,
2155                     ExifAttribute.createULong(0, mExifByteOrder));
2156         }
2157         if (getAttribute(TAG_LIGHT_SOURCE) == null) {
2158             mAttributes[IFD_EXIF_HINT].put(TAG_LIGHT_SOURCE,
2159                     ExifAttribute.createULong(0, mExifByteOrder));
2160         }
2161     }
2162 
2163     // Reads image file directory, which is a tag group in EXIF.
readImageFileDirectory(ByteOrderAwarenessDataInputStream dataInputStream, int hint)2164     private void readImageFileDirectory(ByteOrderAwarenessDataInputStream dataInputStream, int hint)
2165             throws IOException {
2166         if (dataInputStream.peek() + 2 > dataInputStream.mLength) {
2167             // Return if there is no data from the offset.
2168             return;
2169         }
2170         // See JEITA CP-3451 Figure 5. page 9.
2171         short numberOfDirectoryEntry = dataInputStream.readShort();
2172         if (dataInputStream.peek() + 12 * numberOfDirectoryEntry > dataInputStream.mLength) {
2173             // Return if the size of entries is too big.
2174             return;
2175         }
2176 
2177         if (DEBUG) {
2178             Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
2179         }
2180 
2181         for (short i = 0; i < numberOfDirectoryEntry; ++i) {
2182             int tagNumber = dataInputStream.readUnsignedShort();
2183             int dataFormat = dataInputStream.readUnsignedShort();
2184             int numberOfComponents = dataInputStream.readInt();
2185             long nextEntryOffset = dataInputStream.peek() + 4;  // next four bytes is for data
2186                                                                 // offset or value.
2187             // Look up a corresponding tag from tag number
2188             final ExifTag tag = (ExifTag) sExifTagMapsForReading[hint].get(tagNumber);
2189 
2190             if (DEBUG) {
2191                 Log.d(TAG, String.format("hint: %d, tagNumber: %d, tagName: %s, dataFormat: %d, " +
2192                         "numberOfComponents: %d", hint, tagNumber, tag != null ? tag.name : null,
2193                         dataFormat, numberOfComponents));
2194             }
2195 
2196             if (tag == null || dataFormat <= 0 ||
2197                     dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) {
2198                 // Skip if the parsed tag number is not defined or invalid data format.
2199                 if (tag == null) {
2200                     Log.w(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber);
2201                 } else {
2202                     Log.w(TAG, "Skip the tag entry since data format is invalid: " + dataFormat);
2203                 }
2204                 dataInputStream.seek(nextEntryOffset);
2205                 continue;
2206             }
2207 
2208             // Read a value from data field or seek to the value offset which is stored in data
2209             // field if the size of the entry value is bigger than 4.
2210             int byteCount = numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat];
2211             if (byteCount > 4) {
2212                 long offset = dataInputStream.readUnsignedInt();
2213                 if (DEBUG) {
2214                     Log.d(TAG, "seek to data offset: " + offset);
2215                 }
2216                 if (offset + byteCount <= dataInputStream.mLength) {
2217                     dataInputStream.seek(offset);
2218                 } else {
2219                      // Skip if invalid data offset.
2220                     Log.w(TAG, "Skip the tag entry since data offset is invalid: " + offset);
2221                     dataInputStream.seek(nextEntryOffset);
2222                     continue;
2223                 }
2224             }
2225 
2226             // Recursively parse IFD when a IFD pointer tag appears.
2227             int innerIfdHint = getIfdHintFromTagNumber(tagNumber);
2228             if (DEBUG) {
2229                 Log.d(TAG, "innerIfdHint: " + innerIfdHint + " byteCount: " + byteCount);
2230             }
2231 
2232             if (innerIfdHint >= 0) {
2233                 long offset = -1L;
2234                 // Get offset from data field
2235                 switch (dataFormat) {
2236                     case IFD_FORMAT_USHORT: {
2237                         offset = dataInputStream.readUnsignedShort();
2238                         break;
2239                     }
2240                     case IFD_FORMAT_SSHORT: {
2241                         offset = dataInputStream.readShort();
2242                         break;
2243                     }
2244                     case IFD_FORMAT_ULONG: {
2245                         offset = dataInputStream.readUnsignedInt();
2246                         break;
2247                     }
2248                     case IFD_FORMAT_SLONG: {
2249                         offset = dataInputStream.readInt();
2250                         break;
2251                     }
2252                     default: {
2253                         // Nothing to do
2254                         break;
2255                     }
2256                 }
2257                 if (DEBUG) {
2258                     Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tag.name));
2259                 }
2260                 if (offset > 0L && offset < dataInputStream.mLength) {
2261                     dataInputStream.seek(offset);
2262                     readImageFileDirectory(dataInputStream, innerIfdHint);
2263                 } else {
2264                     Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
2265                 }
2266 
2267                 dataInputStream.seek(nextEntryOffset);
2268                 continue;
2269             }
2270 
2271             byte[] bytes = new byte[numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat]];
2272             dataInputStream.readFully(bytes);
2273             mAttributes[hint].put(
2274                     tag.name, new ExifAttribute(dataFormat, numberOfComponents, bytes));
2275             if (dataInputStream.peek() != nextEntryOffset) {
2276                 dataInputStream.seek(nextEntryOffset);
2277             }
2278         }
2279 
2280         if (dataInputStream.peek() + 4 <= dataInputStream.mLength) {
2281             long nextIfdOffset = dataInputStream.readUnsignedInt();
2282             if (DEBUG) {
2283                 Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset));
2284             }
2285             // The next IFD offset needs to be bigger than 8
2286             // since the first IFD offset is at least 8.
2287             if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) {
2288                 dataInputStream.seek(nextIfdOffset);
2289                 readImageFileDirectory(dataInputStream, IFD_THUMBNAIL_HINT);
2290             }
2291         }
2292     }
2293 
2294     // Gets the corresponding IFD group index of the given tag number for writing Exif Tags.
getIfdHintFromTagNumber(int tagNumber)2295     private static int getIfdHintFromTagNumber(int tagNumber) {
2296         for (int i = 0; i < IFD_POINTER_TAG_HINTS.length; ++i) {
2297             if (IFD_POINTER_TAGS[i].number == tagNumber) {
2298                 return IFD_POINTER_TAG_HINTS[i];
2299             }
2300         }
2301         return -1;
2302     }
2303 
2304     // Writes an Exif segment into the given output stream.
writeExifSegment(ByteOrderAwarenessDataOutputStream dataOutputStream, int exifOffsetFromBeginning)2305     private int writeExifSegment(ByteOrderAwarenessDataOutputStream dataOutputStream,
2306             int exifOffsetFromBeginning) throws IOException {
2307         // The following variables are for calculating each IFD tag group size in bytes.
2308         int[] ifdOffsets = new int[EXIF_TAGS.length];
2309         int[] ifdDataSizes = new int[EXIF_TAGS.length];
2310 
2311         // Remove IFD pointer tags (we'll re-add it later.)
2312         for (ExifTag tag : IFD_POINTER_TAGS) {
2313             removeAttribute(tag.name);
2314         }
2315         // Remove old thumbnail data
2316         removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
2317         removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
2318 
2319         // Remove null value tags.
2320         for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
2321             for (Object obj : mAttributes[hint].entrySet().toArray()) {
2322                 final Map.Entry entry = (Map.Entry) obj;
2323                 if (entry.getValue() == null) {
2324                     mAttributes[hint].remove(entry.getKey());
2325                 }
2326             }
2327         }
2328 
2329         // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD
2330         // offset when there is one or more tags in the thumbnail IFD.
2331         if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) {
2332             mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].name,
2333                     ExifAttribute.createULong(0, mExifByteOrder));
2334         }
2335         if (!mAttributes[IFD_EXIF_HINT].isEmpty()) {
2336             mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].name,
2337                     ExifAttribute.createULong(0, mExifByteOrder));
2338         }
2339         if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
2340             mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name,
2341                     ExifAttribute.createULong(0, mExifByteOrder));
2342         }
2343         if (mHasThumbnail) {
2344             mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
2345                     ExifAttribute.createULong(0, mExifByteOrder));
2346             mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
2347                     ExifAttribute.createULong(mThumbnailLength, mExifByteOrder));
2348         }
2349 
2350         // Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
2351         // value which has a bigger size than 4 bytes.
2352         for (int i = 0; i < EXIF_TAGS.length; ++i) {
2353             int sum = 0;
2354             for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
2355                 final ExifAttribute exifAttribute = (ExifAttribute) ((Map.Entry) entry).getValue();
2356                 final int size = exifAttribute.size();
2357                 if (size > 4) {
2358                     sum += size;
2359                 }
2360             }
2361             ifdDataSizes[i] += sum;
2362         }
2363 
2364         // Calculate IFD offsets.
2365         int position = 8;
2366         for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
2367             if (!mAttributes[hint].isEmpty()) {
2368                 ifdOffsets[hint] = position;
2369                 position += 2 + mAttributes[hint].size() * 12 + 4 + ifdDataSizes[hint];
2370             }
2371         }
2372         if (mHasThumbnail) {
2373             int thumbnailOffset = position;
2374             mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
2375                     ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
2376             mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset;
2377             position += mThumbnailLength;
2378         }
2379 
2380         // Calculate the total size
2381         int totalSize = position + 8;  // eight bytes is for header part.
2382         if (DEBUG) {
2383             Log.d(TAG, "totalSize length: " + totalSize);
2384             for (int i = 0; i < EXIF_TAGS.length; ++i) {
2385                 Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d",
2386                         i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i]));
2387             }
2388         }
2389 
2390         // Update IFD pointer tags with the calculated offsets.
2391         if (!mAttributes[IFD_EXIF_HINT].isEmpty()) {
2392             mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].name,
2393                     ExifAttribute.createULong(ifdOffsets[IFD_EXIF_HINT], mExifByteOrder));
2394         }
2395         if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
2396             mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name,
2397                     ExifAttribute.createULong(ifdOffsets[IFD_GPS_HINT], mExifByteOrder));
2398         }
2399         if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) {
2400             mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].name, ExifAttribute.createULong(
2401                     ifdOffsets[IFD_INTEROPERABILITY_HINT], mExifByteOrder));
2402         }
2403 
2404         // Write TIFF Headers. See JEITA CP-3451C Table 1. page 10.
2405         dataOutputStream.writeUnsignedShort(totalSize);
2406         dataOutputStream.write(IDENTIFIER_EXIF_APP1);
2407         dataOutputStream.writeShort(mExifByteOrder == ByteOrder.BIG_ENDIAN
2408                 ? BYTE_ALIGN_MM : BYTE_ALIGN_II);
2409         dataOutputStream.setByteOrder(mExifByteOrder);
2410         dataOutputStream.writeUnsignedShort(0x2a);
2411         dataOutputStream.writeUnsignedInt(8);
2412 
2413         // Write IFD groups. See JEITA CP-3451C Figure 7. page 12.
2414         for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
2415             if (!mAttributes[hint].isEmpty()) {
2416                 // See JEITA CP-3451C 4.6.2 IFD structure. page 13.
2417                 // Write entry count
2418                 dataOutputStream.writeUnsignedShort(mAttributes[hint].size());
2419 
2420                 // Write entry info
2421                 int dataOffset = ifdOffsets[hint] + 2 + mAttributes[hint].size() * 12 + 4;
2422                 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) {
2423                     // Convert tag name to tag number.
2424                     final ExifTag tag = (ExifTag) sExifTagMapsForWriting[hint].get(entry.getKey());
2425                     final int tagNumber = tag.number;
2426                     final ExifAttribute attribute = (ExifAttribute) entry.getValue();
2427                     final int size = attribute.size();
2428 
2429                     dataOutputStream.writeUnsignedShort(tagNumber);
2430                     dataOutputStream.writeUnsignedShort(attribute.format);
2431                     dataOutputStream.writeInt(attribute.numberOfComponents);
2432                     if (size > 4) {
2433                         dataOutputStream.writeUnsignedInt(dataOffset);
2434                         dataOffset += size;
2435                     } else {
2436                         dataOutputStream.write(attribute.bytes);
2437                         // Fill zero up to 4 bytes
2438                         if (size < 4) {
2439                             for (int i = size; i < 4; ++i) {
2440                                 dataOutputStream.writeByte(0);
2441                             }
2442                         }
2443                     }
2444                 }
2445 
2446                 // Write the next offset. It writes the offset of thumbnail IFD if there is one or
2447                 // more tags in the thumbnail IFD when the current IFD is the primary image TIFF
2448                 // IFD; Otherwise 0.
2449                 if (hint == 0 && !mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) {
2450                     dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_THUMBNAIL_HINT]);
2451                 } else {
2452                     dataOutputStream.writeUnsignedInt(0);
2453                 }
2454 
2455                 // Write values of data field exceeding 4 bytes after the next offset.
2456                 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) {
2457                     ExifAttribute attribute = (ExifAttribute) entry.getValue();
2458 
2459                     if (attribute.bytes.length > 4) {
2460                         dataOutputStream.write(attribute.bytes, 0, attribute.bytes.length);
2461                     }
2462                 }
2463             }
2464         }
2465 
2466         // Write thumbnail
2467         if (mHasThumbnail) {
2468             dataOutputStream.write(getThumbnail());
2469         }
2470 
2471         // Reset the byte order to big endian in order to write remaining parts of the JPEG file.
2472         dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
2473 
2474         return totalSize;
2475     }
2476 
2477     /**
2478      * Determines the data format of EXIF entry value.
2479      *
2480      * @param entryValue The value to be determined.
2481      * @return Returns two data formats gussed as a pair in integer. If there is no two candidate
2482                data formats for the given entry value, returns {@code -1} in the second of the pair.
2483      */
guessDataFormat(String entryValue)2484     private static Pair<Integer, Integer> guessDataFormat(String entryValue) {
2485         // See TIFF 6.0 spec Types. page 15.
2486         // Take the first component if there are more than one component.
2487         if (entryValue.contains(",")) {
2488             String[] entryValues = entryValue.split(",");
2489             Pair<Integer, Integer> dataFormat = guessDataFormat(entryValues[0]);
2490             if (dataFormat.first == IFD_FORMAT_STRING) {
2491                 return dataFormat;
2492             }
2493             for (int i = 1; i < entryValues.length; ++i) {
2494                 final Pair<Integer, Integer> guessDataFormat = guessDataFormat(entryValues[i]);
2495                 int first = -1, second = -1;
2496                 if (guessDataFormat.first == dataFormat.first
2497                         || guessDataFormat.second == dataFormat.first) {
2498                     first = dataFormat.first;
2499                 }
2500                 if (dataFormat.second != -1 && (guessDataFormat.first == dataFormat.second
2501                         || guessDataFormat.second == dataFormat.second)) {
2502                     second = dataFormat.second;
2503                 }
2504                 if (first == -1 && second == -1) {
2505                     return new Pair<>(IFD_FORMAT_STRING, -1);
2506                 }
2507                 if (first == -1) {
2508                     dataFormat = new Pair<>(second, -1);
2509                     continue;
2510                 }
2511                 if (second == -1) {
2512                     dataFormat = new Pair<>(first, -1);
2513                     continue;
2514                 }
2515             }
2516             return dataFormat;
2517         }
2518 
2519         if (entryValue.contains("/")) {
2520             String[] rationalNumber = entryValue.split("/");
2521             if (rationalNumber.length == 2) {
2522                 try {
2523                     long numerator = Long.parseLong(rationalNumber[0]);
2524                     long denominator = Long.parseLong(rationalNumber[1]);
2525                     if (numerator < 0L || denominator < 0L) {
2526                         return new Pair<>(IFD_FORMAT_SRATIONAL, - 1);
2527                     }
2528                     if (numerator > Integer.MAX_VALUE || denominator > Integer.MAX_VALUE) {
2529                         return new Pair<>(IFD_FORMAT_URATIONAL, -1);
2530                     }
2531                     return new Pair<>(IFD_FORMAT_SRATIONAL, IFD_FORMAT_URATIONAL);
2532                 } catch (NumberFormatException e)  {
2533                     // Ignored
2534                 }
2535             }
2536             return new Pair<>(IFD_FORMAT_STRING, -1);
2537         }
2538         try {
2539             Long longValue = Long.parseLong(entryValue);
2540             if (longValue >= 0 && longValue <= 65535) {
2541                 return new Pair<>(IFD_FORMAT_USHORT, IFD_FORMAT_ULONG);
2542             }
2543             if (longValue < 0) {
2544                 return new Pair<>(IFD_FORMAT_SLONG, -1);
2545             }
2546             return new Pair<>(IFD_FORMAT_ULONG, -1);
2547         } catch (NumberFormatException e) {
2548             // Ignored
2549         }
2550         try {
2551             Double.parseDouble(entryValue);
2552             return new Pair<>(IFD_FORMAT_DOUBLE, -1);
2553         } catch (NumberFormatException e) {
2554             // Ignored
2555         }
2556         return new Pair<>(IFD_FORMAT_STRING, -1);
2557     }
2558 
2559     // An input stream to parse EXIF data area, which can be written in either little or big endian
2560     // order.
2561     private static class ByteOrderAwarenessDataInputStream extends ByteArrayInputStream {
2562         private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN;
2563         private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN;
2564 
2565         private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN;
2566         private final long mLength;
2567         private long mPosition;
2568 
ByteOrderAwarenessDataInputStream(byte[] bytes)2569         public ByteOrderAwarenessDataInputStream(byte[] bytes) {
2570             super(bytes);
2571             mLength = bytes.length;
2572             mPosition = 0L;
2573         }
2574 
setByteOrder(ByteOrder byteOrder)2575         public void setByteOrder(ByteOrder byteOrder) {
2576             mByteOrder = byteOrder;
2577         }
2578 
seek(long byteCount)2579         public void seek(long byteCount) throws IOException {
2580             mPosition = 0L;
2581             reset();
2582             if (skip(byteCount) != byteCount) {
2583                 throw new IOException("Couldn't seek up to the byteCount");
2584             }
2585         }
2586 
peek()2587         public long peek() {
2588             return mPosition;
2589         }
2590 
readFully(byte[] buffer)2591         public void readFully(byte[] buffer) throws IOException {
2592             mPosition += buffer.length;
2593             if (mPosition > mLength) {
2594                 throw new EOFException();
2595             }
2596             if (super.read(buffer, 0, buffer.length) != buffer.length) {
2597                 throw new IOException("Couldn't read up to the length of buffer");
2598             }
2599         }
2600 
readByte()2601         public byte readByte() throws IOException {
2602             ++mPosition;
2603             if (mPosition > mLength) {
2604                 throw new EOFException();
2605             }
2606             int ch = super.read();
2607             if (ch < 0) {
2608                 throw new EOFException();
2609             }
2610             return (byte) ch;
2611         }
2612 
readShort()2613         public short readShort() throws IOException {
2614             mPosition += 2;
2615             if (mPosition > mLength) {
2616                 throw new EOFException();
2617             }
2618             int ch1 = super.read();
2619             int ch2 = super.read();
2620             if ((ch1 | ch2) < 0) {
2621                 throw new EOFException();
2622             }
2623             if (mByteOrder == LITTLE_ENDIAN) {
2624                 return (short) ((ch2 << 8) + (ch1));
2625             } else if (mByteOrder == BIG_ENDIAN) {
2626                 return (short) ((ch1 << 8) + (ch2));
2627             }
2628             throw new IOException("Invalid byte order: " + mByteOrder);
2629         }
2630 
readInt()2631         public int readInt() throws IOException {
2632             mPosition += 4;
2633             if (mPosition > mLength) {
2634                 throw new EOFException();
2635             }
2636             int ch1 = super.read();
2637             int ch2 = super.read();
2638             int ch3 = super.read();
2639             int ch4 = super.read();
2640             if ((ch1 | ch2 | ch3 | ch4) < 0) {
2641                 throw new EOFException();
2642             }
2643             if (mByteOrder == LITTLE_ENDIAN) {
2644                 return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1);
2645             } else if (mByteOrder == BIG_ENDIAN) {
2646                 return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4);
2647             }
2648             throw new IOException("Invalid byte order: " + mByteOrder);
2649         }
2650 
2651         @Override
skip(long byteCount)2652         public long skip(long byteCount) {
2653             long skipped = super.skip(Math.min(byteCount, mLength - mPosition));
2654             mPosition += skipped;
2655             return skipped;
2656         }
2657 
readUnsignedShort()2658         public int readUnsignedShort() throws IOException {
2659             mPosition += 2;
2660             if (mPosition > mLength) {
2661                 throw new EOFException();
2662             }
2663             int ch1 = super.read();
2664             int ch2 = super.read();
2665             if ((ch1 | ch2) < 0) {
2666                 throw new EOFException();
2667             }
2668             if (mByteOrder == LITTLE_ENDIAN) {
2669                 return ((ch2 << 8) + (ch1));
2670             } else if (mByteOrder == BIG_ENDIAN) {
2671                 return ((ch1 << 8) + (ch2));
2672             }
2673             throw new IOException("Invalid byte order: " + mByteOrder);
2674         }
2675 
readUnsignedInt()2676         public long readUnsignedInt() throws IOException {
2677             return readInt() & 0xffffffffL;
2678         }
2679 
readLong()2680         public long readLong() throws IOException {
2681             mPosition += 8;
2682             if (mPosition > mLength) {
2683                 throw new EOFException();
2684             }
2685             int ch1 = super.read();
2686             int ch2 = super.read();
2687             int ch3 = super.read();
2688             int ch4 = super.read();
2689             int ch5 = super.read();
2690             int ch6 = super.read();
2691             int ch7 = super.read();
2692             int ch8 = super.read();
2693             if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) {
2694                 throw new EOFException();
2695             }
2696             if (mByteOrder == LITTLE_ENDIAN) {
2697                 return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40)
2698                         + ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16)
2699                         + ((long) ch2 << 8) + (long) ch1);
2700             } else if (mByteOrder == BIG_ENDIAN) {
2701                 return (((long) ch1 << 56) + ((long) ch2 << 48) + ((long) ch3 << 40)
2702                         + ((long) ch4 << 32) + ((long) ch5 << 24) + ((long) ch6 << 16)
2703                         + ((long) ch7 << 8) + (long) ch8);
2704             }
2705             throw new IOException("Invalid byte order: " + mByteOrder);
2706         }
2707 
readFloat()2708         public float readFloat() throws IOException {
2709             return Float.intBitsToFloat(readInt());
2710         }
2711 
readDouble()2712         public double readDouble() throws IOException {
2713             return Double.longBitsToDouble(readLong());
2714         }
2715     }
2716 
2717     // An output stream to write EXIF data area, which can be written in either little or big endian
2718     // order.
2719     private static class ByteOrderAwarenessDataOutputStream extends FilterOutputStream {
2720         private final OutputStream mOutputStream;
2721         private ByteOrder mByteOrder;
2722 
ByteOrderAwarenessDataOutputStream(OutputStream out, ByteOrder byteOrder)2723         public ByteOrderAwarenessDataOutputStream(OutputStream out, ByteOrder byteOrder) {
2724             super(out);
2725             mOutputStream = out;
2726             mByteOrder = byteOrder;
2727         }
2728 
setByteOrder(ByteOrder byteOrder)2729         public void setByteOrder(ByteOrder byteOrder) {
2730             mByteOrder = byteOrder;
2731         }
2732 
write(byte[] bytes)2733         public void write(byte[] bytes) throws IOException {
2734             mOutputStream.write(bytes);
2735         }
2736 
write(byte[] bytes, int offset, int length)2737         public void write(byte[] bytes, int offset, int length) throws IOException {
2738             mOutputStream.write(bytes, offset, length);
2739         }
2740 
writeByte(int val)2741         public void writeByte(int val) throws IOException {
2742             mOutputStream.write(val);
2743         }
2744 
writeShort(short val)2745         public void writeShort(short val) throws IOException {
2746             if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
2747                 mOutputStream.write((val >>> 0) & 0xFF);
2748                 mOutputStream.write((val >>> 8) & 0xFF);
2749             } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
2750                 mOutputStream.write((val >>> 8) & 0xFF);
2751                 mOutputStream.write((val >>> 0) & 0xFF);
2752             }
2753         }
2754 
writeInt(int val)2755         public void writeInt(int val) throws IOException {
2756             if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
2757                 mOutputStream.write((val >>> 0) & 0xFF);
2758                 mOutputStream.write((val >>> 8) & 0xFF);
2759                 mOutputStream.write((val >>> 16) & 0xFF);
2760                 mOutputStream.write((val >>> 24) & 0xFF);
2761             } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
2762                 mOutputStream.write((val >>> 24) & 0xFF);
2763                 mOutputStream.write((val >>> 16) & 0xFF);
2764                 mOutputStream.write((val >>> 8) & 0xFF);
2765                 mOutputStream.write((val >>> 0) & 0xFF);
2766             }
2767         }
2768 
writeUnsignedShort(int val)2769         public void writeUnsignedShort(int val) throws IOException {
2770             writeShort((short) val);
2771         }
2772 
writeUnsignedInt(long val)2773         public void writeUnsignedInt(long val) throws IOException {
2774             writeInt((int) val);
2775         }
2776     }
2777 
2778     // JNI methods for RAW formats.
nativeInitRaw()2779     private static native void nativeInitRaw();
nativeGetThumbnailFromAsset( long asset, int thumbnailOffset, int thumbnailLength)2780     private static native byte[] nativeGetThumbnailFromAsset(
2781             long asset, int thumbnailOffset, int thumbnailLength);
nativeGetRawAttributesFromAsset(long asset)2782     private static native HashMap nativeGetRawAttributesFromAsset(long asset);
nativeGetRawAttributesFromFileDescriptor(FileDescriptor fd)2783     private static native HashMap nativeGetRawAttributesFromFileDescriptor(FileDescriptor fd);
nativeGetRawAttributesFromInputStream(InputStream in)2784     private static native HashMap nativeGetRawAttributesFromInputStream(InputStream in);
2785 }
2786