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