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