• 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.support.media;
18 
19 import android.content.res.AssetManager;
20 import android.graphics.Bitmap;
21 import android.graphics.BitmapFactory;
22 import android.support.annotation.IntDef;
23 import android.support.annotation.NonNull;
24 import android.util.Log;
25 import android.util.Pair;
26 
27 import java.io.BufferedInputStream;
28 import java.io.ByteArrayInputStream;
29 import java.io.Closeable;
30 import java.io.DataInput;
31 import java.io.DataInputStream;
32 import java.io.EOFException;
33 import java.io.File;
34 import java.io.FileInputStream;
35 import java.io.FileNotFoundException;
36 import java.io.FileOutputStream;
37 import java.io.FilterOutputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.OutputStream;
41 import java.lang.annotation.Retention;
42 import java.lang.annotation.RetentionPolicy;
43 import java.nio.ByteBuffer;
44 import java.nio.ByteOrder;
45 import java.nio.charset.Charset;
46 import java.text.ParsePosition;
47 import java.text.SimpleDateFormat;
48 import java.util.Arrays;
49 import java.util.Date;
50 import java.util.HashMap;
51 import java.util.HashSet;
52 import java.util.Map;
53 import java.util.TimeZone;
54 import java.util.regex.Matcher;
55 import java.util.regex.Pattern;
56 
57 /**
58  * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file.
59  * <p>
60  * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW and RAF.
61  * <p>
62  * Attribute mutation is supported for JPEG image files.
63  */
64 public class ExifInterface {
65     private static final String TAG = "ExifInterface";
66     private static final boolean DEBUG = false;
67 
68     // The Exif tag names. See Tiff 6.0 Section 3 and Section 8.
69     /** Type is String. */
70     public static final String TAG_ARTIST = "Artist";
71     /** Type is int. */
72     public static final String TAG_BITS_PER_SAMPLE = "BitsPerSample";
73     /** Type is int. */
74     public static final String TAG_COMPRESSION = "Compression";
75     /** Type is String. */
76     public static final String TAG_COPYRIGHT = "Copyright";
77     /** Type is String. */
78     public static final String TAG_DATETIME = "DateTime";
79     /** Type is String. */
80     public static final String TAG_IMAGE_DESCRIPTION = "ImageDescription";
81     /** Type is int. */
82     public static final String TAG_IMAGE_LENGTH = "ImageLength";
83     /** Type is int. */
84     public static final String TAG_IMAGE_WIDTH = "ImageWidth";
85     /** Type is int. */
86     public static final String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
87     /** Type is int. */
88     public static final String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
89     /** Type is String. */
90     public static final String TAG_MAKE = "Make";
91     /** Type is String. */
92     public static final String TAG_MODEL = "Model";
93     /** Type is int. */
94     public static final String TAG_ORIENTATION = "Orientation";
95     /** Type is int. */
96     public static final String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
97     /** Type is int. */
98     public static final String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
99     /** Type is rational. */
100     public static final String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
101     /** Type is rational. */
102     public static final String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
103     /** Type is int. */
104     public static final String TAG_RESOLUTION_UNIT = "ResolutionUnit";
105     /** Type is int. */
106     public static final String TAG_ROWS_PER_STRIP = "RowsPerStrip";
107     /** Type is int. */
108     public static final String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
109     /** Type is String. */
110     public static final String TAG_SOFTWARE = "Software";
111     /** Type is int. */
112     public static final String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
113     /** Type is int. */
114     public static final String TAG_STRIP_OFFSETS = "StripOffsets";
115     /** Type is int. */
116     public static final String TAG_TRANSFER_FUNCTION = "TransferFunction";
117     /** Type is rational. */
118     public static final String TAG_WHITE_POINT = "WhitePoint";
119     /** Type is rational. */
120     public static final String TAG_X_RESOLUTION = "XResolution";
121     /** Type is rational. */
122     public static final String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
123     /** Type is int. */
124     public static final String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
125     /** Type is int. */
126     public static final String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
127     /** Type is rational. */
128     public static final String TAG_Y_RESOLUTION = "YResolution";
129     /** Type is rational. */
130     public static final String TAG_APERTURE_VALUE = "ApertureValue";
131     /** Type is rational. */
132     public static final String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
133     /** Type is String. */
134     public static final String TAG_CFA_PATTERN = "CFAPattern";
135     /** Type is int. */
136     public static final String TAG_COLOR_SPACE = "ColorSpace";
137     /** Type is String. */
138     public static final String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
139     /** Type is rational. */
140     public static final String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
141     /** Type is int. */
142     public static final String TAG_CONTRAST = "Contrast";
143     /** Type is int. */
144     public static final String TAG_CUSTOM_RENDERED = "CustomRendered";
145     /** Type is String. */
146     public static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
147     /** Type is String. */
148     public static final String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
149     /** Type is String. */
150     public static final String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
151     /** Type is double. */
152     public static final String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
153     /** Type is String. */
154     public static final String TAG_EXIF_VERSION = "ExifVersion";
155     /** Type is double. */
156     public static final String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
157     /** Type is rational. */
158     public static final String TAG_EXPOSURE_INDEX = "ExposureIndex";
159     /** Type is int. */
160     public static final String TAG_EXPOSURE_MODE = "ExposureMode";
161     /** Type is int. */
162     public static final String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
163     /** Type is double. */
164     public static final String TAG_EXPOSURE_TIME = "ExposureTime";
165     /** Type is double. */
166     public static final String TAG_F_NUMBER = "FNumber";
167     /** Type is String. */
168     public static final String TAG_FILE_SOURCE = "FileSource";
169     /** Type is int. */
170     public static final String TAG_FLASH = "Flash";
171     /** Type is rational. */
172     public static final String TAG_FLASH_ENERGY = "FlashEnergy";
173     /** Type is String. */
174     public static final String TAG_FLASHPIX_VERSION = "FlashpixVersion";
175     /** Type is rational. */
176     public static final String TAG_FOCAL_LENGTH = "FocalLength";
177     /** Type is int. */
178     public static final String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
179     /** Type is int. */
180     public static final String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
181     /** Type is rational. */
182     public static final String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
183     /** Type is rational. */
184     public static final String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
185     /** Type is int. */
186     public static final String TAG_GAIN_CONTROL = "GainControl";
187     /** Type is int. */
188     public static final String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
189     /** Type is String. */
190     public static final String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
191     /** Type is int. */
192     public static final String TAG_LIGHT_SOURCE = "LightSource";
193     /** Type is String. */
194     public static final String TAG_MAKER_NOTE = "MakerNote";
195     /** Type is rational. */
196     public static final String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
197     /** Type is int. */
198     public static final String TAG_METERING_MODE = "MeteringMode";
199     /** Type is int. */
200     public static final String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
201     /** Type is String. */
202     public static final String TAG_OECF = "OECF";
203     /** Type is int. */
204     public static final String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
205     /** Type is int. */
206     public static final String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
207     /** Type is String. */
208     public static final String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
209     /** Type is int. */
210     public static final String TAG_SATURATION = "Saturation";
211     /** Type is int. */
212     public static final String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
213     /** Type is String. */
214     public static final String TAG_SCENE_TYPE = "SceneType";
215     /** Type is int. */
216     public static final String TAG_SENSING_METHOD = "SensingMethod";
217     /** Type is int. */
218     public static final String TAG_SHARPNESS = "Sharpness";
219     /** Type is rational. */
220     public static final String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
221     /** Type is String. */
222     public static final String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
223     /** Type is String. */
224     public static final String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
225     /** Type is int. */
226     public static final String TAG_SUBFILE_TYPE = "SubfileType";
227     /** Type is String. */
228     public static final String TAG_SUBSEC_TIME = "SubSecTime";
229     /** Type is String. */
230     public static final String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
231     /** Type is String. */
232     public static final String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
233     /** Type is int. */
234     public static final String TAG_SUBJECT_AREA = "SubjectArea";
235     /** Type is double. */
236     public static final String TAG_SUBJECT_DISTANCE = "SubjectDistance";
237     /** Type is int. */
238     public static final String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
239     /** Type is int. */
240     public static final String TAG_SUBJECT_LOCATION = "SubjectLocation";
241     /** Type is String. */
242     public static final String TAG_USER_COMMENT = "UserComment";
243     /** Type is int. */
244     public static final String TAG_WHITE_BALANCE = "WhiteBalance";
245     /**
246      * The altitude (in meters) based on the reference in TAG_GPS_ALTITUDE_REF.
247      * Type is rational.
248      */
249     public static final String TAG_GPS_ALTITUDE = "GPSAltitude";
250     /**
251      * 0 if the altitude is above sea level. 1 if the altitude is below sea
252      * level. Type is int.
253      */
254     public static final String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
255     /** Type is String. */
256     public static final String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
257     /** Type is rational. */
258     public static final String TAG_GPS_DOP = "GPSDOP";
259     /** Type is String. */
260     public static final String TAG_GPS_DATESTAMP = "GPSDateStamp";
261     /** Type is rational. */
262     public static final String TAG_GPS_DEST_BEARING = "GPSDestBearing";
263     /** Type is String. */
264     public static final String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
265     /** Type is rational. */
266     public static final String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
267     /** Type is String. */
268     public static final String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
269     /** Type is rational. */
270     public static final String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
271     /** Type is String. */
272     public static final String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
273     /** Type is rational. */
274     public static final String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
275     /** Type is String. */
276     public static final String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
277     /** Type is int. */
278     public static final String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
279     /** Type is rational. */
280     public static final String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
281     /** Type is String. */
282     public static final String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
283     /** Type is rational. Format is "num1/denom1,num2/denom2,num3/denom3". */
284     public static final String TAG_GPS_LATITUDE = "GPSLatitude";
285     /** Type is String. */
286     public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
287     /** Type is rational. Format is "num1/denom1,num2/denom2,num3/denom3". */
288     public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
289     /** Type is String. */
290     public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
291     /** Type is String. */
292     public static final String TAG_GPS_MAP_DATUM = "GPSMapDatum";
293     /** Type is String. */
294     public static final String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
295     /** Type is String. Name of GPS processing method used for location finding. */
296     public static final String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
297     /** Type is String. */
298     public static final String TAG_GPS_SATELLITES = "GPSSatellites";
299     /** Type is rational. */
300     public static final String TAG_GPS_SPEED = "GPSSpeed";
301     /** Type is String. */
302     public static final String TAG_GPS_SPEED_REF = "GPSSpeedRef";
303     /** Type is String. */
304     public static final String TAG_GPS_STATUS = "GPSStatus";
305     /** Type is String. Format is "hh:mm:ss". */
306     public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
307     /** Type is rational. */
308     public static final String TAG_GPS_TRACK = "GPSTrack";
309     /** Type is String. */
310     public static final String TAG_GPS_TRACK_REF = "GPSTrackRef";
311     /** Type is String. */
312     public static final String TAG_GPS_VERSION_ID = "GPSVersionID";
313     /** Type is String. */
314     public static final String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
315     /** Type is int. */
316     public static final String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
317     /** Type is int. */
318     public static final String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
319     /** Type is int. DNG Specification 1.4.0.0. Section 4 */
320     public static final String TAG_DNG_VERSION = "DNGVersion";
321     /** Type is int. DNG Specification 1.4.0.0. Section 4 */
322     public static final String TAG_DEFAULT_CROP_SIZE = "DefaultCropSize";
323     /** Type is undefined. See Olympus MakerNote tags in http://www.exiv2.org/tags-olympus.html. */
324     public static final String TAG_ORF_THUMBNAIL_IMAGE = "ThumbnailImage";
325     /** Type is int. See Olympus Camera Settings tags in http://www.exiv2.org/tags-olympus.html. */
326     public static final String TAG_ORF_PREVIEW_IMAGE_START = "PreviewImageStart";
327     /** Type is int. See Olympus Camera Settings tags in http://www.exiv2.org/tags-olympus.html. */
328     public static final String TAG_ORF_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
329     /** Type is int. See Olympus Image Processing tags in http://www.exiv2.org/tags-olympus.html. */
330     public static final String TAG_ORF_ASPECT_FRAME = "AspectFrame";
331     /**
332      * Type is int. See PanasonicRaw tags in
333      * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
334      */
335     public static final String TAG_RW2_SENSOR_BOTTOM_BORDER = "SensorBottomBorder";
336     /**
337      * Type is int. See PanasonicRaw tags in
338      * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
339      */
340     public static final String TAG_RW2_SENSOR_LEFT_BORDER = "SensorLeftBorder";
341     /**
342      * Type is int. See PanasonicRaw tags in
343      * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
344      */
345     public static final String TAG_RW2_SENSOR_RIGHT_BORDER = "SensorRightBorder";
346     /**
347      * Type is int. See PanasonicRaw tags in
348      * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
349      */
350     public static final String TAG_RW2_SENSOR_TOP_BORDER = "SensorTopBorder";
351     /**
352      * Type is int. See PanasonicRaw tags in
353      * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
354      */
355     public static final String TAG_RW2_ISO = "ISO";
356     /**
357      * Type is undefined. See PanasonicRaw tags in
358      * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
359      */
360     public static final String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw";
361 
362     /**
363      * Private tags used for pointing the other IFD offsets.
364      * The types of the following tags are int.
365      * See JEITA CP-3451C Section 4.6.3: Exif-specific IFD.
366      * For SubIFD, see Note 1 of Adobe PageMaker® 6.0 TIFF Technical Notes.
367      */
368     private static final String TAG_EXIF_IFD_POINTER = "ExifIFDPointer";
369     private static final String TAG_GPS_INFO_IFD_POINTER = "GPSInfoIFDPointer";
370     private static final String TAG_INTEROPERABILITY_IFD_POINTER = "InteroperabilityIFDPointer";
371     private static final String TAG_SUB_IFD_POINTER = "SubIFDPointer";
372     // Proprietary pointer tags used for ORF files.
373     // See http://www.exiv2.org/tags-olympus.html
374     private static final String TAG_ORF_CAMERA_SETTINGS_IFD_POINTER = "CameraSettingsIFDPointer";
375     private static final String TAG_ORF_IMAGE_PROCESSING_IFD_POINTER = "ImageProcessingIFDPointer";
376 
377     // Private tags used for thumbnail information.
378     private static final String TAG_HAS_THUMBNAIL = "HasThumbnail";
379     private static final String TAG_THUMBNAIL_OFFSET = "ThumbnailOffset";
380     private static final String TAG_THUMBNAIL_LENGTH = "ThumbnailLength";
381     private static final String TAG_THUMBNAIL_DATA = "ThumbnailData";
382     private static final int MAX_THUMBNAIL_SIZE = 512;
383 
384     // Constants used for the Orientation Exif tag.
385     public static final int ORIENTATION_UNDEFINED = 0;
386     public static final int ORIENTATION_NORMAL = 1;
387     public static final int ORIENTATION_FLIP_HORIZONTAL = 2;  // left right reversed mirror
388     public static final int ORIENTATION_ROTATE_180 = 3;
389     public static final int ORIENTATION_FLIP_VERTICAL = 4;  // upside down mirror
390     // flipped about top-left <--> bottom-right axis
391     public static final int ORIENTATION_TRANSPOSE = 5;
392     public static final int ORIENTATION_ROTATE_90 = 6;  // rotate 90 cw to right it
393     // flipped about top-right <--> bottom-left axis
394     public static final int ORIENTATION_TRANSVERSE = 7;
395     public static final int ORIENTATION_ROTATE_270 = 8;  // rotate 270 to right it
396 
397     // Constants used for white balance
398     public static final int WHITEBALANCE_AUTO = 0;
399     public static final int WHITEBALANCE_MANUAL = 1;
400 
401     // Maximum size for checking file type signature (see image_type_recognition_lite.cc)
402     private static final int SIGNATURE_CHECK_SIZE = 5000;
403 
404     private static final byte[] JPEG_SIGNATURE = new byte[] {(byte) 0xff, (byte) 0xd8, (byte) 0xff};
405     private static final String RAF_SIGNATURE = "FUJIFILMCCD-RAW";
406     private static final int RAF_OFFSET_TO_JPEG_IMAGE_OFFSET = 84;
407     private static final int RAF_INFO_SIZE = 160;
408     private static final int RAF_JPEG_LENGTH_VALUE_SIZE = 4;
409 
410     // See http://fileformats.archiveteam.org/wiki/Olympus_ORF
411     private static final short ORF_SIGNATURE_1 = 0x4f52;
412     private static final short ORF_SIGNATURE_2 = 0x5352;
413     // There are two formats for Olympus Makernote Headers. Each has different identifiers and
414     // offsets to the actual data.
415     // See http://www.exiv2.org/makernote.html#R1
416     private static final byte[] ORF_MAKER_NOTE_HEADER_1 = new byte[] {(byte) 0x4f, (byte) 0x4c,
417             (byte) 0x59, (byte) 0x4d, (byte) 0x50, (byte) 0x00}; // "OLYMP\0"
418     private static final byte[] ORF_MAKER_NOTE_HEADER_2 = new byte[] {(byte) 0x4f, (byte) 0x4c,
419             (byte) 0x59, (byte) 0x4d, (byte) 0x50, (byte) 0x55, (byte) 0x53, (byte) 0x00,
420             (byte) 0x49, (byte) 0x49}; // "OLYMPUS\0II"
421     private static final int ORF_MAKER_NOTE_HEADER_1_SIZE = 8;
422     private static final int ORF_MAKER_NOTE_HEADER_2_SIZE = 12;
423 
424     // See http://fileformats.archiveteam.org/wiki/RW2
425     private static final short RW2_SIGNATURE = 0x0055;
426 
427     // See http://fileformats.archiveteam.org/wiki/Pentax_PEF
428     private static final String PEF_SIGNATURE = "PENTAX";
429     // See http://www.exiv2.org/makernote.html#R11
430     private static final int PEF_MAKER_NOTE_SKIP_SIZE = 6;
431 
432     private static SimpleDateFormat sFormatter;
433 
434     // See Exchangeable image file format for digital still cameras: Exif version 2.2.
435     // The following values are for parsing EXIF data area. There are tag groups in EXIF data area.
436     // They are called "Image File Directory". They have multiple data formats to cover various
437     // image metadata from GPS longitude to camera model name.
438 
439     // Types of Exif byte alignments (see JEITA CP-3451C Section 4.5.2)
440     private static final short BYTE_ALIGN_II = 0x4949;  // II: Intel order
441     private static final short BYTE_ALIGN_MM = 0x4d4d;  // MM: Motorola order
442 
443     // TIFF Header Fixed Constant (see JEITA CP-3451C Section 4.5.2)
444     private static final byte START_CODE = 0x2a; // 42
445     private static final int IFD_OFFSET = 8;
446 
447     // Formats for the value in IFD entry (See TIFF 6.0 Section 2, "Image File Directory".)
448     private static final int IFD_FORMAT_BYTE = 1;
449     private static final int IFD_FORMAT_STRING = 2;
450     private static final int IFD_FORMAT_USHORT = 3;
451     private static final int IFD_FORMAT_ULONG = 4;
452     private static final int IFD_FORMAT_URATIONAL = 5;
453     private static final int IFD_FORMAT_SBYTE = 6;
454     private static final int IFD_FORMAT_UNDEFINED = 7;
455     private static final int IFD_FORMAT_SSHORT = 8;
456     private static final int IFD_FORMAT_SLONG = 9;
457     private static final int IFD_FORMAT_SRATIONAL = 10;
458     private static final int IFD_FORMAT_SINGLE = 11;
459     private static final int IFD_FORMAT_DOUBLE = 12;
460     // Format indicating a new IFD entry (See Adobe PageMaker® 6.0 TIFF Technical Notes, "New Tag")
461     private static final int IFD_FORMAT_IFD = 13;
462     // Names for the data formats for debugging purpose.
463     private static final String[] IFD_FORMAT_NAMES = new String[] {
464             "", "BYTE", "STRING", "USHORT", "ULONG", "URATIONAL", "SBYTE", "UNDEFINED", "SSHORT",
465             "SLONG", "SRATIONAL", "SINGLE", "DOUBLE"
466     };
467     // Sizes of the components of each IFD value format
468     private static final int[] IFD_FORMAT_BYTES_PER_FORMAT = new int[] {
469             0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 1
470     };
471     private static final byte[] EXIF_ASCII_PREFIX = new byte[] {
472             0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0
473     };
474 
475     /**
476      * Constants used for Compression tag.
477      * For Value 1, 2, 32773, see TIFF 6.0 Spec Section 3: Bilevel Images, Compression
478      * For Value 6, see TIFF 6.0 Spec Section 22: JPEG Compression, Extensions to Existing Fields
479      * For Value 7, 8, 34892, see DNG Specification 1.4.0.0. Section 3, Compression
480      */
481     private static final int DATA_UNCOMPRESSED = 1;
482     private static final int DATA_HUFFMAN_COMPRESSED = 2;
483     private static final int DATA_JPEG = 6;
484     private static final int DATA_JPEG_COMPRESSED = 7;
485     private static final int DATA_DEFLATE_ZIP = 8;
486     private static final int DATA_PACK_BITS_COMPRESSED = 32773;
487     private static final int DATA_LOSSY_JPEG = 34892;
488 
489     /**
490      * Constants used for BitsPerSample tag.
491      * For RGB, see TIFF 6.0 Spec Section 6, Differences from Palette Color Images
492      * For Greyscale, see TIFF 6.0 Spec Section 4, Differences from Bilevel Images
493      */
494     private static final int[] BITS_PER_SAMPLE_RGB = new int[] { 8, 8, 8 };
495     private static final int[] BITS_PER_SAMPLE_GREYSCALE_1 = new int[] { 4 };
496     private static final int[] BITS_PER_SAMPLE_GREYSCALE_2 = new int[] { 8 };
497 
498     /**
499      * Constants used for PhotometricInterpretation tag.
500      * For White/Black, see Section 3, Color.
501      * See TIFF 6.0 Spec Section 22, Minimum Requirements for TIFF with JPEG Compression.
502      */
503     private static final int PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO = 0;
504     private static final int PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO = 1;
505     private static final int PHOTOMETRIC_INTERPRETATION_RGB = 2;
506     private static final int PHOTOMETRIC_INTERPRETATION_YCBCR = 6;
507 
508     /**
509      * Constants used for NewSubfileType tag.
510      * See TIFF 6.0 Spec Section 8
511      * */
512     private static final int ORIGINAL_RESOLUTION_IMAGE = 0;
513     private static final int REDUCED_RESOLUTION_IMAGE = 1;
514 
515     // A class for indicating EXIF rational type.
516     private static class Rational {
517         public final long numerator;
518         public final long denominator;
519 
Rational(long numerator, long denominator)520         private Rational(long numerator, long denominator) {
521             // Handle erroneous case
522             if (denominator == 0) {
523                 this.numerator = 0;
524                 this.denominator = 1;
525                 return;
526             }
527             this.numerator = numerator;
528             this.denominator = denominator;
529         }
530 
531         @Override
toString()532         public String toString() {
533             return numerator + "/" + denominator;
534         }
535 
calculate()536         public double calculate() {
537             return (double) numerator / denominator;
538         }
539     }
540 
541     // A class for indicating EXIF attribute.
542     private static class ExifAttribute {
543         public final int format;
544         public final int numberOfComponents;
545         public final byte[] bytes;
546 
ExifAttribute(int format, int numberOfComponents, byte[] bytes)547         private ExifAttribute(int format, int numberOfComponents, byte[] bytes) {
548             this.format = format;
549             this.numberOfComponents = numberOfComponents;
550             this.bytes = bytes;
551         }
552 
createUShort(int[] values, ByteOrder byteOrder)553         public static ExifAttribute createUShort(int[] values, ByteOrder byteOrder) {
554             final ByteBuffer buffer = ByteBuffer.wrap(
555                     new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_USHORT] * values.length]);
556             buffer.order(byteOrder);
557             for (int value : values) {
558                 buffer.putShort((short) value);
559             }
560             return new ExifAttribute(IFD_FORMAT_USHORT, values.length, buffer.array());
561         }
562 
createUShort(int value, ByteOrder byteOrder)563         public static ExifAttribute createUShort(int value, ByteOrder byteOrder) {
564             return createUShort(new int[] {value}, byteOrder);
565         }
566 
createULong(long[] values, ByteOrder byteOrder)567         public static ExifAttribute createULong(long[] values, ByteOrder byteOrder) {
568             final ByteBuffer buffer = ByteBuffer.wrap(
569                     new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_ULONG] * values.length]);
570             buffer.order(byteOrder);
571             for (long value : values) {
572                 buffer.putInt((int) value);
573             }
574             return new ExifAttribute(IFD_FORMAT_ULONG, values.length, buffer.array());
575         }
576 
createULong(long value, ByteOrder byteOrder)577         public static ExifAttribute createULong(long value, ByteOrder byteOrder) {
578             return createULong(new long[] {value}, byteOrder);
579         }
580 
createSLong(int[] values, ByteOrder byteOrder)581         public static ExifAttribute createSLong(int[] values, ByteOrder byteOrder) {
582             final ByteBuffer buffer = ByteBuffer.wrap(
583                     new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_SLONG] * values.length]);
584             buffer.order(byteOrder);
585             for (int value : values) {
586                 buffer.putInt(value);
587             }
588             return new ExifAttribute(IFD_FORMAT_SLONG, values.length, buffer.array());
589         }
590 
createSLong(int value, ByteOrder byteOrder)591         public static ExifAttribute createSLong(int value, ByteOrder byteOrder) {
592             return createSLong(new int[] {value}, byteOrder);
593         }
594 
createByte(String value)595         public static ExifAttribute createByte(String value) {
596             // Exception for GPSAltitudeRef tag
597             if (value.length() == 1 && value.charAt(0) >= '0' && value.charAt(0) <= '1') {
598                 final byte[] bytes = new byte[] { (byte) (value.charAt(0) - '0') };
599                 return new ExifAttribute(IFD_FORMAT_BYTE, bytes.length, bytes);
600             }
601             final byte[] ascii = value.getBytes(ASCII);
602             return new ExifAttribute(IFD_FORMAT_BYTE, ascii.length, ascii);
603         }
604 
createString(String value)605         public static ExifAttribute createString(String value) {
606             final byte[] ascii = (value + '\0').getBytes(ASCII);
607             return new ExifAttribute(IFD_FORMAT_STRING, ascii.length, ascii);
608         }
609 
createURational(Rational[] values, ByteOrder byteOrder)610         public static ExifAttribute createURational(Rational[] values, ByteOrder byteOrder) {
611             final ByteBuffer buffer = ByteBuffer.wrap(
612                     new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_URATIONAL] * values.length]);
613             buffer.order(byteOrder);
614             for (Rational value : values) {
615                 buffer.putInt((int) value.numerator);
616                 buffer.putInt((int) value.denominator);
617             }
618             return new ExifAttribute(IFD_FORMAT_URATIONAL, values.length, buffer.array());
619         }
620 
createURational(Rational value, ByteOrder byteOrder)621         public static ExifAttribute createURational(Rational value, ByteOrder byteOrder) {
622             return createURational(new Rational[] {value}, byteOrder);
623         }
624 
createSRational(Rational[] values, ByteOrder byteOrder)625         public static ExifAttribute createSRational(Rational[] values, ByteOrder byteOrder) {
626             final ByteBuffer buffer = ByteBuffer.wrap(
627                     new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_SRATIONAL] * values.length]);
628             buffer.order(byteOrder);
629             for (Rational value : values) {
630                 buffer.putInt((int) value.numerator);
631                 buffer.putInt((int) value.denominator);
632             }
633             return new ExifAttribute(IFD_FORMAT_SRATIONAL, values.length, buffer.array());
634         }
635 
createSRational(Rational value, ByteOrder byteOrder)636         public static ExifAttribute createSRational(Rational value, ByteOrder byteOrder) {
637             return createSRational(new Rational[] {value}, byteOrder);
638         }
639 
createDouble(double[] values, ByteOrder byteOrder)640         public static ExifAttribute createDouble(double[] values, ByteOrder byteOrder) {
641             final ByteBuffer buffer = ByteBuffer.wrap(
642                     new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_DOUBLE] * values.length]);
643             buffer.order(byteOrder);
644             for (double value : values) {
645                 buffer.putDouble(value);
646             }
647             return new ExifAttribute(IFD_FORMAT_DOUBLE, values.length, buffer.array());
648         }
649 
createDouble(double value, ByteOrder byteOrder)650         public static ExifAttribute createDouble(double value, ByteOrder byteOrder) {
651             return createDouble(new double[] {value}, byteOrder);
652         }
653 
654         @Override
toString()655         public String toString() {
656             return "(" + IFD_FORMAT_NAMES[format] + ", data length:" + bytes.length + ")";
657         }
658 
getValue(ByteOrder byteOrder)659         private Object getValue(ByteOrder byteOrder) {
660             ByteOrderedDataInputStream inputStream = null;
661             try {
662                 inputStream = new ByteOrderedDataInputStream(bytes);
663                 inputStream.setByteOrder(byteOrder);
664                 switch (format) {
665                     case IFD_FORMAT_BYTE:
666                     case IFD_FORMAT_SBYTE: {
667                         // Exception for GPSAltitudeRef tag
668                         if (bytes.length == 1 && bytes[0] >= 0 && bytes[0] <= 1) {
669                             return new String(new char[] { (char) (bytes[0] + '0') });
670                         }
671                         return new String(bytes, ASCII);
672                     }
673                     case IFD_FORMAT_UNDEFINED:
674                     case IFD_FORMAT_STRING: {
675                         int index = 0;
676                         if (numberOfComponents >= EXIF_ASCII_PREFIX.length) {
677                             boolean same = true;
678                             for (int i = 0; i < EXIF_ASCII_PREFIX.length; ++i) {
679                                 if (bytes[i] != EXIF_ASCII_PREFIX[i]) {
680                                     same = false;
681                                     break;
682                                 }
683                             }
684                             if (same) {
685                                 index = EXIF_ASCII_PREFIX.length;
686                             }
687                         }
688 
689                         StringBuilder stringBuilder = new StringBuilder();
690                         while (index < numberOfComponents) {
691                             int ch = bytes[index];
692                             if (ch == 0) {
693                                 break;
694                             }
695                             if (ch >= 32) {
696                                 stringBuilder.append((char) ch);
697                             } else {
698                                 stringBuilder.append('?');
699                             }
700                             ++index;
701                         }
702                         return stringBuilder.toString();
703                     }
704                     case IFD_FORMAT_USHORT: {
705                         final int[] values = new int[numberOfComponents];
706                         for (int i = 0; i < numberOfComponents; ++i) {
707                             values[i] = inputStream.readUnsignedShort();
708                         }
709                         return values;
710                     }
711                     case IFD_FORMAT_ULONG: {
712                         final long[] values = new long[numberOfComponents];
713                         for (int i = 0; i < numberOfComponents; ++i) {
714                             values[i] = inputStream.readUnsignedInt();
715                         }
716                         return values;
717                     }
718                     case IFD_FORMAT_URATIONAL: {
719                         final Rational[] values = new Rational[numberOfComponents];
720                         for (int i = 0; i < numberOfComponents; ++i) {
721                             final long numerator = inputStream.readUnsignedInt();
722                             final long denominator = inputStream.readUnsignedInt();
723                             values[i] = new Rational(numerator, denominator);
724                         }
725                         return values;
726                     }
727                     case IFD_FORMAT_SSHORT: {
728                         final int[] values = new int[numberOfComponents];
729                         for (int i = 0; i < numberOfComponents; ++i) {
730                             values[i] = inputStream.readShort();
731                         }
732                         return values;
733                     }
734                     case IFD_FORMAT_SLONG: {
735                         final int[] values = new int[numberOfComponents];
736                         for (int i = 0; i < numberOfComponents; ++i) {
737                             values[i] = inputStream.readInt();
738                         }
739                         return values;
740                     }
741                     case IFD_FORMAT_SRATIONAL: {
742                         final Rational[] values = new Rational[numberOfComponents];
743                         for (int i = 0; i < numberOfComponents; ++i) {
744                             final long numerator = inputStream.readInt();
745                             final long denominator = inputStream.readInt();
746                             values[i] = new Rational(numerator, denominator);
747                         }
748                         return values;
749                     }
750                     case IFD_FORMAT_SINGLE: {
751                         final double[] values = new double[numberOfComponents];
752                         for (int i = 0; i < numberOfComponents; ++i) {
753                             values[i] = inputStream.readFloat();
754                         }
755                         return values;
756                     }
757                     case IFD_FORMAT_DOUBLE: {
758                         final double[] values = new double[numberOfComponents];
759                         for (int i = 0; i < numberOfComponents; ++i) {
760                             values[i] = inputStream.readDouble();
761                         }
762                         return values;
763                     }
764                     default:
765                         return null;
766                 }
767             } catch (IOException e) {
768                 Log.w(TAG, "IOException occurred during reading a value", e);
769                 return null;
770             } finally {
771                 if (inputStream != null) {
772                     try {
773                         inputStream.close();
774                     } catch (IOException e) {
775                         Log.e(TAG, "IOException occurred while closing InputStream", e);
776                     }
777                 }
778             }
779         }
780 
getDoubleValue(ByteOrder byteOrder)781         public double getDoubleValue(ByteOrder byteOrder) {
782             Object value = getValue(byteOrder);
783             if (value == null) {
784                 throw new NumberFormatException("NULL can't be converted to a double value");
785             }
786             if (value instanceof String) {
787                 return Double.parseDouble((String) value);
788             }
789             if (value instanceof long[]) {
790                 long[] array = (long[]) value;
791                 if (array.length == 1) {
792                     return array[0];
793                 }
794                 throw new NumberFormatException("There are more than one component");
795             }
796             if (value instanceof int[]) {
797                 int[] array = (int[]) value;
798                 if (array.length == 1) {
799                     return array[0];
800                 }
801                 throw new NumberFormatException("There are more than one component");
802             }
803             if (value instanceof double[]) {
804                 double[] array = (double[]) value;
805                 if (array.length == 1) {
806                     return array[0];
807                 }
808                 throw new NumberFormatException("There are more than one component");
809             }
810             if (value instanceof Rational[]) {
811                 Rational[] array = (Rational[]) value;
812                 if (array.length == 1) {
813                     return array[0].calculate();
814                 }
815                 throw new NumberFormatException("There are more than one component");
816             }
817             throw new NumberFormatException("Couldn't find a double value");
818         }
819 
getIntValue(ByteOrder byteOrder)820         public int getIntValue(ByteOrder byteOrder) {
821             Object value = getValue(byteOrder);
822             if (value == null) {
823                 throw new NumberFormatException("NULL can't be converted to a integer value");
824             }
825             if (value instanceof String) {
826                 return Integer.parseInt((String) value);
827             }
828             if (value instanceof long[]) {
829                 long[] array = (long[]) value;
830                 if (array.length == 1) {
831                     return (int) array[0];
832                 }
833                 throw new NumberFormatException("There are more than one component");
834             }
835             if (value instanceof int[]) {
836                 int[] array = (int[]) value;
837                 if (array.length == 1) {
838                     return array[0];
839                 }
840                 throw new NumberFormatException("There are more than one component");
841             }
842             throw new NumberFormatException("Couldn't find a integer value");
843         }
844 
getStringValue(ByteOrder byteOrder)845         public String getStringValue(ByteOrder byteOrder) {
846             Object value = getValue(byteOrder);
847             if (value == null) {
848                 return null;
849             }
850             if (value instanceof String) {
851                 return (String) value;
852             }
853 
854             final StringBuilder stringBuilder = new StringBuilder();
855             if (value instanceof long[]) {
856                 long[] array = (long[]) value;
857                 for (int i = 0; i < array.length; ++i) {
858                     stringBuilder.append(array[i]);
859                     if (i + 1 != array.length) {
860                         stringBuilder.append(",");
861                     }
862                 }
863                 return stringBuilder.toString();
864             }
865             if (value instanceof int[]) {
866                 int[] array = (int[]) value;
867                 for (int i = 0; i < array.length; ++i) {
868                     stringBuilder.append(array[i]);
869                     if (i + 1 != array.length) {
870                         stringBuilder.append(",");
871                     }
872                 }
873                 return stringBuilder.toString();
874             }
875             if (value instanceof double[]) {
876                 double[] array = (double[]) value;
877                 for (int i = 0; i < array.length; ++i) {
878                     stringBuilder.append(array[i]);
879                     if (i + 1 != array.length) {
880                         stringBuilder.append(",");
881                     }
882                 }
883                 return stringBuilder.toString();
884             }
885             if (value instanceof Rational[]) {
886                 Rational[] array = (Rational[]) value;
887                 for (int i = 0; i < array.length; ++i) {
888                     stringBuilder.append(array[i].numerator);
889                     stringBuilder.append('/');
890                     stringBuilder.append(array[i].denominator);
891                     if (i + 1 != array.length) {
892                         stringBuilder.append(",");
893                     }
894                 }
895                 return stringBuilder.toString();
896             }
897             return null;
898         }
899 
size()900         public int size() {
901             return IFD_FORMAT_BYTES_PER_FORMAT[format] * numberOfComponents;
902         }
903     }
904 
905     // A class for indicating EXIF tag.
906     private static class ExifTag {
907         public final int number;
908         public final String name;
909         public final int primaryFormat;
910         public final int secondaryFormat;
911 
ExifTag(String name, int number, int format)912         private ExifTag(String name, int number, int format) {
913             this.name = name;
914             this.number = number;
915             this.primaryFormat = format;
916             this.secondaryFormat = -1;
917         }
918 
ExifTag(String name, int number, int primaryFormat, int secondaryFormat)919         private ExifTag(String name, int number, int primaryFormat, int secondaryFormat) {
920             this.name = name;
921             this.number = number;
922             this.primaryFormat = primaryFormat;
923             this.secondaryFormat = secondaryFormat;
924         }
925     }
926 
927     // Primary image IFD TIFF tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
928     private static final ExifTag[] IFD_TIFF_TAGS = new ExifTag[] {
929             // For below two, see TIFF 6.0 Spec Section 3: Bilevel Images.
930             new ExifTag(TAG_NEW_SUBFILE_TYPE, 254, IFD_FORMAT_ULONG),
931             new ExifTag(TAG_SUBFILE_TYPE, 255, IFD_FORMAT_ULONG),
932             new ExifTag(TAG_IMAGE_WIDTH, 256, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
933             new ExifTag(TAG_IMAGE_LENGTH, 257, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
934             new ExifTag(TAG_BITS_PER_SAMPLE, 258, IFD_FORMAT_USHORT),
935             new ExifTag(TAG_COMPRESSION, 259, IFD_FORMAT_USHORT),
936             new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262, IFD_FORMAT_USHORT),
937             new ExifTag(TAG_IMAGE_DESCRIPTION, 270, IFD_FORMAT_STRING),
938             new ExifTag(TAG_MAKE, 271, IFD_FORMAT_STRING),
939             new ExifTag(TAG_MODEL, 272, IFD_FORMAT_STRING),
940             new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
941             new ExifTag(TAG_ORIENTATION, 274, IFD_FORMAT_USHORT),
942             new ExifTag(TAG_SAMPLES_PER_PIXEL, 277, IFD_FORMAT_USHORT),
943             new ExifTag(TAG_ROWS_PER_STRIP, 278, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
944             new ExifTag(TAG_STRIP_BYTE_COUNTS, 279, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
945             new ExifTag(TAG_X_RESOLUTION, 282, IFD_FORMAT_URATIONAL),
946             new ExifTag(TAG_Y_RESOLUTION, 283, IFD_FORMAT_URATIONAL),
947             new ExifTag(TAG_PLANAR_CONFIGURATION, 284, IFD_FORMAT_USHORT),
948             new ExifTag(TAG_RESOLUTION_UNIT, 296, IFD_FORMAT_USHORT),
949             new ExifTag(TAG_TRANSFER_FUNCTION, 301, IFD_FORMAT_USHORT),
950             new ExifTag(TAG_SOFTWARE, 305, IFD_FORMAT_STRING),
951             new ExifTag(TAG_DATETIME, 306, IFD_FORMAT_STRING),
952             new ExifTag(TAG_ARTIST, 315, IFD_FORMAT_STRING),
953             new ExifTag(TAG_WHITE_POINT, 318, IFD_FORMAT_URATIONAL),
954             new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319, IFD_FORMAT_URATIONAL),
955             // See Adobe PageMaker® 6.0 TIFF Technical Notes, Note 1.
956             new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
957             new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG),
958             new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG),
959             new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529, IFD_FORMAT_URATIONAL),
960             new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
961             new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
962             new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
963             new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
964             new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
965             new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
966             // RW2 file tags
967             // See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html)
968             new ExifTag(TAG_RW2_SENSOR_TOP_BORDER, 4, IFD_FORMAT_ULONG),
969             new ExifTag(TAG_RW2_SENSOR_LEFT_BORDER, 5, IFD_FORMAT_ULONG),
970             new ExifTag(TAG_RW2_SENSOR_BOTTOM_BORDER, 6, IFD_FORMAT_ULONG),
971             new ExifTag(TAG_RW2_SENSOR_RIGHT_BORDER, 7, IFD_FORMAT_ULONG),
972             new ExifTag(TAG_RW2_ISO, 23, IFD_FORMAT_USHORT),
973             new ExifTag(TAG_RW2_JPG_FROM_RAW, 46, IFD_FORMAT_UNDEFINED)
974     };
975 
976     // Primary image IFD Exif Private tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
977     private static final ExifTag[] IFD_EXIF_TAGS = new ExifTag[] {
978             new ExifTag(TAG_EXPOSURE_TIME, 33434, IFD_FORMAT_URATIONAL),
979             new ExifTag(TAG_F_NUMBER, 33437, IFD_FORMAT_URATIONAL),
980             new ExifTag(TAG_EXPOSURE_PROGRAM, 34850, IFD_FORMAT_USHORT),
981             new ExifTag(TAG_SPECTRAL_SENSITIVITY, 34852, IFD_FORMAT_STRING),
982             new ExifTag(TAG_ISO_SPEED_RATINGS, 34855, IFD_FORMAT_USHORT),
983             new ExifTag(TAG_OECF, 34856, IFD_FORMAT_UNDEFINED),
984             new ExifTag(TAG_EXIF_VERSION, 36864, IFD_FORMAT_STRING),
985             new ExifTag(TAG_DATETIME_ORIGINAL, 36867, IFD_FORMAT_STRING),
986             new ExifTag(TAG_DATETIME_DIGITIZED, 36868, IFD_FORMAT_STRING),
987             new ExifTag(TAG_COMPONENTS_CONFIGURATION, 37121, IFD_FORMAT_UNDEFINED),
988             new ExifTag(TAG_COMPRESSED_BITS_PER_PIXEL, 37122, IFD_FORMAT_URATIONAL),
989             new ExifTag(TAG_SHUTTER_SPEED_VALUE, 37377, IFD_FORMAT_SRATIONAL),
990             new ExifTag(TAG_APERTURE_VALUE, 37378, IFD_FORMAT_URATIONAL),
991             new ExifTag(TAG_BRIGHTNESS_VALUE, 37379, IFD_FORMAT_SRATIONAL),
992             new ExifTag(TAG_EXPOSURE_BIAS_VALUE, 37380, IFD_FORMAT_SRATIONAL),
993             new ExifTag(TAG_MAX_APERTURE_VALUE, 37381, IFD_FORMAT_URATIONAL),
994             new ExifTag(TAG_SUBJECT_DISTANCE, 37382, IFD_FORMAT_URATIONAL),
995             new ExifTag(TAG_METERING_MODE, 37383, IFD_FORMAT_USHORT),
996             new ExifTag(TAG_LIGHT_SOURCE, 37384, IFD_FORMAT_USHORT),
997             new ExifTag(TAG_FLASH, 37385, IFD_FORMAT_USHORT),
998             new ExifTag(TAG_FOCAL_LENGTH, 37386, IFD_FORMAT_URATIONAL),
999             new ExifTag(TAG_SUBJECT_AREA, 37396, IFD_FORMAT_USHORT),
1000             new ExifTag(TAG_MAKER_NOTE, 37500, IFD_FORMAT_UNDEFINED),
1001             new ExifTag(TAG_USER_COMMENT, 37510, IFD_FORMAT_UNDEFINED),
1002             new ExifTag(TAG_SUBSEC_TIME, 37520, IFD_FORMAT_STRING),
1003             new ExifTag(TAG_SUBSEC_TIME_ORIGINAL, 37521, IFD_FORMAT_STRING),
1004             new ExifTag(TAG_SUBSEC_TIME_DIGITIZED, 37522, IFD_FORMAT_STRING),
1005             new ExifTag(TAG_FLASHPIX_VERSION, 40960, IFD_FORMAT_UNDEFINED),
1006             new ExifTag(TAG_COLOR_SPACE, 40961, IFD_FORMAT_USHORT),
1007             new ExifTag(TAG_PIXEL_X_DIMENSION, 40962, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
1008             new ExifTag(TAG_PIXEL_Y_DIMENSION, 40963, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
1009             new ExifTag(TAG_RELATED_SOUND_FILE, 40964, IFD_FORMAT_STRING),
1010             new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG),
1011             new ExifTag(TAG_FLASH_ENERGY, 41483, IFD_FORMAT_URATIONAL),
1012             new ExifTag(TAG_SPATIAL_FREQUENCY_RESPONSE, 41484, IFD_FORMAT_UNDEFINED),
1013             new ExifTag(TAG_FOCAL_PLANE_X_RESOLUTION, 41486, IFD_FORMAT_URATIONAL),
1014             new ExifTag(TAG_FOCAL_PLANE_Y_RESOLUTION, 41487, IFD_FORMAT_URATIONAL),
1015             new ExifTag(TAG_FOCAL_PLANE_RESOLUTION_UNIT, 41488, IFD_FORMAT_USHORT),
1016             new ExifTag(TAG_SUBJECT_LOCATION, 41492, IFD_FORMAT_USHORT),
1017             new ExifTag(TAG_EXPOSURE_INDEX, 41493, IFD_FORMAT_URATIONAL),
1018             new ExifTag(TAG_SENSING_METHOD, 41495, IFD_FORMAT_USHORT),
1019             new ExifTag(TAG_FILE_SOURCE, 41728, IFD_FORMAT_UNDEFINED),
1020             new ExifTag(TAG_SCENE_TYPE, 41729, IFD_FORMAT_UNDEFINED),
1021             new ExifTag(TAG_CFA_PATTERN, 41730, IFD_FORMAT_UNDEFINED),
1022             new ExifTag(TAG_CUSTOM_RENDERED, 41985, IFD_FORMAT_USHORT),
1023             new ExifTag(TAG_EXPOSURE_MODE, 41986, IFD_FORMAT_USHORT),
1024             new ExifTag(TAG_WHITE_BALANCE, 41987, IFD_FORMAT_USHORT),
1025             new ExifTag(TAG_DIGITAL_ZOOM_RATIO, 41988, IFD_FORMAT_URATIONAL),
1026             new ExifTag(TAG_FOCAL_LENGTH_IN_35MM_FILM, 41989, IFD_FORMAT_USHORT),
1027             new ExifTag(TAG_SCENE_CAPTURE_TYPE, 41990, IFD_FORMAT_USHORT),
1028             new ExifTag(TAG_GAIN_CONTROL, 41991, IFD_FORMAT_USHORT),
1029             new ExifTag(TAG_CONTRAST, 41992, IFD_FORMAT_USHORT),
1030             new ExifTag(TAG_SATURATION, 41993, IFD_FORMAT_USHORT),
1031             new ExifTag(TAG_SHARPNESS, 41994, IFD_FORMAT_USHORT),
1032             new ExifTag(TAG_DEVICE_SETTING_DESCRIPTION, 41995, IFD_FORMAT_UNDEFINED),
1033             new ExifTag(TAG_SUBJECT_DISTANCE_RANGE, 41996, IFD_FORMAT_USHORT),
1034             new ExifTag(TAG_IMAGE_UNIQUE_ID, 42016, IFD_FORMAT_STRING),
1035             new ExifTag(TAG_DNG_VERSION, 50706, IFD_FORMAT_BYTE),
1036             new ExifTag(TAG_DEFAULT_CROP_SIZE, 50720, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG)
1037     };
1038 
1039     // Primary image IFD GPS Info tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
1040     private static final ExifTag[] IFD_GPS_TAGS = new ExifTag[] {
1041             new ExifTag(TAG_GPS_VERSION_ID, 0, IFD_FORMAT_BYTE),
1042             new ExifTag(TAG_GPS_LATITUDE_REF, 1, IFD_FORMAT_STRING),
1043             new ExifTag(TAG_GPS_LATITUDE, 2, IFD_FORMAT_URATIONAL),
1044             new ExifTag(TAG_GPS_LONGITUDE_REF, 3, IFD_FORMAT_STRING),
1045             new ExifTag(TAG_GPS_LONGITUDE, 4, IFD_FORMAT_URATIONAL),
1046             new ExifTag(TAG_GPS_ALTITUDE_REF, 5, IFD_FORMAT_BYTE),
1047             new ExifTag(TAG_GPS_ALTITUDE, 6, IFD_FORMAT_URATIONAL),
1048             new ExifTag(TAG_GPS_TIMESTAMP, 7, IFD_FORMAT_URATIONAL),
1049             new ExifTag(TAG_GPS_SATELLITES, 8, IFD_FORMAT_STRING),
1050             new ExifTag(TAG_GPS_STATUS, 9, IFD_FORMAT_STRING),
1051             new ExifTag(TAG_GPS_MEASURE_MODE, 10, IFD_FORMAT_STRING),
1052             new ExifTag(TAG_GPS_DOP, 11, IFD_FORMAT_URATIONAL),
1053             new ExifTag(TAG_GPS_SPEED_REF, 12, IFD_FORMAT_STRING),
1054             new ExifTag(TAG_GPS_SPEED, 13, IFD_FORMAT_URATIONAL),
1055             new ExifTag(TAG_GPS_TRACK_REF, 14, IFD_FORMAT_STRING),
1056             new ExifTag(TAG_GPS_TRACK, 15, IFD_FORMAT_URATIONAL),
1057             new ExifTag(TAG_GPS_IMG_DIRECTION_REF, 16, IFD_FORMAT_STRING),
1058             new ExifTag(TAG_GPS_IMG_DIRECTION, 17, IFD_FORMAT_URATIONAL),
1059             new ExifTag(TAG_GPS_MAP_DATUM, 18, IFD_FORMAT_STRING),
1060             new ExifTag(TAG_GPS_DEST_LATITUDE_REF, 19, IFD_FORMAT_STRING),
1061             new ExifTag(TAG_GPS_DEST_LATITUDE, 20, IFD_FORMAT_URATIONAL),
1062             new ExifTag(TAG_GPS_DEST_LONGITUDE_REF, 21, IFD_FORMAT_STRING),
1063             new ExifTag(TAG_GPS_DEST_LONGITUDE, 22, IFD_FORMAT_URATIONAL),
1064             new ExifTag(TAG_GPS_DEST_BEARING_REF, 23, IFD_FORMAT_STRING),
1065             new ExifTag(TAG_GPS_DEST_BEARING, 24, IFD_FORMAT_URATIONAL),
1066             new ExifTag(TAG_GPS_DEST_DISTANCE_REF, 25, IFD_FORMAT_STRING),
1067             new ExifTag(TAG_GPS_DEST_DISTANCE, 26, IFD_FORMAT_URATIONAL),
1068             new ExifTag(TAG_GPS_PROCESSING_METHOD, 27, IFD_FORMAT_UNDEFINED),
1069             new ExifTag(TAG_GPS_AREA_INFORMATION, 28, IFD_FORMAT_UNDEFINED),
1070             new ExifTag(TAG_GPS_DATESTAMP, 29, IFD_FORMAT_STRING),
1071             new ExifTag(TAG_GPS_DIFFERENTIAL, 30, IFD_FORMAT_USHORT)
1072     };
1073     // Primary image IFD Interoperability tag (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
1074     private static final ExifTag[] IFD_INTEROPERABILITY_TAGS = new ExifTag[] {
1075             new ExifTag(TAG_INTEROPERABILITY_INDEX, 1, IFD_FORMAT_STRING)
1076     };
1077     // IFD Thumbnail tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
1078     private static final ExifTag[] IFD_THUMBNAIL_TAGS = new ExifTag[] {
1079             // For below two, see TIFF 6.0 Spec Section 3: Bilevel Images.
1080             new ExifTag(TAG_NEW_SUBFILE_TYPE, 254, IFD_FORMAT_ULONG),
1081             new ExifTag(TAG_SUBFILE_TYPE, 255, IFD_FORMAT_ULONG),
1082             new ExifTag(TAG_THUMBNAIL_IMAGE_WIDTH, 256, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
1083             new ExifTag(TAG_THUMBNAIL_IMAGE_LENGTH, 257, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
1084             new ExifTag(TAG_BITS_PER_SAMPLE, 258, IFD_FORMAT_USHORT),
1085             new ExifTag(TAG_COMPRESSION, 259, IFD_FORMAT_USHORT),
1086             new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262, IFD_FORMAT_USHORT),
1087             new ExifTag(TAG_IMAGE_DESCRIPTION, 270, IFD_FORMAT_STRING),
1088             new ExifTag(TAG_MAKE, 271, IFD_FORMAT_STRING),
1089             new ExifTag(TAG_MODEL, 272, IFD_FORMAT_STRING),
1090             new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
1091             new ExifTag(TAG_ORIENTATION, 274, IFD_FORMAT_USHORT),
1092             new ExifTag(TAG_SAMPLES_PER_PIXEL, 277, IFD_FORMAT_USHORT),
1093             new ExifTag(TAG_ROWS_PER_STRIP, 278, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
1094             new ExifTag(TAG_STRIP_BYTE_COUNTS, 279, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
1095             new ExifTag(TAG_X_RESOLUTION, 282, IFD_FORMAT_URATIONAL),
1096             new ExifTag(TAG_Y_RESOLUTION, 283, IFD_FORMAT_URATIONAL),
1097             new ExifTag(TAG_PLANAR_CONFIGURATION, 284, IFD_FORMAT_USHORT),
1098             new ExifTag(TAG_RESOLUTION_UNIT, 296, IFD_FORMAT_USHORT),
1099             new ExifTag(TAG_TRANSFER_FUNCTION, 301, IFD_FORMAT_USHORT),
1100             new ExifTag(TAG_SOFTWARE, 305, IFD_FORMAT_STRING),
1101             new ExifTag(TAG_DATETIME, 306, IFD_FORMAT_STRING),
1102             new ExifTag(TAG_ARTIST, 315, IFD_FORMAT_STRING),
1103             new ExifTag(TAG_WHITE_POINT, 318, IFD_FORMAT_URATIONAL),
1104             new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319, IFD_FORMAT_URATIONAL),
1105             // See Adobe PageMaker® 6.0 TIFF Technical Notes, Note 1.
1106             new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
1107             new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG),
1108             new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG),
1109             new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529, IFD_FORMAT_URATIONAL),
1110             new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
1111             new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
1112             new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
1113             new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
1114             new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
1115             new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
1116             new ExifTag(TAG_DNG_VERSION, 50706, IFD_FORMAT_BYTE),
1117             new ExifTag(TAG_DEFAULT_CROP_SIZE, 50720, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG)
1118     };
1119 
1120     // RAF file tag (See piex.cc line 372)
1121     private static final ExifTag TAG_RAF_IMAGE_SIZE =
1122             new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT);
1123 
1124     // ORF file tags (See http://www.exiv2.org/tags-olympus.html)
1125     private static final ExifTag[] ORF_MAKER_NOTE_TAGS = new ExifTag[] {
1126             new ExifTag(TAG_ORF_THUMBNAIL_IMAGE, 256, IFD_FORMAT_UNDEFINED),
1127             new ExifTag(TAG_ORF_CAMERA_SETTINGS_IFD_POINTER, 8224, IFD_FORMAT_ULONG),
1128             new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_ULONG)
1129     };
1130     private static final ExifTag[] ORF_CAMERA_SETTINGS_TAGS = new ExifTag[] {
1131             new ExifTag(TAG_ORF_PREVIEW_IMAGE_START, 257, IFD_FORMAT_ULONG),
1132             new ExifTag(TAG_ORF_PREVIEW_IMAGE_LENGTH, 258, IFD_FORMAT_ULONG)
1133     };
1134     private static final ExifTag[] ORF_IMAGE_PROCESSING_TAGS = new ExifTag[] {
1135             new ExifTag(TAG_ORF_ASPECT_FRAME, 4371, IFD_FORMAT_USHORT)
1136     };
1137     // PEF file tag (See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Pentax.html)
1138     private static final ExifTag[] PEF_TAGS = new ExifTag[] {
1139             new ExifTag(TAG_COLOR_SPACE, 55, IFD_FORMAT_USHORT)
1140     };
1141 
1142     // See JEITA CP-3451C Section 4.6.3: Exif-specific IFD.
1143     // The following values are used for indicating pointers to the other Image File Directories.
1144 
1145     // Indices of Exif Ifd tag groups
1146     /** @hide */
1147     @Retention(RetentionPolicy.SOURCE)
1148     @IntDef({IFD_TYPE_PRIMARY, IFD_TYPE_EXIF, IFD_TYPE_GPS, IFD_TYPE_INTEROPERABILITY,
1149             IFD_TYPE_THUMBNAIL, IFD_TYPE_PREVIEW, IFD_TYPE_ORF_MAKER_NOTE,
1150             IFD_TYPE_ORF_CAMERA_SETTINGS, IFD_TYPE_ORF_IMAGE_PROCESSING, IFD_TYPE_PEF})
1151     public @interface IfdType {}
1152 
1153     private static final int IFD_TYPE_PRIMARY = 0;
1154     private static final int IFD_TYPE_EXIF = 1;
1155     private static final int IFD_TYPE_GPS = 2;
1156     private static final int IFD_TYPE_INTEROPERABILITY = 3;
1157     private static final int IFD_TYPE_THUMBNAIL = 4;
1158     private static final int IFD_TYPE_PREVIEW = 5;
1159     private static final int IFD_TYPE_ORF_MAKER_NOTE = 6;
1160     private static final int IFD_TYPE_ORF_CAMERA_SETTINGS = 7;
1161     private static final int IFD_TYPE_ORF_IMAGE_PROCESSING = 8;
1162     private static final int IFD_TYPE_PEF = 9;
1163 
1164     // List of Exif tag groups
1165     private static final ExifTag[][] EXIF_TAGS = new ExifTag[][] {
1166             IFD_TIFF_TAGS, IFD_EXIF_TAGS, IFD_GPS_TAGS, IFD_INTEROPERABILITY_TAGS,
1167             IFD_THUMBNAIL_TAGS, IFD_TIFF_TAGS, ORF_MAKER_NOTE_TAGS, ORF_CAMERA_SETTINGS_TAGS,
1168             ORF_IMAGE_PROCESSING_TAGS, PEF_TAGS
1169     };
1170     // List of tags for pointing to the other image file directory offset.
1171     private static final ExifTag[] EXIF_POINTER_TAGS = new ExifTag[] {
1172             new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
1173             new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
1174             new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
1175             new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG),
1176             new ExifTag(TAG_ORF_CAMERA_SETTINGS_IFD_POINTER, 8224, IFD_FORMAT_BYTE),
1177             new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_BYTE)
1178     };
1179 
1180     // Tags for indicating the thumbnail offset and length
1181     private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG =
1182             new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG);
1183     private static final ExifTag JPEG_INTERCHANGE_FORMAT_LENGTH_TAG =
1184             new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG);
1185 
1186     // Mappings from tag number to tag name and each item represents one IFD tag group.
1187     @SuppressWarnings("unchecked")
1188     private static final HashMap<Integer, ExifTag>[] sExifTagMapsForReading =
1189             new HashMap[EXIF_TAGS.length];
1190     // Mappings from tag name to tag number and each item represents one IFD tag group.
1191     @SuppressWarnings("unchecked")
1192     private static final HashMap<String, ExifTag>[] sExifTagMapsForWriting =
1193             new HashMap[EXIF_TAGS.length];
1194     private static final HashSet<String> sTagSetForCompatibility = new HashSet<>(Arrays.asList(
1195             TAG_F_NUMBER, TAG_DIGITAL_ZOOM_RATIO, TAG_EXPOSURE_TIME, TAG_SUBJECT_DISTANCE,
1196             TAG_GPS_TIMESTAMP));
1197     // Mappings from tag number to IFD type for pointer tags.
1198     @SuppressWarnings("unchecked")
1199     private static final HashMap<Integer, Integer> sExifPointerTagMap = new HashMap();
1200 
1201     // See JPEG File Interchange Format Version 1.02.
1202     // The following values are defined for handling JPEG streams. In this implementation, we are
1203     // not only getting information from EXIF but also from some JPEG special segments such as
1204     // MARKER_COM for user comment and MARKER_SOFx for image width and height.
1205 
1206     private static final Charset ASCII = Charset.forName("US-ASCII");
1207     // Identifier for EXIF APP1 segment in JPEG
1208     private static final byte[] IDENTIFIER_EXIF_APP1 = "Exif\0\0".getBytes(ASCII);
1209     // JPEG segment markers, that each marker consumes two bytes beginning with 0xff and ending with
1210     // the indicator. There is no SOF4, SOF8, SOF16 markers in JPEG and SOFx markers indicates start
1211     // of frame(baseline DCT) and the image size info exists in its beginning part.
1212     private static final byte MARKER = (byte) 0xff;
1213     private static final byte MARKER_SOI = (byte) 0xd8;
1214     private static final byte MARKER_SOF0 = (byte) 0xc0;
1215     private static final byte MARKER_SOF1 = (byte) 0xc1;
1216     private static final byte MARKER_SOF2 = (byte) 0xc2;
1217     private static final byte MARKER_SOF3 = (byte) 0xc3;
1218     private static final byte MARKER_SOF5 = (byte) 0xc5;
1219     private static final byte MARKER_SOF6 = (byte) 0xc6;
1220     private static final byte MARKER_SOF7 = (byte) 0xc7;
1221     private static final byte MARKER_SOF9 = (byte) 0xc9;
1222     private static final byte MARKER_SOF10 = (byte) 0xca;
1223     private static final byte MARKER_SOF11 = (byte) 0xcb;
1224     private static final byte MARKER_SOF13 = (byte) 0xcd;
1225     private static final byte MARKER_SOF14 = (byte) 0xce;
1226     private static final byte MARKER_SOF15 = (byte) 0xcf;
1227     private static final byte MARKER_SOS = (byte) 0xda;
1228     private static final byte MARKER_APP1 = (byte) 0xe1;
1229     private static final byte MARKER_COM = (byte) 0xfe;
1230     private static final byte MARKER_EOI = (byte) 0xd9;
1231 
1232     // Supported Image File Types
1233     private static final int IMAGE_TYPE_UNKNOWN = 0;
1234     private static final int IMAGE_TYPE_ARW = 1;
1235     private static final int IMAGE_TYPE_CR2 = 2;
1236     private static final int IMAGE_TYPE_DNG = 3;
1237     private static final int IMAGE_TYPE_JPEG = 4;
1238     private static final int IMAGE_TYPE_NEF = 5;
1239     private static final int IMAGE_TYPE_NRW = 6;
1240     private static final int IMAGE_TYPE_ORF = 7;
1241     private static final int IMAGE_TYPE_PEF = 8;
1242     private static final int IMAGE_TYPE_RAF = 9;
1243     private static final int IMAGE_TYPE_RW2 = 10;
1244     private static final int IMAGE_TYPE_SRW = 11;
1245 
1246     static {
1247         sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
1248         sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
1249 
1250         // Build up the hash tables to look up Exif tags for reading Exif tags.
1251         for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
1252             sExifTagMapsForReading[ifdType] = new HashMap<>();
1253             sExifTagMapsForWriting[ifdType] = new HashMap<>();
1254             for (ExifTag tag : EXIF_TAGS[ifdType]) {
put(tag.number, tag)1255                 sExifTagMapsForReading[ifdType].put(tag.number, tag);
put(tag.name, tag)1256                 sExifTagMapsForWriting[ifdType].put(tag.name, tag);
1257             }
1258         }
1259 
1260         // Build up the hash table to look up Exif pointer tags.
sExifPointerTagMap.put(EXIF_POINTER_TAGS[0].number, IFD_TYPE_PREVIEW)1261         sExifPointerTagMap.put(EXIF_POINTER_TAGS[0].number, IFD_TYPE_PREVIEW); // 330
sExifPointerTagMap.put(EXIF_POINTER_TAGS[1].number, IFD_TYPE_EXIF)1262         sExifPointerTagMap.put(EXIF_POINTER_TAGS[1].number, IFD_TYPE_EXIF); // 34665
sExifPointerTagMap.put(EXIF_POINTER_TAGS[2].number, IFD_TYPE_GPS)1263         sExifPointerTagMap.put(EXIF_POINTER_TAGS[2].number, IFD_TYPE_GPS); // 34853
sExifPointerTagMap.put(EXIF_POINTER_TAGS[3].number, IFD_TYPE_INTEROPERABILITY)1264         sExifPointerTagMap.put(EXIF_POINTER_TAGS[3].number, IFD_TYPE_INTEROPERABILITY); // 40965
sExifPointerTagMap.put(EXIF_POINTER_TAGS[4].number, IFD_TYPE_ORF_CAMERA_SETTINGS)1265         sExifPointerTagMap.put(EXIF_POINTER_TAGS[4].number, IFD_TYPE_ORF_CAMERA_SETTINGS); // 8224
sExifPointerTagMap.put(EXIF_POINTER_TAGS[5].number, IFD_TYPE_ORF_IMAGE_PROCESSING)1266         sExifPointerTagMap.put(EXIF_POINTER_TAGS[5].number, IFD_TYPE_ORF_IMAGE_PROCESSING); // 8256
1267     }
1268 
1269     private final String mFilename;
1270     private final AssetManager.AssetInputStream mAssetInputStream;
1271     private int mMimeType;
1272     @SuppressWarnings("unchecked")
1273     private final HashMap<String, ExifAttribute>[] mAttributes = new HashMap[EXIF_TAGS.length];
1274     private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN;
1275     private boolean mHasThumbnail;
1276     // The following values used for indicating a thumbnail position.
1277     private int mThumbnailOffset;
1278     private int mThumbnailLength;
1279     private byte[] mThumbnailBytes;
1280     private int mThumbnailCompression;
1281     private int mExifOffset;
1282     private int mOrfMakerNoteOffset;
1283     private int mOrfThumbnailOffset;
1284     private int mOrfThumbnailLength;
1285     private int mRw2JpgFromRawOffset;
1286     private boolean mIsSupportedFile;
1287 
1288     // Pattern to check non zero timestamp
1289     private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
1290     // Pattern to check gps timestamp
1291     private static final Pattern sGpsTimestampPattern =
1292             Pattern.compile("^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$");
1293 
1294     /**
1295      * Reads Exif tags from the specified image file.
1296      */
ExifInterface(String filename)1297     public ExifInterface(String filename) throws IOException {
1298         if (filename == null) {
1299             throw new IllegalArgumentException("filename cannot be null");
1300         }
1301         FileInputStream in = null;
1302         mAssetInputStream = null;
1303         mFilename = filename;
1304         try {
1305             in = new FileInputStream(filename);
1306             loadAttributes(in);
1307         } finally {
1308             closeQuietly(in);
1309         }
1310     }
1311 
1312     /**
1313      * Reads Exif tags from the specified image input stream. Attribute mutation is not supported
1314      * for input streams. The given input stream will proceed its current position. Developers
1315      * should close the input stream after use. This constructor is not intended to be used with
1316      * an input stream that performs any networking operations.
1317      */
ExifInterface(InputStream inputStream)1318     public ExifInterface(InputStream inputStream) throws IOException {
1319         if (inputStream == null) {
1320             throw new IllegalArgumentException("inputStream cannot be null");
1321         }
1322         mFilename = null;
1323         if (inputStream instanceof AssetManager.AssetInputStream) {
1324             mAssetInputStream = (AssetManager.AssetInputStream) inputStream;
1325         } else {
1326             mAssetInputStream = null;
1327         }
1328         loadAttributes(inputStream);
1329     }
1330 
1331     /**
1332      * Returns the EXIF attribute of the specified tag or {@code null} if there is no such tag in
1333      * the image file.
1334      *
1335      * @param tag the name of the tag.
1336      */
getExifAttribute(String tag)1337     private ExifAttribute getExifAttribute(String tag) {
1338         // Retrieves all tag groups. The value from primary image tag group has a higher priority
1339         // than the value from the thumbnail tag group if there are more than one candidates.
1340         for (int i = 0; i < EXIF_TAGS.length; ++i) {
1341             ExifAttribute value = mAttributes[i].get(tag);
1342             if (value != null) {
1343                 return value;
1344             }
1345         }
1346         return null;
1347     }
1348 
1349     /**
1350      * Returns the value of the specified tag or {@code null} if there
1351      * is no such tag in the image file.
1352      *
1353      * @param tag the name of the tag.
1354      */
getAttribute(String tag)1355     public String getAttribute(String tag) {
1356         ExifAttribute attribute = getExifAttribute(tag);
1357         if (attribute != null) {
1358             if (!sTagSetForCompatibility.contains(tag)) {
1359                 return attribute.getStringValue(mExifByteOrder);
1360             }
1361             if (tag.equals(TAG_GPS_TIMESTAMP)) {
1362                 // Convert the rational values to the custom formats for backwards compatibility.
1363                 if (attribute.format != IFD_FORMAT_URATIONAL
1364                         && attribute.format != IFD_FORMAT_SRATIONAL) {
1365                     Log.w(TAG, "GPS Timestamp format is not rational. format=" + attribute.format);
1366                     return null;
1367                 }
1368                 Rational[] array = (Rational[]) attribute.getValue(mExifByteOrder);
1369                 if (array == null || array.length != 3) {
1370                     Log.w(TAG, "Invalid GPS Timestamp array. array=" + Arrays.toString(array));
1371                     return null;
1372                 }
1373                 return String.format("%02d:%02d:%02d",
1374                         (int) ((float) array[0].numerator / array[0].denominator),
1375                         (int) ((float) array[1].numerator / array[1].denominator),
1376                         (int) ((float) array[2].numerator / array[2].denominator));
1377             }
1378             try {
1379                 return Double.toString(attribute.getDoubleValue(mExifByteOrder));
1380             } catch (NumberFormatException e) {
1381                 return null;
1382             }
1383         }
1384         return null;
1385     }
1386 
1387     /**
1388      * Returns the integer value of the specified tag. If there is no such tag
1389      * in the image file or the value cannot be parsed as integer, return
1390      * <var>defaultValue</var>.
1391      *
1392      * @param tag the name of the tag.
1393      * @param defaultValue the value to return if the tag is not available.
1394      */
getAttributeInt(String tag, int defaultValue)1395     public int getAttributeInt(String tag, int defaultValue) {
1396         ExifAttribute exifAttribute = getExifAttribute(tag);
1397         if (exifAttribute == null) {
1398             return defaultValue;
1399         }
1400 
1401         try {
1402             return exifAttribute.getIntValue(mExifByteOrder);
1403         } catch (NumberFormatException e) {
1404             return defaultValue;
1405         }
1406     }
1407 
1408     /**
1409      * Returns the double value of the tag that is specified as rational or contains a
1410      * double-formatted value. If there is no such tag in the image file or the value cannot be
1411      * parsed as double, return <var>defaultValue</var>.
1412      *
1413      * @param tag the name of the tag.
1414      * @param defaultValue the value to return if the tag is not available.
1415      */
getAttributeDouble(String tag, double defaultValue)1416     public double getAttributeDouble(String tag, double defaultValue) {
1417         ExifAttribute exifAttribute = getExifAttribute(tag);
1418         if (exifAttribute == null) {
1419             return defaultValue;
1420         }
1421 
1422         try {
1423             return exifAttribute.getDoubleValue(mExifByteOrder);
1424         } catch (NumberFormatException e) {
1425             return defaultValue;
1426         }
1427     }
1428 
1429     /**
1430      * Set the value of the specified tag.
1431      *
1432      * @param tag the name of the tag.
1433      * @param value the value of the tag.
1434      */
setAttribute(String tag, String value)1435     public void setAttribute(String tag, String value) {
1436         // Convert the given value to rational values for backwards compatibility.
1437         if (value != null && sTagSetForCompatibility.contains(tag)) {
1438             if (tag.equals(TAG_GPS_TIMESTAMP)) {
1439                 Matcher m = sGpsTimestampPattern.matcher(value);
1440                 if (!m.find()) {
1441                     Log.w(TAG, "Invalid value for " + tag + " : " + value);
1442                     return;
1443                 }
1444                 value = Integer.parseInt(m.group(1)) + "/1," + Integer.parseInt(m.group(2)) + "/1,"
1445                         + Integer.parseInt(m.group(3)) + "/1";
1446             } else {
1447                 try {
1448                     double doubleValue = Double.parseDouble(value);
1449                     value = (long) (doubleValue * 10000L) + "/10000";
1450                 } catch (NumberFormatException e) {
1451                     Log.w(TAG, "Invalid value for " + tag + " : " + value);
1452                     return;
1453                 }
1454             }
1455         }
1456 
1457         for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1458             if (i == IFD_TYPE_THUMBNAIL && !mHasThumbnail) {
1459                 continue;
1460             }
1461             final ExifTag exifTag = sExifTagMapsForWriting[i].get(tag);
1462             if (exifTag != null) {
1463                 if (value == null) {
1464                     mAttributes[i].remove(tag);
1465                     continue;
1466                 }
1467                 Pair<Integer, Integer> guess = guessDataFormat(value);
1468                 int dataFormat;
1469                 if (exifTag.primaryFormat == guess.first || exifTag.primaryFormat == guess.second) {
1470                     dataFormat = exifTag.primaryFormat;
1471                 } else if (exifTag.secondaryFormat != -1 && (exifTag.secondaryFormat == guess.first
1472                         || exifTag.secondaryFormat == guess.second)) {
1473                     dataFormat = exifTag.secondaryFormat;
1474                 } else if (exifTag.primaryFormat == IFD_FORMAT_BYTE
1475                         || exifTag.primaryFormat == IFD_FORMAT_UNDEFINED
1476                         || exifTag.primaryFormat == IFD_FORMAT_STRING) {
1477                     dataFormat = exifTag.primaryFormat;
1478                 } else {
1479                     Log.w(TAG, "Given tag (" + tag + ") value didn't match with one of expected "
1480                             + "formats: " + IFD_FORMAT_NAMES[exifTag.primaryFormat]
1481                             + (exifTag.secondaryFormat == -1 ? "" : ", "
1482                             + IFD_FORMAT_NAMES[exifTag.secondaryFormat]) + " (guess: "
1483                             + IFD_FORMAT_NAMES[guess.first] + (guess.second == -1 ? "" : ", "
1484                             + IFD_FORMAT_NAMES[guess.second]) + ")");
1485                     continue;
1486                 }
1487                 switch (dataFormat) {
1488                     case IFD_FORMAT_BYTE: {
1489                         mAttributes[i].put(tag, ExifAttribute.createByte(value));
1490                         break;
1491                     }
1492                     case IFD_FORMAT_UNDEFINED:
1493                     case IFD_FORMAT_STRING: {
1494                         mAttributes[i].put(tag, ExifAttribute.createString(value));
1495                         break;
1496                     }
1497                     case IFD_FORMAT_USHORT: {
1498                         final String[] values = value.split(",");
1499                         final int[] intArray = new int[values.length];
1500                         for (int j = 0; j < values.length; ++j) {
1501                             intArray[j] = Integer.parseInt(values[j]);
1502                         }
1503                         mAttributes[i].put(tag,
1504                                 ExifAttribute.createUShort(intArray, mExifByteOrder));
1505                         break;
1506                     }
1507                     case IFD_FORMAT_SLONG: {
1508                         final String[] values = value.split(",");
1509                         final int[] intArray = new int[values.length];
1510                         for (int j = 0; j < values.length; ++j) {
1511                             intArray[j] = Integer.parseInt(values[j]);
1512                         }
1513                         mAttributes[i].put(tag,
1514                                 ExifAttribute.createSLong(intArray, mExifByteOrder));
1515                         break;
1516                     }
1517                     case IFD_FORMAT_ULONG: {
1518                         final String[] values = value.split(",");
1519                         final long[] longArray = new long[values.length];
1520                         for (int j = 0; j < values.length; ++j) {
1521                             longArray[j] = Long.parseLong(values[j]);
1522                         }
1523                         mAttributes[i].put(tag,
1524                                 ExifAttribute.createULong(longArray, mExifByteOrder));
1525                         break;
1526                     }
1527                     case IFD_FORMAT_URATIONAL: {
1528                         final String[] values = value.split(",");
1529                         final Rational[] rationalArray = new Rational[values.length];
1530                         for (int j = 0; j < values.length; ++j) {
1531                             final String[] numbers = values[j].split("/");
1532                             rationalArray[j] = new Rational((long) Double.parseDouble(numbers[0]),
1533                                     (long) Double.parseDouble(numbers[1]));
1534                         }
1535                         mAttributes[i].put(tag,
1536                                 ExifAttribute.createURational(rationalArray, mExifByteOrder));
1537                         break;
1538                     }
1539                     case IFD_FORMAT_SRATIONAL: {
1540                         final String[] values = value.split(",");
1541                         final Rational[] rationalArray = new Rational[values.length];
1542                         for (int j = 0; j < values.length; ++j) {
1543                             final String[] numbers = values[j].split("/");
1544                             rationalArray[j] = new Rational((long) Double.parseDouble(numbers[0]),
1545                                     (long) Double.parseDouble(numbers[1]));
1546                         }
1547                         mAttributes[i].put(tag,
1548                                 ExifAttribute.createSRational(rationalArray, mExifByteOrder));
1549                         break;
1550                     }
1551                     case IFD_FORMAT_DOUBLE: {
1552                         final String[] values = value.split(",");
1553                         final double[] doubleArray = new double[values.length];
1554                         for (int j = 0; j < values.length; ++j) {
1555                             doubleArray[j] = Double.parseDouble(values[j]);
1556                         }
1557                         mAttributes[i].put(tag,
1558                                 ExifAttribute.createDouble(doubleArray, mExifByteOrder));
1559                         break;
1560                     }
1561                     default:
1562                         Log.w(TAG, "Data format isn't one of expected formats: " + dataFormat);
1563                         continue;
1564                 }
1565             }
1566         }
1567     }
1568 
1569     /**
1570      * Update the values of the tags in the tag groups if any value for the tag already was stored.
1571      *
1572      * @param tag the name of the tag.
1573      * @param value the value of the tag in a form of {@link ExifAttribute}.
1574      * @return Returns {@code true} if updating is placed.
1575      */
updateAttribute(String tag, ExifAttribute value)1576     private boolean updateAttribute(String tag, ExifAttribute value) {
1577         boolean updated = false;
1578         for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1579             if (mAttributes[i].containsKey(tag)) {
1580                 mAttributes[i].put(tag, value);
1581                 updated = true;
1582             }
1583         }
1584         return updated;
1585     }
1586 
1587     /**
1588      * Remove any values of the specified tag.
1589      *
1590      * @param tag the name of the tag.
1591      */
removeAttribute(String tag)1592     private void removeAttribute(String tag) {
1593         for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1594             mAttributes[i].remove(tag);
1595         }
1596     }
1597 
1598     /**
1599      * This function decides which parser to read the image data according to the given input stream
1600      * type and the content of the input stream. In each case, it reads the first three bytes to
1601      * determine whether the image data format is JPEG or not.
1602      */
loadAttributes(@onNull InputStream in)1603     private void loadAttributes(@NonNull InputStream in) throws IOException {
1604         try {
1605             // Initialize mAttributes.
1606             for (int i = 0; i < EXIF_TAGS.length; ++i) {
1607                 mAttributes[i] = new HashMap<>();
1608             }
1609 
1610             // Check file type
1611             in = new BufferedInputStream(in, SIGNATURE_CHECK_SIZE);
1612             mMimeType = getMimeType((BufferedInputStream) in);
1613 
1614             // Create byte-ordered input stream
1615             ByteOrderedDataInputStream inputStream = new ByteOrderedDataInputStream(in);
1616 
1617             switch (mMimeType) {
1618                 case IMAGE_TYPE_JPEG: {
1619                     getJpegAttributes(inputStream, 0, IFD_TYPE_PRIMARY); // 0 is offset
1620                     break;
1621                 }
1622                 case IMAGE_TYPE_RAF: {
1623                     getRafAttributes(inputStream);
1624                     break;
1625                 }
1626                 case IMAGE_TYPE_ORF: {
1627                     getOrfAttributes(inputStream);
1628                     break;
1629                 }
1630                 case IMAGE_TYPE_RW2: {
1631                     getRw2Attributes(inputStream);
1632                     break;
1633                 }
1634                 case IMAGE_TYPE_ARW:
1635                 case IMAGE_TYPE_CR2:
1636                 case IMAGE_TYPE_DNG:
1637                 case IMAGE_TYPE_NEF:
1638                 case IMAGE_TYPE_NRW:
1639                 case IMAGE_TYPE_PEF:
1640                 case IMAGE_TYPE_SRW:
1641                 case IMAGE_TYPE_UNKNOWN: {
1642                     getRawAttributes(inputStream);
1643                     break;
1644                 }
1645                 default: {
1646                     break;
1647                 }
1648             }
1649             // Set thumbnail image offset and length
1650             setThumbnailData(inputStream);
1651             mIsSupportedFile = true;
1652         } catch (IOException e) {
1653             // Ignore exceptions in order to keep the compatibility with the old versions of
1654             // ExifInterface.
1655             mIsSupportedFile = false;
1656             if (DEBUG) {
1657                 Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file"
1658                         + "(ExifInterface supports JPEG and some RAW image formats only) "
1659                         + "or a corrupted JPEG file to ExifInterface.", e);
1660             }
1661         } finally {
1662             addDefaultValuesForCompatibility();
1663 
1664             if (DEBUG) {
1665                 printAttributes();
1666             }
1667         }
1668     }
1669 
1670     // Prints out attributes for debugging.
printAttributes()1671     private void printAttributes() {
1672         for (int i = 0; i < mAttributes.length; ++i) {
1673             Log.d(TAG, "The size of tag group[" + i + "]: " + mAttributes[i].size());
1674             for (Map.Entry<String, ExifAttribute> entry : mAttributes[i].entrySet()) {
1675                 final ExifAttribute tagValue = entry.getValue();
1676                 Log.d(TAG, "tagName: " + entry.getKey() + ", tagType: " + tagValue.toString()
1677                         + ", tagValue: '" + tagValue.getStringValue(mExifByteOrder) + "'");
1678             }
1679         }
1680     }
1681 
1682     /**
1683      * Save the tag data into the original image file. This is expensive because it involves
1684      * copying all the data from one file to another and deleting the old file and renaming the
1685      * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write
1686      * and make a single call rather than multiple calls for each attribute.
1687      * <p>
1688      * This method is only supported for JPEG files.
1689      * </p>
1690      */
saveAttributes()1691     public void saveAttributes() throws IOException {
1692         if (!mIsSupportedFile || mMimeType != IMAGE_TYPE_JPEG) {
1693             throw new IOException("ExifInterface only supports saving attributes on JPEG formats.");
1694         }
1695         if (mFilename == null) {
1696             throw new IOException(
1697                     "ExifInterface does not support saving attributes for the current input.");
1698         }
1699 
1700         // Keep the thumbnail in memory
1701         mThumbnailBytes = getThumbnail();
1702 
1703         File tempFile = new File(mFilename + ".tmp");
1704         File originalFile = new File(mFilename);
1705         if (!originalFile.renameTo(tempFile)) {
1706             throw new IOException("Could not rename to " + tempFile.getAbsolutePath());
1707         }
1708 
1709         FileInputStream in = null;
1710         FileOutputStream out = null;
1711         try {
1712             // Save the new file.
1713             in = new FileInputStream(tempFile);
1714             out = new FileOutputStream(mFilename);
1715             saveJpegAttributes(in, out);
1716         } finally {
1717             closeQuietly(in);
1718             closeQuietly(out);
1719             tempFile.delete();
1720         }
1721 
1722         // Discard the thumbnail in memory
1723         mThumbnailBytes = null;
1724     }
1725 
1726     /**
1727      * Returns true if the image file has a thumbnail.
1728      */
hasThumbnail()1729     public boolean hasThumbnail() {
1730         return mHasThumbnail;
1731     }
1732 
1733     /**
1734      * Returns the JPEG compressed thumbnail inside the image file, or {@code null} if there is no
1735      * JPEG compressed thumbnail.
1736      * The returned data can be decoded using
1737      * {@link android.graphics.BitmapFactory#decodeByteArray(byte[],int,int)}
1738      */
getThumbnail()1739     public byte[] getThumbnail() {
1740         if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) {
1741             return getThumbnailBytes();
1742         }
1743         return null;
1744     }
1745 
1746     /**
1747      * Returns the thumbnail bytes inside the image file, regardless of the compression type of the
1748      * thumbnail image.
1749      */
getThumbnailBytes()1750     public byte[] getThumbnailBytes() {
1751         if (!mHasThumbnail) {
1752             return null;
1753         }
1754         if (mThumbnailBytes != null) {
1755             return mThumbnailBytes;
1756         }
1757 
1758         // Read the thumbnail.
1759         InputStream in = null;
1760         try {
1761             if (mAssetInputStream != null) {
1762                 in = mAssetInputStream;
1763                 if (in.markSupported()) {
1764                     in.reset();
1765                 } else {
1766                     Log.d(TAG, "Cannot read thumbnail from inputstream without mark/reset support");
1767                     return null;
1768                 }
1769             } else if (mFilename != null) {
1770                 in = new FileInputStream(mFilename);
1771             }
1772             if (in == null) {
1773                 // Should not be reached this.
1774                 throw new FileNotFoundException();
1775             }
1776             if (in.skip(mThumbnailOffset) != mThumbnailOffset) {
1777                 throw new IOException("Corrupted image");
1778             }
1779             byte[] buffer = new byte[mThumbnailLength];
1780             if (in.read(buffer) != mThumbnailLength) {
1781                 throw new IOException("Corrupted image");
1782             }
1783             mThumbnailBytes = buffer;
1784             return buffer;
1785         } catch (IOException e) {
1786             // Couldn't get a thumbnail image.
1787             Log.d(TAG, "Encountered exception while getting thumbnail", e);
1788         } finally {
1789             closeQuietly(in);
1790         }
1791         return null;
1792     }
1793 
1794     /**
1795      * Creates and returns a Bitmap object of the thumbnail image based on the byte array and the
1796      * thumbnail compression value, or {@code null} if the compression type is unsupported.
1797      */
getThumbnailBitmap()1798     public Bitmap getThumbnailBitmap() {
1799         if (!mHasThumbnail) {
1800             return null;
1801         } else if (mThumbnailBytes == null) {
1802             mThumbnailBytes = getThumbnailBytes();
1803         }
1804 
1805         if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) {
1806             return BitmapFactory.decodeByteArray(mThumbnailBytes, 0, mThumbnailLength);
1807         } else if (mThumbnailCompression == DATA_UNCOMPRESSED) {
1808             int[] rgbValues = new int[mThumbnailBytes.length / 3];
1809             byte alpha = (byte) 0xff000000;
1810             for (int i = 0; i < rgbValues.length; i++) {
1811                 rgbValues[i] = alpha + (mThumbnailBytes[3 * i] << 16)
1812                         + (mThumbnailBytes[3 * i + 1] << 8) + mThumbnailBytes[3 * i + 2];
1813             }
1814 
1815             ExifAttribute imageLengthAttribute =
1816                     (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_LENGTH);
1817             ExifAttribute imageWidthAttribute =
1818                     (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_WIDTH);
1819             if (imageLengthAttribute != null && imageWidthAttribute != null) {
1820                 int imageLength = imageLengthAttribute.getIntValue(mExifByteOrder);
1821                 int imageWidth = imageWidthAttribute.getIntValue(mExifByteOrder);
1822                 return Bitmap.createBitmap(
1823                         rgbValues, imageWidth, imageLength, Bitmap.Config.ARGB_8888);
1824             }
1825         }
1826         return null;
1827     }
1828 
1829     /**
1830      * Returns true if thumbnail image is JPEG Compressed, or false if either thumbnail image does
1831      * not exist or thumbnail image is uncompressed.
1832      */
isThumbnailCompressed()1833     public boolean isThumbnailCompressed() {
1834         return mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED;
1835     }
1836 
1837     /**
1838      * Returns the offset and length of thumbnail inside the image file, or
1839      * {@code null} if there is no thumbnail.
1840      *
1841      * @return two-element array, the offset in the first value, and length in
1842      *         the second, or {@code null} if no thumbnail was found.
1843      */
getThumbnailRange()1844     public long[] getThumbnailRange() {
1845         if (!mHasThumbnail) {
1846             return null;
1847         }
1848 
1849         long[] range = new long[2];
1850         range[0] = mThumbnailOffset;
1851         range[1] = mThumbnailLength;
1852 
1853         return range;
1854     }
1855 
1856     /**
1857      * Stores the latitude and longitude value in a float array. The first element is the latitude,
1858      * and the second element is the longitude. Returns false if the Exif tags are not available.
1859      *
1860      * @deprecated Use {@link #getLatLong()} instead.
1861      */
1862     @Deprecated
getLatLong(float output[])1863     public boolean getLatLong(float output[]) {
1864         double[] latLong = getLatLong();
1865         if (latLong == null) {
1866             return false;
1867         }
1868 
1869         output[0] = (float) latLong[0];
1870         output[1] = (float) latLong[1];
1871         return true;
1872     }
1873 
1874     /**
1875      * Gets the latitude and longitude values.
1876      * <p>
1877      * If there are valid latitude and longitude values in the image, this method returns a double
1878      * array where the first element is the latitude and the second element is the longitude.
1879      * Otherwise, it returns null.
1880      */
getLatLong()1881     public double[] getLatLong() {
1882         String latValue = getAttribute(TAG_GPS_LATITUDE);
1883         String latRef = getAttribute(TAG_GPS_LATITUDE_REF);
1884         String lngValue = getAttribute(TAG_GPS_LONGITUDE);
1885         String lngRef = getAttribute(TAG_GPS_LONGITUDE_REF);
1886 
1887         if (latValue != null && latRef != null && lngValue != null && lngRef != null) {
1888             try {
1889                 double latitude = convertRationalLatLonToDouble(latValue, latRef);
1890                 double longitude = convertRationalLatLonToDouble(lngValue, lngRef);
1891                 return new double[] {latitude, longitude};
1892             } catch (IllegalArgumentException e) {
1893                 Log.w(TAG, "Latitude/longitude values are not parseable. " +
1894                         String.format("latValue=%s, latRef=%s, lngValue=%s, lngRef=%s",
1895                                 latValue, latRef, lngValue, lngRef));
1896             }
1897         }
1898         return null;
1899     }
1900 
1901     /**
1902      * Sets the latitude and longitude values.
1903      *
1904      * @param latitude the decimal value of latitude. Must be a valid double value between -90.0 and
1905      *                 90.0.
1906      * @param longitude the decimal value of longitude. Must be a valid double value between -180.0
1907      *                  and 180.0.
1908      * @throws IllegalArgumentException If {@code latitude} or {@code longitude} is outside the
1909      *                                  specified range.
1910      */
setLatLong(double latitude, double longitude)1911     public void setLatLong(double latitude, double longitude) {
1912         if (latitude < -90.0 || latitude > 90.0 || Double.isNaN(latitude)) {
1913             throw new IllegalArgumentException("Latitude value " + latitude + " is not valid.");
1914         }
1915         if (longitude < -180.0 || longitude > 180.0 || Double.isNaN(longitude)) {
1916             throw new IllegalArgumentException("Longitude value " + longitude + " is not valid.");
1917         }
1918         setAttribute(TAG_GPS_LATITUDE_REF, latitude >= 0 ? "N" : "S");
1919         setAttribute(TAG_GPS_LATITUDE, convertDecimalDegree(Math.abs(latitude)));
1920         setAttribute(TAG_GPS_LONGITUDE_REF, longitude >= 0 ? "E" : "W");
1921         setAttribute(TAG_GPS_LONGITUDE, convertDecimalDegree(Math.abs(longitude)));
1922     }
1923 
1924     /**
1925      * Return the altitude in meters. If the exif tag does not exist, return
1926      * <var>defaultValue</var>.
1927      *
1928      * @param defaultValue the value to return if the tag is not available.
1929      */
getAltitude(double defaultValue)1930     public double getAltitude(double defaultValue) {
1931         double altitude = getAttributeDouble(TAG_GPS_ALTITUDE, -1);
1932         int ref = getAttributeInt(TAG_GPS_ALTITUDE_REF, -1);
1933 
1934         if (altitude >= 0 && ref >= 0) {
1935             return (altitude * ((ref == 1) ? -1 : 1));
1936         } else {
1937             return defaultValue;
1938         }
1939     }
1940 
1941     /**
1942      * Returns number of milliseconds since Jan. 1, 1970, midnight local time.
1943      * Returns -1 if the date time information if not available.
1944      * @hide
1945      */
getDateTime()1946     public long getDateTime() {
1947         String dateTimeString = getAttribute(TAG_DATETIME);
1948         if (dateTimeString == null
1949                 || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
1950 
1951         ParsePosition pos = new ParsePosition(0);
1952         try {
1953             // The exif field is in local time. Parsing it as if it is UTC will yield time
1954             // since 1/1/1970 local time
1955             Date datetime = sFormatter.parse(dateTimeString, pos);
1956             if (datetime == null) return -1;
1957             long msecs = datetime.getTime();
1958 
1959             String subSecs = getAttribute(TAG_SUBSEC_TIME);
1960             if (subSecs != null) {
1961                 try {
1962                     long sub = Long.parseLong(subSecs);
1963                     while (sub > 1000) {
1964                         sub /= 10;
1965                     }
1966                     msecs += sub;
1967                 } catch (NumberFormatException e) {
1968                     // Ignored
1969                 }
1970             }
1971             return msecs;
1972         } catch (IllegalArgumentException e) {
1973             return -1;
1974         }
1975     }
1976 
1977     /**
1978      * Returns number of milliseconds since Jan. 1, 1970, midnight UTC.
1979      * Returns -1 if the date time information if not available.
1980      * @hide
1981      */
getGpsDateTime()1982     public long getGpsDateTime() {
1983         String date = getAttribute(TAG_GPS_DATESTAMP);
1984         String time = getAttribute(TAG_GPS_TIMESTAMP);
1985         if (date == null || time == null
1986                 || (!sNonZeroTimePattern.matcher(date).matches()
1987                 && !sNonZeroTimePattern.matcher(time).matches())) {
1988             return -1;
1989         }
1990 
1991         String dateTimeString = date + ' ' + time;
1992 
1993         ParsePosition pos = new ParsePosition(0);
1994         try {
1995             Date datetime = sFormatter.parse(dateTimeString, pos);
1996             if (datetime == null) return -1;
1997             return datetime.getTime();
1998         } catch (IllegalArgumentException e) {
1999             return -1;
2000         }
2001     }
2002 
convertRationalLatLonToDouble(String rationalString, String ref)2003     private static double convertRationalLatLonToDouble(String rationalString, String ref) {
2004         try {
2005             String [] parts = rationalString.split(",");
2006 
2007             String [] pair;
2008             pair = parts[0].split("/");
2009             double degrees = Double.parseDouble(pair[0].trim())
2010                     / Double.parseDouble(pair[1].trim());
2011 
2012             pair = parts[1].split("/");
2013             double minutes = Double.parseDouble(pair[0].trim())
2014                     / Double.parseDouble(pair[1].trim());
2015 
2016             pair = parts[2].split("/");
2017             double seconds = Double.parseDouble(pair[0].trim())
2018                     / Double.parseDouble(pair[1].trim());
2019 
2020             double result = degrees + (minutes / 60.0) + (seconds / 3600.0);
2021             if ((ref.equals("S") || ref.equals("W"))) {
2022                 return -result;
2023             } else if (ref.equals("N") || ref.equals("E")) {
2024                 return result;
2025             } else {
2026                 // Not valid
2027                 throw new IllegalArgumentException();
2028             }
2029         } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
2030             // Not valid
2031             throw new IllegalArgumentException();
2032         }
2033     }
2034 
convertDecimalDegree(double decimalDegree)2035     private String convertDecimalDegree(double decimalDegree) {
2036         long degrees = (long) decimalDegree;
2037         long minutes = (long) ((decimalDegree - degrees) * 60.0);
2038         long seconds = Math.round((decimalDegree - degrees - minutes / 60.0) * 3600.0 * 1e7);
2039         return degrees + "/1," + minutes + "/1," + seconds + "/10000000";
2040     }
2041 
2042     // Checks the type of image file
getMimeType(BufferedInputStream in)2043     private int getMimeType(BufferedInputStream in) throws IOException {
2044         in.mark(SIGNATURE_CHECK_SIZE);
2045         byte[] signatureCheckBytes = new byte[SIGNATURE_CHECK_SIZE];
2046         if (in.read(signatureCheckBytes) != SIGNATURE_CHECK_SIZE) {
2047             throw new EOFException();
2048         }
2049         in.reset();
2050         if (isJpegFormat(signatureCheckBytes)) {
2051             return IMAGE_TYPE_JPEG;
2052         } else if (isRafFormat(signatureCheckBytes)) {
2053             return IMAGE_TYPE_RAF;
2054         } else if (isOrfFormat(signatureCheckBytes)) {
2055             return IMAGE_TYPE_ORF;
2056         } else if (isRw2Format(signatureCheckBytes)) {
2057             return IMAGE_TYPE_RW2;
2058         }
2059         // Certain file formats (PEF) are identified in readImageFileDirectory()
2060         return IMAGE_TYPE_UNKNOWN;
2061     }
2062 
2063     /**
2064      * This method looks at the first 3 bytes to determine if this file is a JPEG file.
2065      * See http://www.media.mit.edu/pia/Research/deepview/exif.html, "JPEG format and Marker"
2066      */
isJpegFormat(byte[] signatureCheckBytes)2067     private static boolean isJpegFormat(byte[] signatureCheckBytes) throws IOException {
2068         for (int i = 0; i < JPEG_SIGNATURE.length; i++) {
2069             if (signatureCheckBytes[i] != JPEG_SIGNATURE[i]) {
2070                 return false;
2071             }
2072         }
2073         return true;
2074     }
2075 
2076     /**
2077      * This method looks at the first 15 bytes to determine if this file is a RAF file.
2078      * There is no official specification for RAF files from Fuji, but there is an online archive of
2079      * image file specifications:
2080      * http://fileformats.archiveteam.org/wiki/Fujifilm_RAF
2081      */
isRafFormat(byte[] signatureCheckBytes)2082     private boolean isRafFormat(byte[] signatureCheckBytes) throws IOException {
2083         byte[] rafSignatureBytes = RAF_SIGNATURE.getBytes(Charset.defaultCharset());
2084         for (int i = 0; i < rafSignatureBytes.length; i++) {
2085             if (signatureCheckBytes[i] != rafSignatureBytes[i]) {
2086                 return false;
2087             }
2088         }
2089         return true;
2090     }
2091 
2092     /**
2093      * ORF has a similar structure to TIFF but it contains a different signature at the TIFF Header.
2094      * This method looks at the 2 bytes following the Byte Order bytes to determine if this file is
2095      * an ORF file.
2096      * There is no official specification for ORF files from Olympus, but there is an online archive
2097      * of image file specifications:
2098      * http://fileformats.archiveteam.org/wiki/Olympus_ORF
2099      */
isOrfFormat(byte[] signatureCheckBytes)2100     private boolean isOrfFormat(byte[] signatureCheckBytes) throws IOException {
2101         ByteOrderedDataInputStream signatureInputStream =
2102                 new ByteOrderedDataInputStream(signatureCheckBytes);
2103         // Read byte order
2104         mExifByteOrder = readByteOrder(signatureInputStream);
2105         // Set byte order
2106         signatureInputStream.setByteOrder(mExifByteOrder);
2107 
2108         short orfSignature = signatureInputStream.readShort();
2109         signatureInputStream.close();
2110         return orfSignature == ORF_SIGNATURE_1 || orfSignature == ORF_SIGNATURE_2;
2111     }
2112 
2113     /**
2114      * RW2 is TIFF-based, but stores 0x55 signature byte instead of 0x42 at the header
2115      * See http://lclevy.free.fr/raw/
2116      */
isRw2Format(byte[] signatureCheckBytes)2117     private boolean isRw2Format(byte[] signatureCheckBytes) throws IOException {
2118         ByteOrderedDataInputStream signatureInputStream =
2119                 new ByteOrderedDataInputStream(signatureCheckBytes);
2120         // Read byte order
2121         mExifByteOrder = readByteOrder(signatureInputStream);
2122         // Set byte order
2123         signatureInputStream.setByteOrder(mExifByteOrder);
2124 
2125         short signatureByte = signatureInputStream.readShort();
2126         signatureInputStream.close();
2127         return signatureByte == RW2_SIGNATURE;
2128     }
2129 
2130     /**
2131      * Loads EXIF attributes from a JPEG input stream.
2132      *
2133      * @param in The input stream that starts with the JPEG data.
2134      * @param jpegOffset The offset value in input stream for JPEG data.
2135      * @param imageType The image type from which to retrieve metadata. Use IFD_TYPE_PRIMARY for
2136      *                   primary image, IFD_TYPE_PREVIEW for preview image, and
2137      *                   IFD_TYPE_THUMBNAIL for thumbnail image.
2138      * @throws IOException If the data contains invalid JPEG markers, offsets, or length values.
2139      */
getJpegAttributes(ByteOrderedDataInputStream in, int jpegOffset, int imageType)2140     private void getJpegAttributes(ByteOrderedDataInputStream in, int jpegOffset, int imageType)
2141             throws IOException {
2142         // See JPEG File Interchange Format Specification, "JFIF Specification"
2143         if (DEBUG) {
2144             Log.d(TAG, "getJpegAttributes starting with: " + in);
2145         }
2146 
2147         // JPEG uses Big Endian by default. See https://people.cs.umass.edu/~verts/cs32/endian.html
2148         in.setByteOrder(ByteOrder.BIG_ENDIAN);
2149 
2150         // Skip to JPEG data
2151         in.seek(jpegOffset);
2152         int bytesRead = jpegOffset;
2153 
2154         byte marker;
2155         if ((marker = in.readByte()) != MARKER) {
2156             throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
2157         }
2158         ++bytesRead;
2159         if (in.readByte() != MARKER_SOI) {
2160             throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
2161         }
2162         ++bytesRead;
2163         while (true) {
2164             marker = in.readByte();
2165             if (marker != MARKER) {
2166                 throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff));
2167             }
2168             ++bytesRead;
2169             marker = in.readByte();
2170             if (DEBUG) {
2171                 Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff));
2172             }
2173             ++bytesRead;
2174 
2175             // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and
2176             // the image data will terminate right after.
2177             if (marker == MARKER_EOI || marker == MARKER_SOS) {
2178                 break;
2179             }
2180             int length = in.readUnsignedShort() - 2;
2181             bytesRead += 2;
2182             if (DEBUG) {
2183                 Log.d(TAG, "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: "
2184                         + (length + 2) + ")");
2185             }
2186             if (length < 0) {
2187                 throw new IOException("Invalid length");
2188             }
2189             switch (marker) {
2190                 case MARKER_APP1: {
2191                     if (DEBUG) {
2192                         Log.d(TAG, "MARKER_APP1");
2193                     }
2194                     if (length < 6) {
2195                         // Skip if it's not an EXIF APP1 segment.
2196                         break;
2197                     }
2198                     byte[] identifier = new byte[6];
2199                     if (in.read(identifier) != 6) {
2200                         throw new IOException("Invalid exif");
2201                     }
2202                     bytesRead += 6;
2203                     length -= 6;
2204                     if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
2205                         // Skip if it's not an EXIF APP1 segment.
2206                         break;
2207                     }
2208                     if (length <= 0) {
2209                         throw new IOException("Invalid exif");
2210                     }
2211                     if (DEBUG) {
2212                         Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")");
2213                     }
2214                     // Save offset values for createJpegThumbnailBitmap() function
2215                     mExifOffset = bytesRead;
2216 
2217                     byte[] bytes = new byte[length];
2218                     if (in.read(bytes) != length) {
2219                         throw new IOException("Invalid exif");
2220                     }
2221                     bytesRead += length;
2222                     length = 0;
2223 
2224                     readExifSegment(bytes, imageType);
2225                     break;
2226                 }
2227 
2228                 case MARKER_COM: {
2229                     byte[] bytes = new byte[length];
2230                     if (in.read(bytes) != length) {
2231                         throw new IOException("Invalid exif");
2232                     }
2233                     length = 0;
2234                     if (getAttribute(TAG_USER_COMMENT) == null) {
2235                         mAttributes[IFD_TYPE_EXIF].put(TAG_USER_COMMENT, ExifAttribute.createString(
2236                                 new String(bytes, ASCII)));
2237                     }
2238                     break;
2239                 }
2240 
2241                 case MARKER_SOF0:
2242                 case MARKER_SOF1:
2243                 case MARKER_SOF2:
2244                 case MARKER_SOF3:
2245                 case MARKER_SOF5:
2246                 case MARKER_SOF6:
2247                 case MARKER_SOF7:
2248                 case MARKER_SOF9:
2249                 case MARKER_SOF10:
2250                 case MARKER_SOF11:
2251                 case MARKER_SOF13:
2252                 case MARKER_SOF14:
2253                 case MARKER_SOF15: {
2254                     if (in.skipBytes(1) != 1) {
2255                         throw new IOException("Invalid SOFx");
2256                     }
2257                     mAttributes[imageType].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong(
2258                             in.readUnsignedShort(), mExifByteOrder));
2259                     mAttributes[imageType].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong(
2260                             in.readUnsignedShort(), mExifByteOrder));
2261                     length -= 5;
2262                     break;
2263                 }
2264 
2265                 default: {
2266                     break;
2267                 }
2268             }
2269             if (length < 0) {
2270                 throw new IOException("Invalid length");
2271             }
2272             if (in.skipBytes(length) != length) {
2273                 throw new IOException("Invalid JPEG segment");
2274             }
2275             bytesRead += length;
2276         }
2277         // Restore original byte order
2278         in.setByteOrder(mExifByteOrder);
2279     }
2280 
getRawAttributes(ByteOrderedDataInputStream in)2281     private void getRawAttributes(ByteOrderedDataInputStream in) throws IOException {
2282         // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
2283         parseTiffHeaders(in, in.available());
2284 
2285         // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6.
2286         readImageFileDirectory(in, IFD_TYPE_PRIMARY);
2287 
2288         // Update ImageLength/Width tags for all image data.
2289         updateImageSizeValues(in, IFD_TYPE_PRIMARY);
2290         updateImageSizeValues(in, IFD_TYPE_PREVIEW);
2291         updateImageSizeValues(in, IFD_TYPE_THUMBNAIL);
2292 
2293         // Check if each image data is in valid position.
2294         validateImages(in);
2295 
2296         if (mMimeType == IMAGE_TYPE_PEF) {
2297             // PEF files contain a MakerNote data, which contains the data for ColorSpace tag.
2298             // See http://lclevy.free.fr/raw/ and piex.cc PefGetPreviewData()
2299             ExifAttribute makerNoteAttribute =
2300                     (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE);
2301             if (makerNoteAttribute != null) {
2302                 // Create an ordered DataInputStream for MakerNote
2303                 ByteOrderedDataInputStream makerNoteDataInputStream =
2304                         new ByteOrderedDataInputStream(makerNoteAttribute.bytes);
2305                 makerNoteDataInputStream.setByteOrder(mExifByteOrder);
2306 
2307                 // Seek to MakerNote data
2308                 makerNoteDataInputStream.seek(PEF_MAKER_NOTE_SKIP_SIZE);
2309 
2310                 // Read IFD data from MakerNote
2311                 readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_PEF);
2312 
2313                 // Update ColorSpace tag
2314                 ExifAttribute colorSpaceAttribute =
2315                         (ExifAttribute) mAttributes[IFD_TYPE_PEF].get(TAG_COLOR_SPACE);
2316                 if (colorSpaceAttribute != null) {
2317                     mAttributes[IFD_TYPE_EXIF].put(TAG_COLOR_SPACE, colorSpaceAttribute);
2318                 }
2319             }
2320         }
2321     }
2322 
2323     /**
2324      * RAF files contains a JPEG and a CFA data.
2325      * The JPEG contains two images, a preview and a thumbnail, while the CFA contains a RAW image.
2326      * This method looks at the first 160 bytes of a RAF file to retrieve the offset and length
2327      * values for the JPEG and CFA data.
2328      * Using that data, it parses the JPEG data to retrieve the preview and thumbnail image data,
2329      * then parses the CFA metadata to retrieve the primary image length/width values.
2330      * For data format details, see http://fileformats.archiveteam.org/wiki/Fujifilm_RAF
2331      */
getRafAttributes(ByteOrderedDataInputStream in)2332     private void getRafAttributes(ByteOrderedDataInputStream in) throws IOException {
2333         // Retrieve offset & length values
2334         in.skipBytes(RAF_OFFSET_TO_JPEG_IMAGE_OFFSET);
2335         byte[] jpegOffsetBytes = new byte[4];
2336         byte[] cfaHeaderOffsetBytes = new byte[4];
2337         in.read(jpegOffsetBytes);
2338         // Skip JPEG length value since it is not needed
2339         in.skipBytes(RAF_JPEG_LENGTH_VALUE_SIZE);
2340         in.read(cfaHeaderOffsetBytes);
2341         int rafJpegOffset = ByteBuffer.wrap(jpegOffsetBytes).getInt();
2342         int rafCfaHeaderOffset = ByteBuffer.wrap(cfaHeaderOffsetBytes).getInt();
2343 
2344         // Retrieve JPEG image metadata
2345         getJpegAttributes(in, rafJpegOffset, IFD_TYPE_PREVIEW);
2346 
2347         // Skip to CFA header offset.
2348         in.seek(rafCfaHeaderOffset);
2349 
2350         // Retrieve primary image length/width values, if TAG_RAF_IMAGE_SIZE exists
2351         in.setByteOrder(ByteOrder.BIG_ENDIAN);
2352         int numberOfDirectoryEntry = in.readInt();
2353         if (DEBUG) {
2354             Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
2355         }
2356         // CFA stores some metadata about the RAW image. Since CFA uses proprietary tags, can only
2357         // find and retrieve image size information tags, while skipping others.
2358         // See piex.cc RafGetDimension()
2359         for (int i = 0; i < numberOfDirectoryEntry; ++i) {
2360             int tagNumber = in.readUnsignedShort();
2361             int numberOfBytes = in.readUnsignedShort();
2362             if (tagNumber == TAG_RAF_IMAGE_SIZE.number) {
2363                 int imageLength = in.readShort();
2364                 int imageWidth = in.readShort();
2365                 ExifAttribute imageLengthAttribute =
2366                         ExifAttribute.createUShort(imageLength, mExifByteOrder);
2367                 ExifAttribute imageWidthAttribute =
2368                         ExifAttribute.createUShort(imageWidth, mExifByteOrder);
2369                 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, imageLengthAttribute);
2370                 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, imageWidthAttribute);
2371                 if (DEBUG) {
2372                     Log.d(TAG, "Updated to length: " + imageLength + ", width: " + imageWidth);
2373                 }
2374                 return;
2375             }
2376             in.skipBytes(numberOfBytes);
2377         }
2378     }
2379 
2380     /**
2381      * ORF files contains a primary image data and a MakerNote data that contains preview/thumbnail
2382      * images. Both data takes the form of IFDs and can therefore be read with the
2383      * readImageFileDirectory() method.
2384      * This method reads all the necessary data and updates the primary/preview/thumbnail image
2385      * information according to the GetOlympusPreviewImage() method in piex.cc.
2386      * For data format details, see the following:
2387      * http://fileformats.archiveteam.org/wiki/Olympus_ORF
2388      * https://libopenraw.freedesktop.org/wiki/Olympus_ORF
2389      */
getOrfAttributes(ByteOrderedDataInputStream in)2390     private void getOrfAttributes(ByteOrderedDataInputStream in) throws IOException {
2391         // Retrieve primary image data
2392         // Other Exif data will be located in the Makernote.
2393         getRawAttributes(in);
2394 
2395         // Additionally retrieve preview/thumbnail information from MakerNote tag, which contains
2396         // proprietary tags and therefore does not have offical documentation
2397         // See GetOlympusPreviewImage() in piex.cc & http://www.exiv2.org/tags-olympus.html
2398         ExifAttribute makerNoteAttribute =
2399                 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE);
2400         if (makerNoteAttribute != null) {
2401             // Create an ordered DataInputStream for MakerNote
2402             ByteOrderedDataInputStream makerNoteDataInputStream =
2403                     new ByteOrderedDataInputStream(makerNoteAttribute.bytes);
2404             makerNoteDataInputStream.setByteOrder(mExifByteOrder);
2405 
2406             // There are two types of headers for Olympus MakerNotes
2407             // See http://www.exiv2.org/makernote.html#R1
2408             byte[] makerNoteHeader1Bytes = new byte[ORF_MAKER_NOTE_HEADER_1.length];
2409             makerNoteDataInputStream.readFully(makerNoteHeader1Bytes);
2410             makerNoteDataInputStream.seek(0);
2411             byte[] makerNoteHeader2Bytes = new byte[ORF_MAKER_NOTE_HEADER_2.length];
2412             makerNoteDataInputStream.readFully(makerNoteHeader2Bytes);
2413             // Skip the corresponding amount of bytes for each header type
2414             if (Arrays.equals(makerNoteHeader1Bytes, ORF_MAKER_NOTE_HEADER_1)) {
2415                 makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_1_SIZE);
2416             } else if (Arrays.equals(makerNoteHeader2Bytes, ORF_MAKER_NOTE_HEADER_2)) {
2417                 makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_2_SIZE);
2418             }
2419 
2420             // Read IFD data from MakerNote
2421             readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_ORF_MAKER_NOTE);
2422 
2423             // Retrieve & update preview image offset & length values
2424             ExifAttribute imageLengthAttribute = (ExifAttribute)
2425                     mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_START);
2426             ExifAttribute bitsPerSampleAttribute = (ExifAttribute)
2427                     mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_LENGTH);
2428 
2429             if (imageLengthAttribute != null && bitsPerSampleAttribute != null) {
2430                 mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT,
2431                         imageLengthAttribute);
2432                 mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
2433                         bitsPerSampleAttribute);
2434             }
2435 
2436             // TODO: Check this behavior in other ORF files
2437             // Retrieve primary image length & width values
2438             // See piex.cc GetOlympusPreviewImage()
2439             ExifAttribute aspectFrameAttribute = (ExifAttribute)
2440                     mAttributes[IFD_TYPE_ORF_IMAGE_PROCESSING].get(TAG_ORF_ASPECT_FRAME);
2441             if (aspectFrameAttribute != null) {
2442                 int[] aspectFrameValues = (int[]) aspectFrameAttribute.getValue(mExifByteOrder);
2443                 if (aspectFrameValues == null || aspectFrameValues.length != 4) {
2444                     Log.w(TAG, "Invalid aspect frame values. frame="
2445                             + Arrays.toString(aspectFrameValues));
2446                     return;
2447                 }
2448                 if (aspectFrameValues[2] > aspectFrameValues[0] &&
2449                         aspectFrameValues[3] > aspectFrameValues[1]) {
2450                     int primaryImageWidth = aspectFrameValues[2] - aspectFrameValues[0] + 1;
2451                     int primaryImageLength = aspectFrameValues[3] - aspectFrameValues[1] + 1;
2452                     // Swap width & length values
2453                     if (primaryImageWidth < primaryImageLength) {
2454                         primaryImageWidth += primaryImageLength;
2455                         primaryImageLength = primaryImageWidth - primaryImageLength;
2456                         primaryImageWidth -= primaryImageLength;
2457                     }
2458                     ExifAttribute primaryImageWidthAttribute =
2459                             ExifAttribute.createUShort(primaryImageWidth, mExifByteOrder);
2460                     ExifAttribute primaryImageLengthAttribute =
2461                             ExifAttribute.createUShort(primaryImageLength, mExifByteOrder);
2462 
2463                     mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, primaryImageWidthAttribute);
2464                     mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, primaryImageLengthAttribute);
2465                 }
2466             }
2467         }
2468     }
2469 
2470     // RW2 contains the primary image data in IFD0 and the preview and/or thumbnail image data in
2471     // the JpgFromRaw tag
2472     // See https://libopenraw.freedesktop.org/wiki/Panasonic_RAW/ and piex.cc Rw2GetPreviewData()
getRw2Attributes(ByteOrderedDataInputStream in)2473     private void getRw2Attributes(ByteOrderedDataInputStream in) throws IOException {
2474         // Retrieve primary image data
2475         getRawAttributes(in);
2476 
2477         // Retrieve preview and/or thumbnail image data
2478         ExifAttribute jpgFromRawAttribute =
2479                 (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_JPG_FROM_RAW);
2480         if (jpgFromRawAttribute != null) {
2481             getJpegAttributes(in, mRw2JpgFromRawOffset, IFD_TYPE_PREVIEW);
2482         }
2483 
2484         // Set ISO tag value if necessary
2485         ExifAttribute rw2IsoAttribute =
2486                 (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_ISO);
2487         ExifAttribute exifIsoAttribute =
2488                 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_ISO_SPEED_RATINGS);
2489         if (rw2IsoAttribute != null && exifIsoAttribute == null) {
2490             // Place this attribute only if it doesn't exist
2491             mAttributes[IFD_TYPE_EXIF].put(TAG_ISO_SPEED_RATINGS, rw2IsoAttribute);
2492         }
2493     }
2494 
2495     // Stores a new JPEG image with EXIF attributes into a given output stream.
saveJpegAttributes(InputStream inputStream, OutputStream outputStream)2496     private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream)
2497             throws IOException {
2498         // See JPEG File Interchange Format Specification, "JFIF Specification"
2499         if (DEBUG) {
2500             Log.d(TAG, "saveJpegAttributes starting with (inputStream: " + inputStream
2501                     + ", outputStream: " + outputStream + ")");
2502         }
2503         DataInputStream dataInputStream = new DataInputStream(inputStream);
2504         ByteOrderedDataOutputStream dataOutputStream =
2505                 new ByteOrderedDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN);
2506         if (dataInputStream.readByte() != MARKER) {
2507             throw new IOException("Invalid marker");
2508         }
2509         dataOutputStream.writeByte(MARKER);
2510         if (dataInputStream.readByte() != MARKER_SOI) {
2511             throw new IOException("Invalid marker");
2512         }
2513         dataOutputStream.writeByte(MARKER_SOI);
2514 
2515         // Write EXIF APP1 segment
2516         dataOutputStream.writeByte(MARKER);
2517         dataOutputStream.writeByte(MARKER_APP1);
2518         writeExifSegment(dataOutputStream, 6);
2519 
2520         byte[] bytes = new byte[4096];
2521 
2522         while (true) {
2523             byte marker = dataInputStream.readByte();
2524             if (marker != MARKER) {
2525                 throw new IOException("Invalid marker");
2526             }
2527             marker = dataInputStream.readByte();
2528             switch (marker) {
2529                 case MARKER_APP1: {
2530                     int length = dataInputStream.readUnsignedShort() - 2;
2531                     if (length < 0) {
2532                         throw new IOException("Invalid length");
2533                     }
2534                     byte[] identifier = new byte[6];
2535                     if (length >= 6) {
2536                         if (dataInputStream.read(identifier) != 6) {
2537                             throw new IOException("Invalid exif");
2538                         }
2539                         if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
2540                             // Skip the original EXIF APP1 segment.
2541                             if (dataInputStream.skipBytes(length - 6) != length - 6) {
2542                                 throw new IOException("Invalid length");
2543                             }
2544                             break;
2545                         }
2546                     }
2547                     // Copy non-EXIF APP1 segment.
2548                     dataOutputStream.writeByte(MARKER);
2549                     dataOutputStream.writeByte(marker);
2550                     dataOutputStream.writeUnsignedShort(length + 2);
2551                     if (length >= 6) {
2552                         length -= 6;
2553                         dataOutputStream.write(identifier);
2554                     }
2555                     int read;
2556                     while (length > 0 && (read = dataInputStream.read(
2557                             bytes, 0, Math.min(length, bytes.length))) >= 0) {
2558                         dataOutputStream.write(bytes, 0, read);
2559                         length -= read;
2560                     }
2561                     break;
2562                 }
2563                 case MARKER_EOI:
2564                 case MARKER_SOS: {
2565                     dataOutputStream.writeByte(MARKER);
2566                     dataOutputStream.writeByte(marker);
2567                     // Copy all the remaining data
2568                     copy(dataInputStream, dataOutputStream);
2569                     return;
2570                 }
2571                 default: {
2572                     // Copy JPEG segment
2573                     dataOutputStream.writeByte(MARKER);
2574                     dataOutputStream.writeByte(marker);
2575                     int length = dataInputStream.readUnsignedShort();
2576                     dataOutputStream.writeUnsignedShort(length);
2577                     length -= 2;
2578                     if (length < 0) {
2579                         throw new IOException("Invalid length");
2580                     }
2581                     int read;
2582                     while (length > 0 && (read = dataInputStream.read(
2583                             bytes, 0, Math.min(length, bytes.length))) >= 0) {
2584                         dataOutputStream.write(bytes, 0, read);
2585                         length -= read;
2586                     }
2587                     break;
2588                 }
2589             }
2590         }
2591     }
2592 
2593     // Reads the given EXIF byte area and save its tag data into attributes.
readExifSegment(byte[] exifBytes, int imageType)2594     private void readExifSegment(byte[] exifBytes, int imageType) throws IOException {
2595         ByteOrderedDataInputStream dataInputStream =
2596                 new ByteOrderedDataInputStream(exifBytes);
2597 
2598         // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
2599         parseTiffHeaders(dataInputStream, exifBytes.length);
2600 
2601         // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6.
2602         readImageFileDirectory(dataInputStream, imageType);
2603     }
2604 
addDefaultValuesForCompatibility()2605     private void addDefaultValuesForCompatibility() {
2606         // The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag.
2607         String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL);
2608         if (valueOfDateTimeOriginal != null) {
2609             mAttributes[IFD_TYPE_PRIMARY].put(TAG_DATETIME,
2610                     ExifAttribute.createString(valueOfDateTimeOriginal));
2611         }
2612 
2613         // Add the default value.
2614         if (getAttribute(TAG_IMAGE_WIDTH) == null) {
2615             mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH,
2616                     ExifAttribute.createULong(0, mExifByteOrder));
2617         }
2618         if (getAttribute(TAG_IMAGE_LENGTH) == null) {
2619             mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH,
2620                     ExifAttribute.createULong(0, mExifByteOrder));
2621         }
2622         if (getAttribute(TAG_ORIENTATION) == null) {
2623             mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
2624                     ExifAttribute.createULong(0, mExifByteOrder));
2625         }
2626         if (getAttribute(TAG_LIGHT_SOURCE) == null) {
2627             mAttributes[IFD_TYPE_EXIF].put(TAG_LIGHT_SOURCE,
2628                     ExifAttribute.createULong(0, mExifByteOrder));
2629         }
2630     }
2631 
readByteOrder(ByteOrderedDataInputStream dataInputStream)2632     private ByteOrder readByteOrder(ByteOrderedDataInputStream dataInputStream)
2633             throws IOException {
2634         // Read byte order.
2635         short byteOrder = dataInputStream.readShort();
2636         switch (byteOrder) {
2637             case BYTE_ALIGN_II:
2638                 if (DEBUG) {
2639                     Log.d(TAG, "readExifSegment: Byte Align II");
2640                 }
2641                 return ByteOrder.LITTLE_ENDIAN;
2642             case BYTE_ALIGN_MM:
2643                 if (DEBUG) {
2644                     Log.d(TAG, "readExifSegment: Byte Align MM");
2645                 }
2646                 return ByteOrder.BIG_ENDIAN;
2647             default:
2648                 throw new IOException("Invalid byte order: " + Integer.toHexString(byteOrder));
2649         }
2650     }
2651 
parseTiffHeaders(ByteOrderedDataInputStream dataInputStream, int exifBytesLength)2652     private void parseTiffHeaders(ByteOrderedDataInputStream dataInputStream,
2653             int exifBytesLength) throws IOException {
2654         // Read byte order
2655         mExifByteOrder = readByteOrder(dataInputStream);
2656         // Set byte order
2657         dataInputStream.setByteOrder(mExifByteOrder);
2658 
2659         // Check start code
2660         int startCode = dataInputStream.readUnsignedShort();
2661         if (mMimeType != IMAGE_TYPE_ORF && mMimeType != IMAGE_TYPE_RW2 && startCode != START_CODE) {
2662             throw new IOException("Invalid start code: " + Integer.toHexString(startCode));
2663         }
2664 
2665         // Read and skip to first ifd offset
2666         int firstIfdOffset = dataInputStream.readInt();
2667         if (firstIfdOffset < 8 || firstIfdOffset >= exifBytesLength) {
2668             throw new IOException("Invalid first Ifd offset: " + firstIfdOffset);
2669         }
2670         firstIfdOffset -= 8;
2671         if (firstIfdOffset > 0) {
2672             if (dataInputStream.skipBytes(firstIfdOffset) != firstIfdOffset) {
2673                 throw new IOException("Couldn't jump to first Ifd: " + firstIfdOffset);
2674             }
2675         }
2676     }
2677 
2678     // Reads image file directory, which is a tag group in EXIF.
readImageFileDirectory(ByteOrderedDataInputStream dataInputStream, @IfdType int ifdType)2679     private void readImageFileDirectory(ByteOrderedDataInputStream dataInputStream,
2680             @IfdType int ifdType) throws IOException {
2681         if (dataInputStream.mPosition + 2 > dataInputStream.mLength) {
2682             // Return if there is no data from the offset.
2683             return;
2684         }
2685         // See TIFF 6.0 Section 2: TIFF Structure, Figure 1.
2686         short numberOfDirectoryEntry = dataInputStream.readShort();
2687         if (dataInputStream.mPosition + 12 * numberOfDirectoryEntry > dataInputStream.mLength) {
2688             // Return if the size of entries is too big.
2689             return;
2690         }
2691 
2692         if (DEBUG) {
2693             Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
2694         }
2695 
2696         // See TIFF 6.0 Section 2: TIFF Structure, "Image File Directory".
2697         for (short i = 0; i < numberOfDirectoryEntry; ++i) {
2698             int tagNumber = dataInputStream.readUnsignedShort();
2699             int dataFormat = dataInputStream.readUnsignedShort();
2700             int numberOfComponents = dataInputStream.readInt();
2701             // Next four bytes is for data offset or value.
2702             long nextEntryOffset = dataInputStream.peek() + 4;
2703 
2704             // Look up a corresponding tag from tag number
2705             ExifTag tag = (ExifTag) sExifTagMapsForReading[ifdType].get(tagNumber);
2706 
2707             if (DEBUG) {
2708                 Log.d(TAG, String.format("ifdType: %d, tagNumber: %d, tagName: %s, dataFormat: %d, "
2709                         + "numberOfComponents: %d", ifdType, tagNumber,
2710                         tag != null ? tag.name : null, dataFormat, numberOfComponents));
2711             }
2712 
2713             long byteCount = 0;
2714             boolean valid = false;
2715             if (tag == null) {
2716                 Log.w(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber);
2717             } else if (dataFormat <= 0 || dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) {
2718                 Log.w(TAG, "Skip the tag entry since data format is invalid: " + dataFormat);
2719             } else {
2720                 byteCount = (long) numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat];
2721                 if (byteCount < 0 || byteCount > Integer.MAX_VALUE) {
2722                     Log.w(TAG, "Skip the tag entry since the number of components is invalid: "
2723                             + numberOfComponents);
2724                 } else {
2725                     valid = true;
2726                 }
2727             }
2728             if (!valid) {
2729                 dataInputStream.seek(nextEntryOffset);
2730                 continue;
2731             }
2732 
2733             // Read a value from data field or seek to the value offset which is stored in data
2734             // field if the size of the entry value is bigger than 4.
2735             if (byteCount > 4) {
2736                 int offset = dataInputStream.readInt();
2737                 if (DEBUG) {
2738                     Log.d(TAG, "seek to data offset: " + offset);
2739                 }
2740                 if (mMimeType == IMAGE_TYPE_ORF) {
2741                     if (TAG_MAKER_NOTE.equals(tag.name)) {
2742                         // Save offset value for reading thumbnail
2743                         mOrfMakerNoteOffset = offset;
2744                     } else if (ifdType == IFD_TYPE_ORF_MAKER_NOTE
2745                             && TAG_ORF_THUMBNAIL_IMAGE.equals(tag.name)) {
2746                         // Retrieve & update values for thumbnail offset and length values for ORF
2747                         mOrfThumbnailOffset = offset;
2748                         mOrfThumbnailLength = numberOfComponents;
2749 
2750                         ExifAttribute compressionAttribute =
2751                                 ExifAttribute.createUShort(DATA_JPEG, mExifByteOrder);
2752                         ExifAttribute jpegInterchangeFormatAttribute =
2753                                 ExifAttribute.createULong(mOrfThumbnailOffset, mExifByteOrder);
2754                         ExifAttribute jpegInterchangeFormatLengthAttribute =
2755                                 ExifAttribute.createULong(mOrfThumbnailLength, mExifByteOrder);
2756 
2757                         mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_COMPRESSION, compressionAttribute);
2758                         mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT,
2759                                 jpegInterchangeFormatAttribute);
2760                         mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
2761                                 jpegInterchangeFormatLengthAttribute);
2762                     }
2763                 } else if (mMimeType == IMAGE_TYPE_RW2) {
2764                     if (TAG_RW2_JPG_FROM_RAW.equals(tag.name)) {
2765                         mRw2JpgFromRawOffset = offset;
2766                     }
2767                 }
2768                 if (offset + byteCount <= dataInputStream.mLength) {
2769                     dataInputStream.seek(offset);
2770                 } else {
2771                     // Skip if invalid data offset.
2772                     Log.w(TAG, "Skip the tag entry since data offset is invalid: " + offset);
2773                     dataInputStream.seek(nextEntryOffset);
2774                     continue;
2775                 }
2776             }
2777 
2778             // Recursively parse IFD when a IFD pointer tag appears.
2779             Integer nextIfdType = sExifPointerTagMap.get(tagNumber);
2780             if (DEBUG) {
2781                 Log.d(TAG, "nextIfdType: " + nextIfdType + " byteCount: " + byteCount);
2782             }
2783 
2784             if (nextIfdType != null) {
2785                 long offset = -1L;
2786                 // Get offset from data field
2787                 switch (dataFormat) {
2788                     case IFD_FORMAT_USHORT: {
2789                         offset = dataInputStream.readUnsignedShort();
2790                         break;
2791                     }
2792                     case IFD_FORMAT_SSHORT: {
2793                         offset = dataInputStream.readShort();
2794                         break;
2795                     }
2796                     case IFD_FORMAT_ULONG: {
2797                         offset = dataInputStream.readUnsignedInt();
2798                         break;
2799                     }
2800                     case IFD_FORMAT_SLONG:
2801                     case IFD_FORMAT_IFD: {
2802                         offset = dataInputStream.readInt();
2803                         break;
2804                     }
2805                     default: {
2806                         // Nothing to do
2807                         break;
2808                     }
2809                 }
2810                 if (DEBUG) {
2811                     Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tag.name));
2812                 }
2813                 if (offset > 0L && offset < dataInputStream.mLength) {
2814                     dataInputStream.seek(offset);
2815                     readImageFileDirectory(dataInputStream, nextIfdType);
2816                 } else {
2817                     Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
2818                 }
2819 
2820                 dataInputStream.seek(nextEntryOffset);
2821                 continue;
2822             }
2823 
2824             byte[] bytes = new byte[(int) byteCount];
2825             dataInputStream.readFully(bytes);
2826             ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytes);
2827             mAttributes[ifdType].put(tag.name, attribute);
2828 
2829             // DNG files have a DNG Version tag specifying the version of specifications that the
2830             // image file is following.
2831             // See http://fileformats.archiveteam.org/wiki/DNG
2832             if (TAG_DNG_VERSION.equals(tag.name)) {
2833                 mMimeType = IMAGE_TYPE_DNG;
2834             }
2835 
2836             // PEF files have a Make or Model tag that begins with "PENTAX" or a compression tag
2837             // that is 65535.
2838             // See http://fileformats.archiveteam.org/wiki/Pentax_PEF
2839             if (((TAG_MAKE.equals(tag.name) || TAG_MODEL.equals(tag.name))
2840                     && attribute.getStringValue(mExifByteOrder).contains(PEF_SIGNATURE))
2841                     || (TAG_COMPRESSION.equals(tag.name)
2842                     && attribute.getIntValue(mExifByteOrder) == 65535)) {
2843                 mMimeType = IMAGE_TYPE_PEF;
2844             }
2845 
2846             // Seek to next tag offset
2847             if (dataInputStream.peek() != nextEntryOffset) {
2848                 dataInputStream.seek(nextEntryOffset);
2849             }
2850         }
2851 
2852         if (dataInputStream.peek() + 4 <= dataInputStream.mLength) {
2853             int nextIfdOffset = dataInputStream.readInt();
2854             if (DEBUG) {
2855                 Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset));
2856             }
2857             // The next IFD offset needs to be bigger than 8
2858             // since the first IFD offset is at least 8.
2859             if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) {
2860                 dataInputStream.seek(nextIfdOffset);
2861                 if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
2862                     // Do not overwrite thumbnail IFD data if it alreay exists.
2863                     readImageFileDirectory(dataInputStream, IFD_TYPE_THUMBNAIL);
2864                 } else if (mAttributes[IFD_TYPE_PREVIEW].isEmpty()) {
2865                     readImageFileDirectory(dataInputStream, IFD_TYPE_PREVIEW);
2866                 }
2867             }
2868         }
2869     }
2870 
2871     /**
2872      * JPEG compressed images do not contain IMAGE_LENGTH & IMAGE_WIDTH tags.
2873      * This value uses JpegInterchangeFormat(JPEG data offset) value, and calls getJpegAttributes()
2874      * to locate SOF(Start of Frame) marker and update the image length & width values.
2875      * See JEITA CP-3451C Table 5 and Section 4.8.1. B.
2876      */
retrieveJpegImageSize(ByteOrderedDataInputStream in, int imageType)2877     private void retrieveJpegImageSize(ByteOrderedDataInputStream in, int imageType)
2878             throws IOException {
2879         // Check if image already has IMAGE_LENGTH & IMAGE_WIDTH values
2880         ExifAttribute imageLengthAttribute =
2881                 (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_LENGTH);
2882         ExifAttribute imageWidthAttribute =
2883                 (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_WIDTH);
2884 
2885         if (imageLengthAttribute == null || imageWidthAttribute == null) {
2886             // Find if offset for JPEG data exists
2887             ExifAttribute jpegInterchangeFormatAttribute =
2888                     (ExifAttribute) mAttributes[imageType].get(TAG_JPEG_INTERCHANGE_FORMAT);
2889             if (jpegInterchangeFormatAttribute != null) {
2890                 int jpegInterchangeFormat =
2891                         jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder);
2892 
2893                 // Searches for SOF marker in JPEG data and updates IMAGE_LENGTH & IMAGE_WIDTH tags
2894                 getJpegAttributes(in, jpegInterchangeFormat, imageType);
2895             }
2896         }
2897     }
2898 
2899     // Sets thumbnail offset & length attributes based on JpegInterchangeFormat or StripOffsets tags
setThumbnailData(ByteOrderedDataInputStream in)2900     private void setThumbnailData(ByteOrderedDataInputStream in) throws IOException {
2901         HashMap thumbnailData = mAttributes[IFD_TYPE_THUMBNAIL];
2902 
2903         ExifAttribute compressionAttribute =
2904                 (ExifAttribute) thumbnailData.get(TAG_COMPRESSION);
2905         if (compressionAttribute != null) {
2906             mThumbnailCompression = compressionAttribute.getIntValue(mExifByteOrder);
2907             switch (mThumbnailCompression) {
2908                 case DATA_JPEG: {
2909                     handleThumbnailFromJfif(in, thumbnailData);
2910                     break;
2911                 }
2912                 case DATA_UNCOMPRESSED:
2913                 case DATA_JPEG_COMPRESSED: {
2914                     if (isSupportedDataType(thumbnailData)) {
2915                         handleThumbnailFromStrips(in, thumbnailData);
2916                     }
2917                     break;
2918                 }
2919             }
2920         } else {
2921             // Thumbnail data may not contain Compression tag value
2922             mThumbnailCompression = DATA_JPEG;
2923             handleThumbnailFromJfif(in, thumbnailData);
2924         }
2925     }
2926 
2927     // Check JpegInterchangeFormat(JFIF) tags to retrieve thumbnail offset & length values
2928     // and reads the corresponding bytes if stream does not support seek function
handleThumbnailFromJfif(ByteOrderedDataInputStream in, HashMap thumbnailData)2929     private void handleThumbnailFromJfif(ByteOrderedDataInputStream in, HashMap thumbnailData)
2930             throws IOException {
2931         ExifAttribute jpegInterchangeFormatAttribute =
2932                 (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT);
2933         ExifAttribute jpegInterchangeFormatLengthAttribute =
2934                 (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
2935         if (jpegInterchangeFormatAttribute != null
2936                 && jpegInterchangeFormatLengthAttribute != null) {
2937             int thumbnailOffset = jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder);
2938             int thumbnailLength = jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder);
2939 
2940             // The following code limits the size of thumbnail size not to overflow EXIF data area.
2941             thumbnailLength = Math.min(thumbnailLength, in.available() - thumbnailOffset);
2942             if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF
2943                     || mMimeType == IMAGE_TYPE_RW2) {
2944                 thumbnailOffset += mExifOffset;
2945             } else if (mMimeType == IMAGE_TYPE_ORF) {
2946                 // Update offset value since RAF files have IFD data preceding MakerNote data.
2947                 thumbnailOffset += mOrfMakerNoteOffset;
2948             }
2949             if (DEBUG) {
2950                 Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset
2951                         + ", length: " + thumbnailLength);
2952             }
2953             if (thumbnailOffset > 0 && thumbnailLength > 0) {
2954                 mHasThumbnail = true;
2955                 mThumbnailOffset = thumbnailOffset;
2956                 mThumbnailLength = thumbnailLength;
2957                 if (mFilename == null && mAssetInputStream == null) {
2958                     // Save the thumbnail in memory if the input doesn't support reading again.
2959                     byte[] thumbnailBytes = new byte[thumbnailLength];
2960                     in.seek(thumbnailOffset);
2961                     in.readFully(thumbnailBytes);
2962                     mThumbnailBytes = thumbnailBytes;
2963                 }
2964             }
2965         }
2966     }
2967 
2968     // Check StripOffsets & StripByteCounts tags to retrieve thumbnail offset & length values
handleThumbnailFromStrips(ByteOrderedDataInputStream in, HashMap thumbnailData)2969     private void handleThumbnailFromStrips(ByteOrderedDataInputStream in, HashMap thumbnailData)
2970             throws IOException {
2971         ExifAttribute stripOffsetsAttribute =
2972                 (ExifAttribute) thumbnailData.get(TAG_STRIP_OFFSETS);
2973         ExifAttribute stripByteCountsAttribute =
2974                 (ExifAttribute) thumbnailData.get(TAG_STRIP_BYTE_COUNTS);
2975 
2976         if (stripOffsetsAttribute != null && stripByteCountsAttribute != null) {
2977             long[] stripOffsets =
2978                     convertToLongArray(stripOffsetsAttribute.getValue(mExifByteOrder));
2979             long[] stripByteCounts =
2980                     convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder));
2981 
2982             if (stripOffsets == null) {
2983                 Log.w(TAG, "stripOffsets should not be null.");
2984                 return;
2985             }
2986             if (stripByteCounts == null) {
2987                 Log.w(TAG, "stripByteCounts should not be null.");
2988                 return;
2989             }
2990 
2991             long totalStripByteCount = 0;
2992             for (long byteCount : stripByteCounts) {
2993                 totalStripByteCount += byteCount;
2994             }
2995 
2996             // Set thumbnail byte array data for non-consecutive strip bytes
2997             byte[] totalStripBytes = new byte[(int) totalStripByteCount];
2998 
2999             int bytesRead = 0;
3000             int bytesAdded = 0;
3001             for (int i = 0; i < stripOffsets.length; i++) {
3002                 int stripOffset = (int) stripOffsets[i];
3003                 int stripByteCount = (int) stripByteCounts[i];
3004 
3005                 // Skip to offset
3006                 int skipBytes = stripOffset - bytesRead;
3007                 if (skipBytes < 0) {
3008                     Log.d(TAG, "Invalid strip offset value");
3009                 }
3010                 in.seek(skipBytes);
3011                 bytesRead += skipBytes;
3012 
3013                 // Read strip bytes
3014                 byte[] stripBytes = new byte[stripByteCount];
3015                 in.read(stripBytes);
3016                 bytesRead += stripByteCount;
3017 
3018                 // Add bytes to array
3019                 System.arraycopy(stripBytes, 0, totalStripBytes, bytesAdded,
3020                         stripBytes.length);
3021                 bytesAdded += stripBytes.length;
3022             }
3023 
3024             mHasThumbnail = true;
3025             mThumbnailBytes = totalStripBytes;
3026             mThumbnailLength = totalStripBytes.length;
3027         }
3028     }
3029 
3030     // Check if thumbnail data type is currently supported or not
isSupportedDataType(HashMap thumbnailData)3031     private boolean isSupportedDataType(HashMap thumbnailData) throws IOException {
3032         ExifAttribute bitsPerSampleAttribute =
3033                 (ExifAttribute) thumbnailData.get(TAG_BITS_PER_SAMPLE);
3034         if (bitsPerSampleAttribute != null) {
3035             int[] bitsPerSampleValue = (int[]) bitsPerSampleAttribute.getValue(mExifByteOrder);
3036 
3037             if (Arrays.equals(BITS_PER_SAMPLE_RGB, bitsPerSampleValue)) {
3038                 return true;
3039             }
3040 
3041             // See DNG Specification 1.4.0.0. Section 3, Compression.
3042             if (mMimeType == IMAGE_TYPE_DNG) {
3043                 ExifAttribute photometricInterpretationAttribute =
3044                         (ExifAttribute) thumbnailData.get(TAG_PHOTOMETRIC_INTERPRETATION);
3045                 if (photometricInterpretationAttribute != null) {
3046                     int photometricInterpretationValue
3047                             = photometricInterpretationAttribute.getIntValue(mExifByteOrder);
3048                     if ((photometricInterpretationValue == PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO
3049                             && Arrays.equals(bitsPerSampleValue, BITS_PER_SAMPLE_GREYSCALE_2))
3050                             || ((photometricInterpretationValue == PHOTOMETRIC_INTERPRETATION_YCBCR)
3051                             && (Arrays.equals(bitsPerSampleValue, BITS_PER_SAMPLE_RGB)))) {
3052                         return true;
3053                     } else {
3054                         // TODO: Add support for lossless Huffman JPEG data
3055                     }
3056                 }
3057             }
3058         }
3059         if (DEBUG) {
3060             Log.d(TAG, "Unsupported data type value");
3061         }
3062         return false;
3063     }
3064 
3065     // Returns true if the image length and width values are <= 512.
3066     // See Section 4.8 of http://standardsproposals.bsigroup.com/Home/getPDF/567
isThumbnail(HashMap map)3067     private boolean isThumbnail(HashMap map) throws IOException {
3068         ExifAttribute imageLengthAttribute = (ExifAttribute) map.get(TAG_IMAGE_LENGTH);
3069         ExifAttribute imageWidthAttribute = (ExifAttribute) map.get(TAG_IMAGE_WIDTH);
3070 
3071         if (imageLengthAttribute != null && imageWidthAttribute != null) {
3072             int imageLengthValue = imageLengthAttribute.getIntValue(mExifByteOrder);
3073             int imageWidthValue = imageWidthAttribute.getIntValue(mExifByteOrder);
3074             if (imageLengthValue <= MAX_THUMBNAIL_SIZE && imageWidthValue <= MAX_THUMBNAIL_SIZE) {
3075                 return true;
3076             }
3077         }
3078         return false;
3079     }
3080 
3081     // Validate primary, preview, thumbnail image data by comparing image size
validateImages(InputStream in)3082     private void validateImages(InputStream in) throws IOException {
3083         // Swap images based on size (primary > preview > thumbnail)
3084         swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_PREVIEW);
3085         swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_THUMBNAIL);
3086         swapBasedOnImageSize(IFD_TYPE_PREVIEW, IFD_TYPE_THUMBNAIL);
3087 
3088         // Check if image has PixelXDimension/PixelYDimension tags, which contain valid image
3089         // sizes, excluding padding at the right end or bottom end of the image to make sure that
3090         // the values are multiples of 64. See JEITA CP-3451C Table 5 and Section 4.8.1. B.
3091         ExifAttribute pixelXDimAttribute =
3092                 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_X_DIMENSION);
3093         ExifAttribute pixelYDimAttribute =
3094                 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_Y_DIMENSION);
3095         if (pixelXDimAttribute != null && pixelYDimAttribute != null) {
3096             mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, pixelXDimAttribute);
3097             mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, pixelYDimAttribute);
3098         }
3099 
3100         // Check whether thumbnail image exists and whether preview image satisfies the thumbnail
3101         // image requirements
3102         if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
3103             if (isThumbnail(mAttributes[IFD_TYPE_PREVIEW])) {
3104                 mAttributes[IFD_TYPE_THUMBNAIL] = mAttributes[IFD_TYPE_PREVIEW];
3105                 mAttributes[IFD_TYPE_PREVIEW] = new HashMap<>();
3106             }
3107         }
3108 
3109         // Check if the thumbnail image satisfies the thumbnail size requirements
3110         if (!isThumbnail(mAttributes[IFD_TYPE_THUMBNAIL])) {
3111             Log.d(TAG, "No image meets the size requirements of a thumbnail image.");
3112         }
3113     }
3114 
3115     /**
3116      * If image is uncompressed, ImageWidth/Length tags are used to store size info.
3117      * However, uncompressed images often store extra pixels around the edges of the final image,
3118      * which results in larger values for TAG_IMAGE_WIDTH and TAG_IMAGE_LENGTH tags.
3119      * This method corrects those tag values by checking first the values of TAG_DEFAULT_CROP_SIZE
3120      * See DNG Specification 1.4.0.0. Section 4. (DefaultCropSize)
3121      *
3122      * If image is a RW2 file, valid image sizes are stored in SensorBorder tags.
3123      * See tiff_parser.cc GetFullDimension32()
3124      * */
updateImageSizeValues(ByteOrderedDataInputStream in, int imageType)3125     private void updateImageSizeValues(ByteOrderedDataInputStream in, int imageType)
3126             throws IOException {
3127         // Uncompressed image valid image size values
3128         ExifAttribute defaultCropSizeAttribute =
3129                 (ExifAttribute) mAttributes[imageType].get(TAG_DEFAULT_CROP_SIZE);
3130         // RW2 image valid image size values
3131         ExifAttribute topBorderAttribute =
3132                 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_TOP_BORDER);
3133         ExifAttribute leftBorderAttribute =
3134                 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_LEFT_BORDER);
3135         ExifAttribute bottomBorderAttribute =
3136                 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_BOTTOM_BORDER);
3137         ExifAttribute rightBorderAttribute =
3138                 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_RIGHT_BORDER);
3139 
3140         if (defaultCropSizeAttribute != null) {
3141             // Update for uncompressed image
3142             ExifAttribute defaultCropSizeXAttribute, defaultCropSizeYAttribute;
3143             if (defaultCropSizeAttribute.format == IFD_FORMAT_URATIONAL) {
3144                 Rational[] defaultCropSizeValue =
3145                         (Rational[]) defaultCropSizeAttribute.getValue(mExifByteOrder);
3146                 if (defaultCropSizeValue == null || defaultCropSizeValue.length != 2) {
3147                     Log.w(TAG, "Invalid crop size values. cropSize="
3148                             + Arrays.toString(defaultCropSizeValue));
3149                     return;
3150                 }
3151                 defaultCropSizeXAttribute =
3152                         ExifAttribute.createURational(defaultCropSizeValue[0], mExifByteOrder);
3153                 defaultCropSizeYAttribute =
3154                         ExifAttribute.createURational(defaultCropSizeValue[1], mExifByteOrder);
3155             } else {
3156                 int[] defaultCropSizeValue =
3157                         (int[]) defaultCropSizeAttribute.getValue(mExifByteOrder);
3158                 if (defaultCropSizeValue == null || defaultCropSizeValue.length != 2) {
3159                     Log.w(TAG, "Invalid crop size values. cropSize="
3160                             + Arrays.toString(defaultCropSizeValue));
3161                     return;
3162                 }
3163                 defaultCropSizeXAttribute =
3164                         ExifAttribute.createUShort(defaultCropSizeValue[0], mExifByteOrder);
3165                 defaultCropSizeYAttribute =
3166                         ExifAttribute.createUShort(defaultCropSizeValue[1], mExifByteOrder);
3167             }
3168             mAttributes[imageType].put(TAG_IMAGE_WIDTH, defaultCropSizeXAttribute);
3169             mAttributes[imageType].put(TAG_IMAGE_LENGTH, defaultCropSizeYAttribute);
3170         } else if (topBorderAttribute != null && leftBorderAttribute != null &&
3171                 bottomBorderAttribute != null && rightBorderAttribute != null) {
3172             // Update for RW2 image
3173             int topBorderValue = topBorderAttribute.getIntValue(mExifByteOrder);
3174             int bottomBorderValue = bottomBorderAttribute.getIntValue(mExifByteOrder);
3175             int rightBorderValue = rightBorderAttribute.getIntValue(mExifByteOrder);
3176             int leftBorderValue = leftBorderAttribute.getIntValue(mExifByteOrder);
3177             if (bottomBorderValue > topBorderValue && rightBorderValue > leftBorderValue) {
3178                 int length = bottomBorderValue - topBorderValue;
3179                 int width = rightBorderValue - leftBorderValue;
3180                 ExifAttribute imageLengthAttribute =
3181                         ExifAttribute.createUShort(length, mExifByteOrder);
3182                 ExifAttribute imageWidthAttribute =
3183                         ExifAttribute.createUShort(width, mExifByteOrder);
3184                 mAttributes[imageType].put(TAG_IMAGE_LENGTH, imageLengthAttribute);
3185                 mAttributes[imageType].put(TAG_IMAGE_WIDTH, imageWidthAttribute);
3186             }
3187         } else {
3188             retrieveJpegImageSize(in, imageType);
3189         }
3190     }
3191 
3192     // Writes an Exif segment into the given output stream.
writeExifSegment(ByteOrderedDataOutputStream dataOutputStream, int exifOffsetFromBeginning)3193     private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream,
3194             int exifOffsetFromBeginning) throws IOException {
3195         // The following variables are for calculating each IFD tag group size in bytes.
3196         int[] ifdOffsets = new int[EXIF_TAGS.length];
3197         int[] ifdDataSizes = new int[EXIF_TAGS.length];
3198 
3199         // Remove IFD pointer tags (we'll re-add it later.)
3200         for (ExifTag tag : EXIF_POINTER_TAGS) {
3201             removeAttribute(tag.name);
3202         }
3203         // Remove old thumbnail data
3204         removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
3205         removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
3206 
3207         // Remove null value tags.
3208         for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
3209             for (Object obj : mAttributes[ifdType].entrySet().toArray()) {
3210                 final Map.Entry entry = (Map.Entry) obj;
3211                 if (entry.getValue() == null) {
3212                     mAttributes[ifdType].remove(entry.getKey());
3213                 }
3214             }
3215         }
3216 
3217         // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD
3218         // offset when there is one or more tags in the thumbnail IFD.
3219         if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) {
3220             mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name,
3221                     ExifAttribute.createULong(0, mExifByteOrder));
3222         }
3223         if (!mAttributes[IFD_TYPE_GPS].isEmpty()) {
3224             mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name,
3225                     ExifAttribute.createULong(0, mExifByteOrder));
3226         }
3227         if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) {
3228             mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name,
3229                     ExifAttribute.createULong(0, mExifByteOrder));
3230         }
3231         if (mHasThumbnail) {
3232             mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
3233                     ExifAttribute.createULong(0, mExifByteOrder));
3234             mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
3235                     ExifAttribute.createULong(mThumbnailLength, mExifByteOrder));
3236         }
3237 
3238         // Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
3239         // value which has a bigger size than 4 bytes.
3240         for (int i = 0; i < EXIF_TAGS.length; ++i) {
3241             int sum = 0;
3242             for (Map.Entry<String, ExifAttribute> entry : mAttributes[i].entrySet()) {
3243                 final ExifAttribute exifAttribute = entry.getValue();
3244                 final int size = exifAttribute.size();
3245                 if (size > 4) {
3246                     sum += size;
3247                 }
3248             }
3249             ifdDataSizes[i] += sum;
3250         }
3251 
3252         // Calculate IFD offsets.
3253         int position = 8;
3254         for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
3255             if (!mAttributes[ifdType].isEmpty()) {
3256                 ifdOffsets[ifdType] = position;
3257                 position += 2 + mAttributes[ifdType].size() * 12 + 4 + ifdDataSizes[ifdType];
3258             }
3259         }
3260         if (mHasThumbnail) {
3261             int thumbnailOffset = position;
3262             mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
3263                     ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
3264             mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset;
3265             position += mThumbnailLength;
3266         }
3267 
3268         // Calculate the total size
3269         int totalSize = position + 8;  // eight bytes is for header part.
3270         if (DEBUG) {
3271             Log.d(TAG, "totalSize length: " + totalSize);
3272             for (int i = 0; i < EXIF_TAGS.length; ++i) {
3273                 Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d",
3274                         i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i]));
3275             }
3276         }
3277 
3278         // Update IFD pointer tags with the calculated offsets.
3279         if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) {
3280             mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name,
3281                     ExifAttribute.createULong(ifdOffsets[IFD_TYPE_EXIF], mExifByteOrder));
3282         }
3283         if (!mAttributes[IFD_TYPE_GPS].isEmpty()) {
3284             mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name,
3285                     ExifAttribute.createULong(ifdOffsets[IFD_TYPE_GPS], mExifByteOrder));
3286         }
3287         if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) {
3288             mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name, ExifAttribute.createULong(
3289                     ifdOffsets[IFD_TYPE_INTEROPERABILITY], mExifByteOrder));
3290         }
3291 
3292         // Write TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
3293         dataOutputStream.writeUnsignedShort(totalSize);
3294         dataOutputStream.write(IDENTIFIER_EXIF_APP1);
3295         dataOutputStream.writeShort(mExifByteOrder == ByteOrder.BIG_ENDIAN
3296                 ? BYTE_ALIGN_MM : BYTE_ALIGN_II);
3297         dataOutputStream.setByteOrder(mExifByteOrder);
3298         dataOutputStream.writeUnsignedShort(START_CODE);
3299         dataOutputStream.writeUnsignedInt(IFD_OFFSET);
3300 
3301         // Write IFD groups. See JEITA CP-3451C Section 4.5.8. Figure 9.
3302         for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
3303             if (!mAttributes[ifdType].isEmpty()) {
3304                 // See JEITA CP-3451C Section 4.6.2: IFD structure.
3305                 // Write entry count
3306                 dataOutputStream.writeUnsignedShort(mAttributes[ifdType].size());
3307 
3308                 // Write entry info
3309                 int dataOffset = ifdOffsets[ifdType] + 2 + mAttributes[ifdType].size() * 12 + 4;
3310                 for (Map.Entry<String, ExifAttribute> entry : mAttributes[ifdType].entrySet()) {
3311                     // Convert tag name to tag number.
3312                     final ExifTag tag = sExifTagMapsForWriting[ifdType].get(entry.getKey());
3313                     final int tagNumber = tag.number;
3314                     final ExifAttribute attribute = entry.getValue();
3315                     final int size = attribute.size();
3316 
3317                     dataOutputStream.writeUnsignedShort(tagNumber);
3318                     dataOutputStream.writeUnsignedShort(attribute.format);
3319                     dataOutputStream.writeInt(attribute.numberOfComponents);
3320                     if (size > 4) {
3321                         dataOutputStream.writeUnsignedInt(dataOffset);
3322                         dataOffset += size;
3323                     } else {
3324                         dataOutputStream.write(attribute.bytes);
3325                         // Fill zero up to 4 bytes
3326                         if (size < 4) {
3327                             for (int i = size; i < 4; ++i) {
3328                                 dataOutputStream.writeByte(0);
3329                             }
3330                         }
3331                     }
3332                 }
3333 
3334                 // Write the next offset. It writes the offset of thumbnail IFD if there is one or
3335                 // more tags in the thumbnail IFD when the current IFD is the primary image TIFF
3336                 // IFD; Otherwise 0.
3337                 if (ifdType == 0 && !mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
3338                     dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_TYPE_THUMBNAIL]);
3339                 } else {
3340                     dataOutputStream.writeUnsignedInt(0);
3341                 }
3342 
3343                 // Write values of data field exceeding 4 bytes after the next offset.
3344                 for (Map.Entry<String, ExifAttribute> entry : mAttributes[ifdType].entrySet()) {
3345                     ExifAttribute attribute = entry.getValue();
3346 
3347                     if (attribute.bytes.length > 4) {
3348                         dataOutputStream.write(attribute.bytes, 0, attribute.bytes.length);
3349                     }
3350                 }
3351             }
3352         }
3353 
3354         // Write thumbnail
3355         if (mHasThumbnail) {
3356             dataOutputStream.write(getThumbnailBytes());
3357         }
3358 
3359         // Reset the byte order to big endian in order to write remaining parts of the JPEG file.
3360         dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
3361 
3362         return totalSize;
3363     }
3364 
3365     /**
3366      * Determines the data format of EXIF entry value.
3367      *
3368      * @param entryValue The value to be determined.
3369      * @return Returns two data formats gussed as a pair in integer. If there is no two candidate
3370                data formats for the given entry value, returns {@code -1} in the second of the pair.
3371      */
guessDataFormat(String entryValue)3372     private static Pair<Integer, Integer> guessDataFormat(String entryValue) {
3373         // See TIFF 6.0 Section 2, "Image File Directory".
3374         // Take the first component if there are more than one component.
3375         if (entryValue.contains(",")) {
3376             String[] entryValues = entryValue.split(",");
3377             Pair<Integer, Integer> dataFormat = guessDataFormat(entryValues[0]);
3378             if (dataFormat.first == IFD_FORMAT_STRING) {
3379                 return dataFormat;
3380             }
3381             for (int i = 1; i < entryValues.length; ++i) {
3382                 final Pair<Integer, Integer> guessDataFormat = guessDataFormat(entryValues[i]);
3383                 int first = -1, second = -1;
3384                 if (guessDataFormat.first.equals(dataFormat.first)
3385                         || guessDataFormat.second.equals(dataFormat.first)) {
3386                     first = dataFormat.first;
3387                 }
3388                 if (dataFormat.second != -1 && (guessDataFormat.first.equals(dataFormat.second)
3389                         || guessDataFormat.second.equals(dataFormat.second))) {
3390                     second = dataFormat.second;
3391                 }
3392                 if (first == -1 && second == -1) {
3393                     return new Pair<>(IFD_FORMAT_STRING, -1);
3394                 }
3395                 if (first == -1) {
3396                     dataFormat = new Pair<>(second, -1);
3397                     continue;
3398                 }
3399                 if (second == -1) {
3400                     dataFormat = new Pair<>(first, -1);
3401                     continue;
3402                 }
3403             }
3404             return dataFormat;
3405         }
3406 
3407         if (entryValue.contains("/")) {
3408             String[] rationalNumber = entryValue.split("/");
3409             if (rationalNumber.length == 2) {
3410                 try {
3411                     long numerator = (long) Double.parseDouble(rationalNumber[0]);
3412                     long denominator = (long) Double.parseDouble(rationalNumber[1]);
3413                     if (numerator < 0L || denominator < 0L) {
3414                         return new Pair<>(IFD_FORMAT_SRATIONAL, -1);
3415                     }
3416                     if (numerator > Integer.MAX_VALUE || denominator > Integer.MAX_VALUE) {
3417                         return new Pair<>(IFD_FORMAT_URATIONAL, -1);
3418                     }
3419                     return new Pair<>(IFD_FORMAT_SRATIONAL, IFD_FORMAT_URATIONAL);
3420                 } catch (NumberFormatException e)  {
3421                     // Ignored
3422                 }
3423             }
3424             return new Pair<>(IFD_FORMAT_STRING, -1);
3425         }
3426         try {
3427             Long longValue = Long.parseLong(entryValue);
3428             if (longValue >= 0 && longValue <= 65535) {
3429                 return new Pair<>(IFD_FORMAT_USHORT, IFD_FORMAT_ULONG);
3430             }
3431             if (longValue < 0) {
3432                 return new Pair<>(IFD_FORMAT_SLONG, -1);
3433             }
3434             return new Pair<>(IFD_FORMAT_ULONG, -1);
3435         } catch (NumberFormatException e) {
3436             // Ignored
3437         }
3438         try {
3439             Double.parseDouble(entryValue);
3440             return new Pair<>(IFD_FORMAT_DOUBLE, -1);
3441         } catch (NumberFormatException e) {
3442             // Ignored
3443         }
3444         return new Pair<>(IFD_FORMAT_STRING, -1);
3445     }
3446 
3447     // An input stream to parse EXIF data area, which can be written in either little or big endian
3448     // order.
3449     private static class ByteOrderedDataInputStream extends InputStream implements DataInput {
3450         private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN;
3451         private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN;
3452 
3453         private DataInputStream mDataInputStream;
3454         private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN;
3455         private final int mLength;
3456         private int mPosition;
3457 
ByteOrderedDataInputStream(InputStream in)3458         public ByteOrderedDataInputStream(InputStream in) throws IOException {
3459             mDataInputStream = new DataInputStream(in);
3460             mLength = mDataInputStream.available();
3461             mPosition = 0;
3462             mDataInputStream.mark(mLength);
3463         }
3464 
ByteOrderedDataInputStream(byte[] bytes)3465         public ByteOrderedDataInputStream(byte[] bytes) throws IOException {
3466             this(new ByteArrayInputStream(bytes));
3467         }
3468 
setByteOrder(ByteOrder byteOrder)3469         public void setByteOrder(ByteOrder byteOrder) {
3470             mByteOrder = byteOrder;
3471         }
3472 
seek(long byteCount)3473         public void seek(long byteCount) throws IOException {
3474             if (mPosition > byteCount) {
3475                 mPosition = 0;
3476                 mDataInputStream.reset();
3477                 mDataInputStream.mark(mLength);
3478             } else {
3479                 byteCount -= mPosition;
3480             }
3481 
3482             if (skipBytes((int) byteCount) != (int) byteCount) {
3483                 throw new IOException("Couldn't seek up to the byteCount");
3484             }
3485         }
3486 
peek()3487         public int peek() {
3488             return mPosition;
3489         }
3490 
3491         @Override
available()3492         public int available() throws IOException {
3493             return mDataInputStream.available();
3494         }
3495 
3496         @Override
read()3497         public int read() throws IOException {
3498             ++mPosition;
3499             return mDataInputStream.read();
3500         }
3501 
3502         @Override
read(byte[] b, int off, int len)3503         public int read(byte[] b, int off, int len) throws IOException {
3504             int bytesRead = mDataInputStream.read(b, off, len);
3505             mPosition += bytesRead;
3506             return bytesRead;
3507         }
3508 
3509         @Override
readUnsignedByte()3510         public int readUnsignedByte() throws IOException {
3511             ++mPosition;
3512             return mDataInputStream.readUnsignedByte();
3513         }
3514 
3515         @Override
readLine()3516         public String readLine() throws IOException {
3517             Log.d(TAG, "Currently unsupported");
3518             return null;
3519         }
3520 
3521         @Override
readBoolean()3522         public boolean readBoolean() throws IOException {
3523             ++mPosition;
3524             return mDataInputStream.readBoolean();
3525         }
3526 
3527         @Override
readChar()3528         public char readChar() throws IOException {
3529             mPosition += 2;
3530             return mDataInputStream.readChar();
3531         }
3532 
3533         @Override
readUTF()3534         public String readUTF() throws IOException {
3535             mPosition += 2;
3536             return mDataInputStream.readUTF();
3537         }
3538 
3539         @Override
readFully(byte[] buffer, int offset, int length)3540         public void readFully(byte[] buffer, int offset, int length) throws IOException {
3541             mPosition += length;
3542             if (mPosition > mLength) {
3543                 throw new EOFException();
3544             }
3545             if (mDataInputStream.read(buffer, offset, length) != length) {
3546                 throw new IOException("Couldn't read up to the length of buffer");
3547             }
3548         }
3549 
3550         @Override
readFully(byte[] buffer)3551         public void readFully(byte[] buffer) throws IOException {
3552             mPosition += buffer.length;
3553             if (mPosition > mLength) {
3554                 throw new EOFException();
3555             }
3556             if (mDataInputStream.read(buffer, 0, buffer.length) != buffer.length) {
3557                 throw new IOException("Couldn't read up to the length of buffer");
3558             }
3559         }
3560 
3561         @Override
readByte()3562         public byte readByte() throws IOException {
3563             ++mPosition;
3564             if (mPosition > mLength) {
3565                 throw new EOFException();
3566             }
3567             int ch = mDataInputStream.read();
3568             if (ch < 0) {
3569                 throw new EOFException();
3570             }
3571             return (byte) ch;
3572         }
3573 
3574         @Override
readShort()3575         public short readShort() throws IOException {
3576             mPosition += 2;
3577             if (mPosition > mLength) {
3578                 throw new EOFException();
3579             }
3580             int ch1 = mDataInputStream.read();
3581             int ch2 = mDataInputStream.read();
3582             if ((ch1 | ch2) < 0) {
3583                 throw new EOFException();
3584             }
3585             if (mByteOrder == LITTLE_ENDIAN) {
3586                 return (short) ((ch2 << 8) + (ch1));
3587             } else if (mByteOrder == BIG_ENDIAN) {
3588                 return (short) ((ch1 << 8) + (ch2));
3589             }
3590             throw new IOException("Invalid byte order: " + mByteOrder);
3591         }
3592 
3593         @Override
readInt()3594         public int readInt() throws IOException {
3595             mPosition += 4;
3596             if (mPosition > mLength) {
3597                 throw new EOFException();
3598             }
3599             int ch1 = mDataInputStream.read();
3600             int ch2 = mDataInputStream.read();
3601             int ch3 = mDataInputStream.read();
3602             int ch4 = mDataInputStream.read();
3603             if ((ch1 | ch2 | ch3 | ch4) < 0) {
3604                 throw new EOFException();
3605             }
3606             if (mByteOrder == LITTLE_ENDIAN) {
3607                 return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1);
3608             } else if (mByteOrder == BIG_ENDIAN) {
3609                 return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4);
3610             }
3611             throw new IOException("Invalid byte order: " + mByteOrder);
3612         }
3613 
3614         @Override
skipBytes(int byteCount)3615         public int skipBytes(int byteCount) throws IOException {
3616             int totalSkip = Math.min(byteCount, mLength - mPosition);
3617             int skipped = 0;
3618             while (skipped < totalSkip) {
3619                 skipped += mDataInputStream.skipBytes(totalSkip - skipped);
3620             }
3621             mPosition += skipped;
3622             return skipped;
3623         }
3624 
3625         @Override
readUnsignedShort()3626         public int readUnsignedShort() throws IOException {
3627             mPosition += 2;
3628             if (mPosition > mLength) {
3629                 throw new EOFException();
3630             }
3631             int ch1 = mDataInputStream.read();
3632             int ch2 = mDataInputStream.read();
3633             if ((ch1 | ch2) < 0) {
3634                 throw new EOFException();
3635             }
3636             if (mByteOrder == LITTLE_ENDIAN) {
3637                 return ((ch2 << 8) + (ch1));
3638             } else if (mByteOrder == BIG_ENDIAN) {
3639                 return ((ch1 << 8) + (ch2));
3640             }
3641             throw new IOException("Invalid byte order: " + mByteOrder);
3642         }
3643 
readUnsignedInt()3644         public long readUnsignedInt() throws IOException {
3645             return readInt() & 0xffffffffL;
3646         }
3647 
3648         @Override
readLong()3649         public long readLong() throws IOException {
3650             mPosition += 8;
3651             if (mPosition > mLength) {
3652                 throw new EOFException();
3653             }
3654             int ch1 = mDataInputStream.read();
3655             int ch2 = mDataInputStream.read();
3656             int ch3 = mDataInputStream.read();
3657             int ch4 = mDataInputStream.read();
3658             int ch5 = mDataInputStream.read();
3659             int ch6 = mDataInputStream.read();
3660             int ch7 = mDataInputStream.read();
3661             int ch8 = mDataInputStream.read();
3662             if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) {
3663                 throw new EOFException();
3664             }
3665             if (mByteOrder == LITTLE_ENDIAN) {
3666                 return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40)
3667                         + ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16)
3668                         + ((long) ch2 << 8) + (long) ch1);
3669             } else if (mByteOrder == BIG_ENDIAN) {
3670                 return (((long) ch1 << 56) + ((long) ch2 << 48) + ((long) ch3 << 40)
3671                         + ((long) ch4 << 32) + ((long) ch5 << 24) + ((long) ch6 << 16)
3672                         + ((long) ch7 << 8) + (long) ch8);
3673             }
3674             throw new IOException("Invalid byte order: " + mByteOrder);
3675         }
3676 
3677         @Override
readFloat()3678         public float readFloat() throws IOException {
3679             return Float.intBitsToFloat(readInt());
3680         }
3681 
3682         @Override
readDouble()3683         public double readDouble() throws IOException {
3684             return Double.longBitsToDouble(readLong());
3685         }
3686     }
3687 
3688     // An output stream to write EXIF data area, which can be written in either little or big endian
3689     // order.
3690     private static class ByteOrderedDataOutputStream extends FilterOutputStream {
3691         private final OutputStream mOutputStream;
3692         private ByteOrder mByteOrder;
3693 
ByteOrderedDataOutputStream(OutputStream out, ByteOrder byteOrder)3694         public ByteOrderedDataOutputStream(OutputStream out, ByteOrder byteOrder) {
3695             super(out);
3696             mOutputStream = out;
3697             mByteOrder = byteOrder;
3698         }
3699 
setByteOrder(ByteOrder byteOrder)3700         public void setByteOrder(ByteOrder byteOrder) {
3701             mByteOrder = byteOrder;
3702         }
3703 
3704         @Override
write(byte[] bytes)3705         public void write(byte[] bytes) throws IOException {
3706             mOutputStream.write(bytes);
3707         }
3708 
3709         @Override
write(byte[] bytes, int offset, int length)3710         public void write(byte[] bytes, int offset, int length) throws IOException {
3711             mOutputStream.write(bytes, offset, length);
3712         }
3713 
writeByte(int val)3714         public void writeByte(int val) throws IOException {
3715             mOutputStream.write(val);
3716         }
3717 
writeShort(short val)3718         public void writeShort(short val) throws IOException {
3719             if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
3720                 mOutputStream.write((val >>> 0) & 0xFF);
3721                 mOutputStream.write((val >>> 8) & 0xFF);
3722             } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
3723                 mOutputStream.write((val >>> 8) & 0xFF);
3724                 mOutputStream.write((val >>> 0) & 0xFF);
3725             }
3726         }
3727 
writeInt(int val)3728         public void writeInt(int val) throws IOException {
3729             if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
3730                 mOutputStream.write((val >>> 0) & 0xFF);
3731                 mOutputStream.write((val >>> 8) & 0xFF);
3732                 mOutputStream.write((val >>> 16) & 0xFF);
3733                 mOutputStream.write((val >>> 24) & 0xFF);
3734             } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
3735                 mOutputStream.write((val >>> 24) & 0xFF);
3736                 mOutputStream.write((val >>> 16) & 0xFF);
3737                 mOutputStream.write((val >>> 8) & 0xFF);
3738                 mOutputStream.write((val >>> 0) & 0xFF);
3739             }
3740         }
3741 
writeUnsignedShort(int val)3742         public void writeUnsignedShort(int val) throws IOException {
3743             writeShort((short) val);
3744         }
3745 
writeUnsignedInt(long val)3746         public void writeUnsignedInt(long val) throws IOException {
3747             writeInt((int) val);
3748         }
3749     }
3750 
3751     // Swaps image data based on image size
swapBasedOnImageSize(@fdType int firstIfdType, @IfdType int secondIfdType)3752     private void swapBasedOnImageSize(@IfdType int firstIfdType, @IfdType int secondIfdType)
3753             throws IOException {
3754         if (mAttributes[firstIfdType].isEmpty() || mAttributes[secondIfdType].isEmpty()) {
3755             if (DEBUG) {
3756                 Log.d(TAG, "Cannot perform swap since only one image data exists");
3757             }
3758             return;
3759         }
3760 
3761         ExifAttribute firstImageLengthAttribute =
3762                 (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_LENGTH);
3763         ExifAttribute firstImageWidthAttribute =
3764                 (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_WIDTH);
3765         ExifAttribute secondImageLengthAttribute =
3766                 (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_LENGTH);
3767         ExifAttribute secondImageWidthAttribute =
3768                 (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_WIDTH);
3769 
3770         if (firstImageLengthAttribute == null || firstImageWidthAttribute == null) {
3771             if (DEBUG) {
3772                 Log.d(TAG, "First image does not contain valid size information");
3773             }
3774         } else if (secondImageLengthAttribute == null || secondImageWidthAttribute == null) {
3775             if (DEBUG) {
3776                 Log.d(TAG, "Second image does not contain valid size information");
3777             }
3778         } else {
3779             int firstImageLengthValue = firstImageLengthAttribute.getIntValue(mExifByteOrder);
3780             int firstImageWidthValue = firstImageWidthAttribute.getIntValue(mExifByteOrder);
3781             int secondImageLengthValue = secondImageLengthAttribute.getIntValue(mExifByteOrder);
3782             int secondImageWidthValue = secondImageWidthAttribute.getIntValue(mExifByteOrder);
3783 
3784             if (firstImageLengthValue < secondImageLengthValue &&
3785                     firstImageWidthValue < secondImageWidthValue) {
3786                 HashMap<String, ExifAttribute> tempMap = mAttributes[firstIfdType];
3787                 mAttributes[firstIfdType] = mAttributes[secondIfdType];
3788                 mAttributes[secondIfdType] = tempMap;
3789             }
3790         }
3791     }
3792 
3793     /**
3794      * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null.
3795      */
closeQuietly(Closeable closeable)3796     private static void closeQuietly(Closeable closeable) {
3797         if (closeable != null) {
3798             try {
3799                 closeable.close();
3800             } catch (RuntimeException rethrown) {
3801                 throw rethrown;
3802             } catch (Exception ignored) {
3803             }
3804         }
3805     }
3806 
3807     /**
3808      * Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed.
3809      * Returns the total number of bytes transferred.
3810      */
copy(InputStream in, OutputStream out)3811     private static int copy(InputStream in, OutputStream out) throws IOException {
3812         int total = 0;
3813         byte[] buffer = new byte[8192];
3814         int c;
3815         while ((c = in.read(buffer)) != -1) {
3816             total += c;
3817             out.write(buffer, 0, c);
3818         }
3819         return total;
3820     }
3821 
3822     /**
3823      * Convert given int[] to long[]. If long[] is given, just return it.
3824      * Return null for other types of input.
3825      */
convertToLongArray(Object inputObj)3826     private static long[] convertToLongArray(Object inputObj) {
3827         if (inputObj instanceof int[]) {
3828             int[] input = (int[]) inputObj;
3829             long[] result = new long[input.length];
3830             for (int i = 0; i < input.length; i++) {
3831                 result[i] = input[i];
3832             }
3833             return result;
3834         } else if (inputObj instanceof long[]) {
3835             return (long[]) inputObj;
3836         }
3837         return null;
3838     }
3839 }
3840