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