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.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     // The following values used for indicating a thumbnail position.
1349     private int mThumbnailOffset;
1350     private int mThumbnailLength;
1351     private byte[] mThumbnailBytes;
1352     private int mThumbnailCompression;
1353     private int mExifOffset;
1354     private int mOrfMakerNoteOffset;
1355     private int mOrfThumbnailOffset;
1356     private int mOrfThumbnailLength;
1357     private int mRw2JpgFromRawOffset;
1358     private boolean mIsSupportedFile;
1359     private boolean mModified;
1360 
1361     // Pattern to check non zero timestamp
1362     private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
1363     // Pattern to check gps timestamp
1364     private static final Pattern sGpsTimestampPattern =
1365             Pattern.compile("^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$");
1366 
1367     /**
1368      * Reads Exif tags from the specified image file.
1369      */
ExifInterface(@onNull File file)1370     public ExifInterface(@NonNull File file) throws IOException {
1371         if (file == null) {
1372             throw new NullPointerException("file cannot be null");
1373         }
1374         initForFilename(file.getAbsolutePath());
1375     }
1376 
1377     /**
1378      * Reads Exif tags from the specified image file.
1379      */
ExifInterface(@onNull String filename)1380     public ExifInterface(@NonNull String filename) throws IOException {
1381         if (filename == null) {
1382             throw new NullPointerException("filename cannot be null");
1383         }
1384         initForFilename(filename);
1385     }
1386 
1387     /**
1388      * Reads Exif tags from the specified image file descriptor. Attribute mutation is supported
1389      * for writable and seekable file descriptors only. This constructor will not rewind the offset
1390      * of the given file descriptor. Developers should close the file descriptor after use.
1391      */
ExifInterface(@onNull FileDescriptor fileDescriptor)1392     public ExifInterface(@NonNull FileDescriptor fileDescriptor) throws IOException {
1393         if (fileDescriptor == null) {
1394             throw new NullPointerException("fileDescriptor cannot be null");
1395         }
1396         mAssetInputStream = null;
1397         mFilename = null;
1398         // When FileDescriptor is duplicated and set to FileInputStream, ownership needs to be
1399         // clarified in order for garbage collection to take place.
1400         boolean isFdOwner = false;
1401         if (isSeekableFD(fileDescriptor)) {
1402             mSeekableFileDescriptor = fileDescriptor;
1403             // Keep the original file descriptor in order to save attributes when it's seekable.
1404             // Otherwise, just close the given file descriptor after reading it because the save
1405             // feature won't be working.
1406             try {
1407                 fileDescriptor = Os.dup(fileDescriptor);
1408                 isFdOwner = true;
1409             } catch (ErrnoException e) {
1410                 throw e.rethrowAsIOException();
1411             }
1412         } else {
1413             mSeekableFileDescriptor = null;
1414         }
1415         mIsInputStream = false;
1416         FileInputStream in = null;
1417         try {
1418             in = new FileInputStream(fileDescriptor, isFdOwner);
1419             loadAttributes(in);
1420         } finally {
1421             IoUtils.closeQuietly(in);
1422         }
1423     }
1424 
1425     /**
1426      * Reads Exif tags from the specified image input stream. Attribute mutation is not supported
1427      * for input streams. The given input stream will proceed its current position. Developers
1428      * should close the input stream after use.
1429      */
ExifInterface(@onNull InputStream inputStream)1430     public ExifInterface(@NonNull InputStream inputStream) throws IOException {
1431         if (inputStream == null) {
1432             throw new NullPointerException("inputStream cannot be null");
1433         }
1434         mFilename = null;
1435         if (inputStream instanceof AssetManager.AssetInputStream) {
1436             mAssetInputStream = (AssetManager.AssetInputStream) inputStream;
1437             mSeekableFileDescriptor = null;
1438         } else if (inputStream instanceof FileInputStream
1439                 && isSeekableFD(((FileInputStream) inputStream).getFD())) {
1440             mAssetInputStream = null;
1441             mSeekableFileDescriptor = ((FileInputStream) inputStream).getFD();
1442         } else {
1443             mAssetInputStream = null;
1444             mSeekableFileDescriptor = null;
1445         }
1446         mIsInputStream = true;
1447         loadAttributes(inputStream);
1448     }
1449 
1450     /**
1451      * Returns the EXIF attribute of the specified tag or {@code null} if there is no such tag in
1452      * the image file.
1453      *
1454      * @param tag the name of the tag.
1455      */
getExifAttribute(@onNull String tag)1456     private @Nullable ExifAttribute getExifAttribute(@NonNull String tag) {
1457         if (tag == null) {
1458             throw new NullPointerException("tag shouldn't be null");
1459         }
1460         // Retrieves all tag groups. The value from primary image tag group has a higher priority
1461         // than the value from the thumbnail tag group if there are more than one candidates.
1462         for (int i = 0; i < EXIF_TAGS.length; ++i) {
1463             Object value = mAttributes[i].get(tag);
1464             if (value != null) {
1465                 return (ExifAttribute) value;
1466             }
1467         }
1468         return null;
1469     }
1470 
1471     /**
1472      * Returns the value of the specified tag or {@code null} if there
1473      * is no such tag in the image file.
1474      *
1475      * @param tag the name of the tag.
1476      */
getAttribute(@onNull String tag)1477     public @Nullable String getAttribute(@NonNull String tag) {
1478         if (tag == null) {
1479             throw new NullPointerException("tag shouldn't be null");
1480         }
1481         ExifAttribute attribute = getExifAttribute(tag);
1482         if (attribute != null) {
1483             if (!sTagSetForCompatibility.contains(tag)) {
1484                 return attribute.getStringValue(mExifByteOrder);
1485             }
1486             if (tag.equals(TAG_GPS_TIMESTAMP)) {
1487                 // Convert the rational values to the custom formats for backwards compatibility.
1488                 if (attribute.format != IFD_FORMAT_URATIONAL
1489                         && attribute.format != IFD_FORMAT_SRATIONAL) {
1490                     return null;
1491                 }
1492                 Rational[] array = (Rational[]) attribute.getValue(mExifByteOrder);
1493                 if (array.length != 3) {
1494                     return null;
1495                 }
1496                 return String.format("%02d:%02d:%02d",
1497                         (int) ((float) array[0].numerator / array[0].denominator),
1498                         (int) ((float) array[1].numerator / array[1].denominator),
1499                         (int) ((float) array[2].numerator / array[2].denominator));
1500             }
1501             try {
1502                 return Double.toString(attribute.getDoubleValue(mExifByteOrder));
1503             } catch (NumberFormatException e) {
1504                 return null;
1505             }
1506         }
1507         return null;
1508     }
1509 
1510     /**
1511      * Returns the integer value of the specified tag. If there is no such tag
1512      * in the image file or the value cannot be parsed as integer, return
1513      * <var>defaultValue</var>.
1514      *
1515      * @param tag the name of the tag.
1516      * @param defaultValue the value to return if the tag is not available.
1517      */
getAttributeInt(@onNull String tag, int defaultValue)1518     public int getAttributeInt(@NonNull String tag, int defaultValue) {
1519         if (tag == null) {
1520             throw new NullPointerException("tag shouldn't be null");
1521         }
1522         ExifAttribute exifAttribute = getExifAttribute(tag);
1523         if (exifAttribute == null) {
1524             return defaultValue;
1525         }
1526 
1527         try {
1528             return exifAttribute.getIntValue(mExifByteOrder);
1529         } catch (NumberFormatException e) {
1530             return defaultValue;
1531         }
1532     }
1533 
1534     /**
1535      * Returns the double value of the tag that is specified as rational or contains a
1536      * double-formatted value. If there is no such tag in the image file or the value cannot be
1537      * parsed as double, return <var>defaultValue</var>.
1538      *
1539      * @param tag the name of the tag.
1540      * @param defaultValue the value to return if the tag is not available.
1541      */
getAttributeDouble(@onNull String tag, double defaultValue)1542     public double getAttributeDouble(@NonNull String tag, double defaultValue) {
1543         if (tag == null) {
1544             throw new NullPointerException("tag shouldn't be null");
1545         }
1546         ExifAttribute exifAttribute = getExifAttribute(tag);
1547         if (exifAttribute == null) {
1548             return defaultValue;
1549         }
1550 
1551         try {
1552             return exifAttribute.getDoubleValue(mExifByteOrder);
1553         } catch (NumberFormatException e) {
1554             return defaultValue;
1555         }
1556     }
1557 
1558     /**
1559      * Set the value of the specified tag.
1560      *
1561      * @param tag the name of the tag.
1562      * @param value the value of the tag.
1563      */
setAttribute(@onNull String tag, @Nullable String value)1564     public void setAttribute(@NonNull String tag, @Nullable String value) {
1565         if (tag == null) {
1566             throw new NullPointerException("tag shouldn't be null");
1567         }
1568         // Convert the given value to rational values for backwards compatibility.
1569         if (value != null && sTagSetForCompatibility.contains(tag)) {
1570             if (tag.equals(TAG_GPS_TIMESTAMP)) {
1571                 Matcher m = sGpsTimestampPattern.matcher(value);
1572                 if (!m.find()) {
1573                     Log.w(TAG, "Invalid value for " + tag + " : " + value);
1574                     return;
1575                 }
1576                 value = Integer.parseInt(m.group(1)) + "/1," + Integer.parseInt(m.group(2)) + "/1,"
1577                         + Integer.parseInt(m.group(3)) + "/1";
1578             } else {
1579                 try {
1580                     double doubleValue = Double.parseDouble(value);
1581                     value = (long) (doubleValue * 10000L) + "/10000";
1582                 } catch (NumberFormatException e) {
1583                     Log.w(TAG, "Invalid value for " + tag + " : " + value);
1584                     return;
1585                 }
1586             }
1587         }
1588 
1589         for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1590             if (i == IFD_TYPE_THUMBNAIL && !mHasThumbnail) {
1591                 continue;
1592             }
1593             final Object obj = sExifTagMapsForWriting[i].get(tag);
1594             if (obj != null) {
1595                 if (value == null) {
1596                     mAttributes[i].remove(tag);
1597                     continue;
1598                 }
1599                 final ExifTag exifTag = (ExifTag) obj;
1600                 Pair<Integer, Integer> guess = guessDataFormat(value);
1601                 int dataFormat;
1602                 if (exifTag.primaryFormat == guess.first || exifTag.primaryFormat == guess.second) {
1603                     dataFormat = exifTag.primaryFormat;
1604                 } else if (exifTag.secondaryFormat != -1 && (exifTag.secondaryFormat == guess.first
1605                         || exifTag.secondaryFormat == guess.second)) {
1606                     dataFormat = exifTag.secondaryFormat;
1607                 } else if (exifTag.primaryFormat == IFD_FORMAT_BYTE
1608                         || exifTag.primaryFormat == IFD_FORMAT_UNDEFINED
1609                         || exifTag.primaryFormat == IFD_FORMAT_STRING) {
1610                     dataFormat = exifTag.primaryFormat;
1611                 } else {
1612                     if (DEBUG) {
1613                         Log.d(TAG, "Given tag (" + tag
1614                                 + ") value didn't match with one of expected "
1615                                 + "formats: " + IFD_FORMAT_NAMES[exifTag.primaryFormat]
1616                                 + (exifTag.secondaryFormat == -1 ? "" : ", "
1617                                 + IFD_FORMAT_NAMES[exifTag.secondaryFormat]) + " (guess: "
1618                                 + IFD_FORMAT_NAMES[guess.first] + (guess.second == -1 ? "" : ", "
1619                                 + IFD_FORMAT_NAMES[guess.second]) + ")");
1620                     }
1621                     continue;
1622                 }
1623                 switch (dataFormat) {
1624                     case IFD_FORMAT_BYTE: {
1625                         mAttributes[i].put(tag, ExifAttribute.createByte(value));
1626                         break;
1627                     }
1628                     case IFD_FORMAT_UNDEFINED:
1629                     case IFD_FORMAT_STRING: {
1630                         mAttributes[i].put(tag, ExifAttribute.createString(value));
1631                         break;
1632                     }
1633                     case IFD_FORMAT_USHORT: {
1634                         final String[] values = value.split(",");
1635                         final int[] intArray = new int[values.length];
1636                         for (int j = 0; j < values.length; ++j) {
1637                             intArray[j] = Integer.parseInt(values[j]);
1638                         }
1639                         mAttributes[i].put(tag,
1640                                 ExifAttribute.createUShort(intArray, mExifByteOrder));
1641                         break;
1642                     }
1643                     case IFD_FORMAT_SLONG: {
1644                         final String[] values = value.split(",");
1645                         final int[] intArray = new int[values.length];
1646                         for (int j = 0; j < values.length; ++j) {
1647                             intArray[j] = Integer.parseInt(values[j]);
1648                         }
1649                         mAttributes[i].put(tag,
1650                                 ExifAttribute.createSLong(intArray, mExifByteOrder));
1651                         break;
1652                     }
1653                     case IFD_FORMAT_ULONG: {
1654                         final String[] values = value.split(",");
1655                         final long[] longArray = new long[values.length];
1656                         for (int j = 0; j < values.length; ++j) {
1657                             longArray[j] = Long.parseLong(values[j]);
1658                         }
1659                         mAttributes[i].put(tag,
1660                                 ExifAttribute.createULong(longArray, mExifByteOrder));
1661                         break;
1662                     }
1663                     case IFD_FORMAT_URATIONAL: {
1664                         final String[] values = value.split(",");
1665                         final Rational[] rationalArray = new Rational[values.length];
1666                         for (int j = 0; j < values.length; ++j) {
1667                             final String[] numbers = values[j].split("/");
1668                             rationalArray[j] = new Rational((long) Double.parseDouble(numbers[0]),
1669                                     (long) Double.parseDouble(numbers[1]));
1670                         }
1671                         mAttributes[i].put(tag,
1672                                 ExifAttribute.createURational(rationalArray, mExifByteOrder));
1673                         break;
1674                     }
1675                     case IFD_FORMAT_SRATIONAL: {
1676                         final String[] values = value.split(",");
1677                         final Rational[] rationalArray = new Rational[values.length];
1678                         for (int j = 0; j < values.length; ++j) {
1679                             final String[] numbers = values[j].split("/");
1680                             rationalArray[j] = new Rational((long) Double.parseDouble(numbers[0]),
1681                                     (long) Double.parseDouble(numbers[1]));
1682                         }
1683                         mAttributes[i].put(tag,
1684                                 ExifAttribute.createSRational(rationalArray, mExifByteOrder));
1685                         break;
1686                     }
1687                     case IFD_FORMAT_DOUBLE: {
1688                         final String[] values = value.split(",");
1689                         final double[] doubleArray = new double[values.length];
1690                         for (int j = 0; j < values.length; ++j) {
1691                             doubleArray[j] = Double.parseDouble(values[j]);
1692                         }
1693                         mAttributes[i].put(tag,
1694                                 ExifAttribute.createDouble(doubleArray, mExifByteOrder));
1695                         break;
1696                     }
1697                     default:
1698                         if (DEBUG) {
1699                             Log.d(TAG, "Data format isn't one of expected formats: " + dataFormat);
1700                         }
1701                         continue;
1702                 }
1703             }
1704         }
1705     }
1706 
1707     /**
1708      * Update the values of the tags in the tag groups if any value for the tag already was stored.
1709      *
1710      * @param tag the name of the tag.
1711      * @param value the value of the tag in a form of {@link ExifAttribute}.
1712      * @return Returns {@code true} if updating is placed.
1713      */
updateAttribute(String tag, ExifAttribute value)1714     private boolean updateAttribute(String tag, ExifAttribute value) {
1715         boolean updated = false;
1716         for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1717             if (mAttributes[i].containsKey(tag)) {
1718                 mAttributes[i].put(tag, value);
1719                 updated = true;
1720             }
1721         }
1722         return updated;
1723     }
1724 
1725     /**
1726      * Remove any values of the specified tag.
1727      *
1728      * @param tag the name of the tag.
1729      */
removeAttribute(String tag)1730     private void removeAttribute(String tag) {
1731         for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1732             mAttributes[i].remove(tag);
1733         }
1734     }
1735 
1736     /**
1737      * This function decides which parser to read the image data according to the given input stream
1738      * type and the content of the input stream. In each case, it reads the first three bytes to
1739      * determine whether the image data format is JPEG or not.
1740      */
loadAttributes(@onNull InputStream in)1741     private void loadAttributes(@NonNull InputStream in) throws IOException {
1742         if (in == null) {
1743             throw new NullPointerException("inputstream shouldn't be null");
1744         }
1745         try {
1746             // Initialize mAttributes.
1747             for (int i = 0; i < EXIF_TAGS.length; ++i) {
1748                 mAttributes[i] = new HashMap();
1749             }
1750 
1751             // Check file type
1752             in = new BufferedInputStream(in, SIGNATURE_CHECK_SIZE);
1753             mMimeType = getMimeType((BufferedInputStream) in);
1754 
1755             // Create byte-ordered input stream
1756             ByteOrderedDataInputStream inputStream = new ByteOrderedDataInputStream(in);
1757 
1758             switch (mMimeType) {
1759                 case IMAGE_TYPE_JPEG: {
1760                     getJpegAttributes(inputStream, 0, IFD_TYPE_PRIMARY); // 0 is offset
1761                     break;
1762                 }
1763                 case IMAGE_TYPE_RAF: {
1764                     getRafAttributes(inputStream);
1765                     break;
1766                 }
1767                 case IMAGE_TYPE_HEIF: {
1768                     getHeifAttributes(inputStream);
1769                     break;
1770                 }
1771                 case IMAGE_TYPE_ORF: {
1772                     getOrfAttributes(inputStream);
1773                     break;
1774                 }
1775                 case IMAGE_TYPE_RW2: {
1776                     getRw2Attributes(inputStream);
1777                     break;
1778                 }
1779                 case IMAGE_TYPE_ARW:
1780                 case IMAGE_TYPE_CR2:
1781                 case IMAGE_TYPE_DNG:
1782                 case IMAGE_TYPE_NEF:
1783                 case IMAGE_TYPE_NRW:
1784                 case IMAGE_TYPE_PEF:
1785                 case IMAGE_TYPE_SRW:
1786                 case IMAGE_TYPE_UNKNOWN: {
1787                     getRawAttributes(inputStream);
1788                     break;
1789                 }
1790                 default: {
1791                     break;
1792                 }
1793             }
1794             // Set thumbnail image offset and length
1795             setThumbnailData(inputStream);
1796             mIsSupportedFile = true;
1797         } catch (IOException | OutOfMemoryError e) {
1798             // Ignore exceptions in order to keep the compatibility with the old versions of
1799             // ExifInterface.
1800             mIsSupportedFile = false;
1801             Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file"
1802                     + "(ExifInterface supports JPEG and some RAW image formats only) "
1803                     + "or a corrupted JPEG file to ExifInterface.", e);
1804         } finally {
1805             addDefaultValuesForCompatibility();
1806 
1807             if (DEBUG) {
1808                 printAttributes();
1809             }
1810         }
1811     }
1812 
isSeekableFD(FileDescriptor fd)1813     private static boolean isSeekableFD(FileDescriptor fd) throws IOException {
1814         try {
1815             Os.lseek(fd, 0, OsConstants.SEEK_CUR);
1816             return true;
1817         } catch (ErrnoException e) {
1818             return false;
1819         }
1820     }
1821 
1822     // Prints out attributes for debugging.
printAttributes()1823     private void printAttributes() {
1824         for (int i = 0; i < mAttributes.length; ++i) {
1825             Log.d(TAG, "The size of tag group[" + i + "]: " + mAttributes[i].size());
1826             for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
1827                 final ExifAttribute tagValue = (ExifAttribute) entry.getValue();
1828                 Log.d(TAG, "tagName: " + entry.getKey() + ", tagType: " + tagValue.toString()
1829                         + ", tagValue: '" + tagValue.getStringValue(mExifByteOrder) + "'");
1830             }
1831         }
1832     }
1833 
1834     /**
1835      * Save the tag data into the original image file. This is expensive because
1836      * it involves copying all the data from one file to another and deleting
1837      * the old file and renaming the other. It's best to use
1838      * {@link #setAttribute(String,String)} to set all attributes to write and
1839      * make a single call rather than multiple calls for each attribute.
1840      * <p>
1841      * This method is only supported for JPEG files.
1842      * <p class="note">
1843      * Note: after calling this method, any attempts to obtain range information
1844      * from {@link #getAttributeRange(String)} or {@link #getThumbnailRange()}
1845      * will throw {@link IllegalStateException}, since the offsets may have
1846      * changed in the newly written file.
1847      * </p>
1848      */
saveAttributes()1849     public void saveAttributes() throws IOException {
1850         if (!mIsSupportedFile || mMimeType != IMAGE_TYPE_JPEG) {
1851             throw new IOException("ExifInterface only supports saving attributes on JPEG formats.");
1852         }
1853         if (mIsInputStream || (mSeekableFileDescriptor == null && mFilename == null)) {
1854             throw new IOException(
1855                     "ExifInterface does not support saving attributes for the current input.");
1856         }
1857 
1858         // Remember the fact that we've changed the file on disk from what was
1859         // originally parsed, meaning we can't answer range questions
1860         mModified = true;
1861 
1862         // Keep the thumbnail in memory
1863         mThumbnailBytes = getThumbnail();
1864 
1865         FileInputStream in = null;
1866         FileOutputStream out = null;
1867         File tempFile = null;
1868         try {
1869             // Move the original file to temporary file.
1870             if (mFilename != null) {
1871                 tempFile = new File(mFilename + ".tmp");
1872                 File originalFile = new File(mFilename);
1873                 if (!originalFile.renameTo(tempFile)) {
1874                     throw new IOException("Could'nt rename to " + tempFile.getAbsolutePath());
1875                 }
1876             } else if (mSeekableFileDescriptor != null) {
1877                 tempFile = File.createTempFile("temp", "jpg");
1878                 Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
1879                 in = new FileInputStream(mSeekableFileDescriptor);
1880                 out = new FileOutputStream(tempFile);
1881                 Streams.copy(in, out);
1882             }
1883         } catch (ErrnoException e) {
1884             throw e.rethrowAsIOException();
1885         } finally {
1886             IoUtils.closeQuietly(in);
1887             IoUtils.closeQuietly(out);
1888         }
1889 
1890         in = null;
1891         out = null;
1892         try {
1893             // Save the new file.
1894             in = new FileInputStream(tempFile);
1895             if (mFilename != null) {
1896                 out = new FileOutputStream(mFilename);
1897             } else if (mSeekableFileDescriptor != null) {
1898                 Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
1899                 out = new FileOutputStream(mSeekableFileDescriptor);
1900             }
1901             saveJpegAttributes(in, out);
1902         } catch (ErrnoException e) {
1903             throw e.rethrowAsIOException();
1904         } finally {
1905             IoUtils.closeQuietly(in);
1906             IoUtils.closeQuietly(out);
1907             tempFile.delete();
1908         }
1909 
1910         // Discard the thumbnail in memory
1911         mThumbnailBytes = null;
1912     }
1913 
1914     /**
1915      * Returns true if the image file has a thumbnail.
1916      */
hasThumbnail()1917     public boolean hasThumbnail() {
1918         return mHasThumbnail;
1919     }
1920 
1921     /**
1922      * Returns true if the image file has the given attribute defined.
1923      *
1924      * @param tag the name of the tag.
1925      */
hasAttribute(@onNull String tag)1926     public boolean hasAttribute(@NonNull String tag) {
1927         return (getExifAttribute(tag) != null);
1928     }
1929 
1930     /**
1931      * Returns the JPEG compressed thumbnail inside the image file, or {@code null} if there is no
1932      * JPEG compressed thumbnail.
1933      * The returned data can be decoded using
1934      * {@link android.graphics.BitmapFactory#decodeByteArray(byte[],int,int)}
1935      */
getThumbnail()1936     public byte[] getThumbnail() {
1937         if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) {
1938             return getThumbnailBytes();
1939         }
1940         return null;
1941     }
1942 
1943     /**
1944      * Returns the thumbnail bytes inside the image file, regardless of the compression type of the
1945      * thumbnail image.
1946      */
getThumbnailBytes()1947     public byte[] getThumbnailBytes() {
1948         if (!mHasThumbnail) {
1949             return null;
1950         }
1951         if (mThumbnailBytes != null) {
1952             return mThumbnailBytes;
1953         }
1954 
1955         // Read the thumbnail.
1956         InputStream in = null;
1957         try {
1958             if (mAssetInputStream != null) {
1959                 in = mAssetInputStream;
1960                 if (in.markSupported()) {
1961                     in.reset();
1962                 } else {
1963                     Log.d(TAG, "Cannot read thumbnail from inputstream without mark/reset support");
1964                     return null;
1965                 }
1966             } else if (mFilename != null) {
1967                 in = new FileInputStream(mFilename);
1968             } else if (mSeekableFileDescriptor != null) {
1969                 FileDescriptor fileDescriptor = Os.dup(mSeekableFileDescriptor);
1970                 Os.lseek(fileDescriptor, 0, OsConstants.SEEK_SET);
1971                 in = new FileInputStream(fileDescriptor, true);
1972             }
1973             if (in == null) {
1974                 // Should not be reached this.
1975                 throw new FileNotFoundException();
1976             }
1977             if (in.skip(mThumbnailOffset) != mThumbnailOffset) {
1978                 throw new IOException("Corrupted image");
1979             }
1980             byte[] buffer = new byte[mThumbnailLength];
1981             if (in.read(buffer) != mThumbnailLength) {
1982                 throw new IOException("Corrupted image");
1983             }
1984             mThumbnailBytes = buffer;
1985             return buffer;
1986         } catch (IOException | ErrnoException e) {
1987             // Couldn't get a thumbnail image.
1988             Log.d(TAG, "Encountered exception while getting thumbnail", e);
1989         } finally {
1990             IoUtils.closeQuietly(in);
1991         }
1992         return null;
1993     }
1994 
1995     /**
1996      * Creates and returns a Bitmap object of the thumbnail image based on the byte array and the
1997      * thumbnail compression value, or {@code null} if the compression type is unsupported.
1998      */
getThumbnailBitmap()1999     public Bitmap getThumbnailBitmap() {
2000         if (!mHasThumbnail) {
2001             return null;
2002         } else if (mThumbnailBytes == null) {
2003             mThumbnailBytes = getThumbnailBytes();
2004         }
2005 
2006         if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) {
2007             return BitmapFactory.decodeByteArray(mThumbnailBytes, 0, mThumbnailLength);
2008         } else if (mThumbnailCompression == DATA_UNCOMPRESSED) {
2009             int[] rgbValues = new int[mThumbnailBytes.length / 3];
2010             byte alpha = (byte) 0xff000000;
2011             for (int i = 0; i < rgbValues.length; i++) {
2012                 rgbValues[i] = alpha + (mThumbnailBytes[3 * i] << 16)
2013                         + (mThumbnailBytes[3 * i + 1] << 8) + mThumbnailBytes[3 * i + 2];
2014             }
2015 
2016             ExifAttribute imageLengthAttribute =
2017                     (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_LENGTH);
2018             ExifAttribute imageWidthAttribute =
2019                     (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_WIDTH);
2020             if (imageLengthAttribute != null && imageWidthAttribute != null) {
2021                 int imageLength = imageLengthAttribute.getIntValue(mExifByteOrder);
2022                 int imageWidth = imageWidthAttribute.getIntValue(mExifByteOrder);
2023                 return Bitmap.createBitmap(
2024                         rgbValues, imageWidth, imageLength, Bitmap.Config.ARGB_8888);
2025             }
2026         }
2027         return null;
2028     }
2029 
2030     /**
2031      * Returns true if thumbnail image is JPEG Compressed, or false if either thumbnail image does
2032      * not exist or thumbnail image is uncompressed.
2033      */
isThumbnailCompressed()2034     public boolean isThumbnailCompressed() {
2035         if (!mHasThumbnail) {
2036             return false;
2037         }
2038         if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) {
2039             return true;
2040         }
2041         return false;
2042     }
2043 
2044     /**
2045      * Returns the offset and length of thumbnail inside the image file, or
2046      * {@code null} if there is no thumbnail.
2047      *
2048      * @return two-element array, the offset in the first value, and length in
2049      *         the second, or {@code null} if no thumbnail was found.
2050      * @throws IllegalStateException if {@link #saveAttributes()} has been
2051      *             called since the underlying file was initially parsed, since
2052      *             that means offsets may have changed.
2053      */
getThumbnailRange()2054     public @Nullable long[] getThumbnailRange() {
2055         if (mModified) {
2056             throw new IllegalStateException(
2057                     "The underlying file has been modified since being parsed");
2058         }
2059 
2060         if (mHasThumbnail) {
2061             return new long[] { mThumbnailOffset, mThumbnailLength };
2062         } else {
2063             return null;
2064         }
2065     }
2066 
2067     /**
2068      * Returns the offset and length of the requested tag inside the image file,
2069      * or {@code null} if the tag is not contained.
2070      *
2071      * @return two-element array, the offset in the first value, and length in
2072      *         the second, or {@code null} if no tag was found.
2073      * @throws IllegalStateException if {@link #saveAttributes()} has been
2074      *             called since the underlying file was initially parsed, since
2075      *             that means offsets may have changed.
2076      */
getAttributeRange(@onNull String tag)2077     public @Nullable long[] getAttributeRange(@NonNull String tag) {
2078         if (tag == null) {
2079             throw new NullPointerException("tag shouldn't be null");
2080         }
2081         if (mModified) {
2082             throw new IllegalStateException(
2083                     "The underlying file has been modified since being parsed");
2084         }
2085 
2086         final ExifAttribute attribute = getExifAttribute(tag);
2087         if (attribute != null) {
2088             return new long[] { attribute.bytesOffset, attribute.bytes.length };
2089         } else {
2090             return null;
2091         }
2092     }
2093 
2094     /**
2095      * Returns the raw bytes for the value of the requested tag inside the image
2096      * file, or {@code null} if the tag is not contained.
2097      *
2098      * @return raw bytes for the value of the requested tag, or {@code null} if
2099      *         no tag was found.
2100      */
getAttributeBytes(@onNull String tag)2101     public @Nullable byte[] getAttributeBytes(@NonNull String tag) {
2102         if (tag == null) {
2103             throw new NullPointerException("tag shouldn't be null");
2104         }
2105         final ExifAttribute attribute = getExifAttribute(tag);
2106         if (attribute != null) {
2107             return attribute.bytes;
2108         } else {
2109             return null;
2110         }
2111     }
2112 
2113     /**
2114      * Stores the latitude and longitude value in a float array. The first element is
2115      * the latitude, and the second element is the longitude. Returns false if the
2116      * Exif tags are not available.
2117      */
getLatLong(float output[])2118     public boolean getLatLong(float output[]) {
2119         String latValue = getAttribute(TAG_GPS_LATITUDE);
2120         String latRef = getAttribute(TAG_GPS_LATITUDE_REF);
2121         String lngValue = getAttribute(TAG_GPS_LONGITUDE);
2122         String lngRef = getAttribute(TAG_GPS_LONGITUDE_REF);
2123 
2124         if (latValue != null && latRef != null && lngValue != null && lngRef != null) {
2125             try {
2126                 output[0] = convertRationalLatLonToFloat(latValue, latRef);
2127                 output[1] = convertRationalLatLonToFloat(lngValue, lngRef);
2128                 return true;
2129             } catch (IllegalArgumentException e) {
2130                 // if values are not parseable
2131             }
2132         }
2133 
2134         return false;
2135     }
2136 
2137     /**
2138      * Return the altitude in meters. If the exif tag does not exist, return
2139      * <var>defaultValue</var>.
2140      *
2141      * @param defaultValue the value to return if the tag is not available.
2142      */
getAltitude(double defaultValue)2143     public double getAltitude(double defaultValue) {
2144         double altitude = getAttributeDouble(TAG_GPS_ALTITUDE, -1);
2145         int ref = getAttributeInt(TAG_GPS_ALTITUDE_REF, -1);
2146 
2147         if (altitude >= 0 && ref >= 0) {
2148             return (altitude * ((ref == 1) ? -1 : 1));
2149         } else {
2150             return defaultValue;
2151         }
2152     }
2153 
2154     /**
2155      * Returns parsed {@code DateTime} value, or -1 if unavailable or invalid.
2156      *
2157      * @hide
2158      */
2159     @UnsupportedAppUsage
getDateTime()2160     public @CurrentTimeMillisLong long getDateTime() {
2161         return parseDateTime(getAttribute(TAG_DATETIME),
2162                 getAttribute(TAG_SUBSEC_TIME),
2163                 getAttribute(TAG_OFFSET_TIME));
2164     }
2165 
2166     /**
2167      * Returns parsed {@code DateTimeDigitized} value, or -1 if unavailable or
2168      * invalid.
2169      *
2170      * @hide
2171      */
getDateTimeDigitized()2172     public @CurrentTimeMillisLong long getDateTimeDigitized() {
2173         return parseDateTime(getAttribute(TAG_DATETIME_DIGITIZED),
2174                 getAttribute(TAG_SUBSEC_TIME_DIGITIZED),
2175                 getAttribute(TAG_OFFSET_TIME_DIGITIZED));
2176     }
2177 
2178     /**
2179      * Returns parsed {@code DateTimeOriginal} value, or -1 if unavailable or
2180      * invalid.
2181      *
2182      * @hide
2183      */
2184     @UnsupportedAppUsage
getDateTimeOriginal()2185     public @CurrentTimeMillisLong long getDateTimeOriginal() {
2186         return parseDateTime(getAttribute(TAG_DATETIME_ORIGINAL),
2187                 getAttribute(TAG_SUBSEC_TIME_ORIGINAL),
2188                 getAttribute(TAG_OFFSET_TIME_ORIGINAL));
2189     }
2190 
parseDateTime(@ullable String dateTimeString, @Nullable String subSecs, @Nullable String offsetString)2191     private static @CurrentTimeMillisLong long parseDateTime(@Nullable String dateTimeString,
2192             @Nullable String subSecs, @Nullable String offsetString) {
2193         if (dateTimeString == null
2194                 || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
2195 
2196         ParsePosition pos = new ParsePosition(0);
2197         try {
2198             // The exif field is in local time. Parsing it as if it is UTC will yield time
2199             // since 1/1/1970 local time
2200             Date datetime = sFormatter.parse(dateTimeString, pos);
2201 
2202             if (offsetString != null) {
2203                 dateTimeString = dateTimeString + " " + offsetString;
2204                 ParsePosition position = new ParsePosition(0);
2205                 datetime = sFormatterTz.parse(dateTimeString, position);
2206             }
2207 
2208             if (datetime == null) return -1;
2209             long msecs = datetime.getTime();
2210 
2211             if (subSecs != null) {
2212                 try {
2213                     long sub = Long.parseLong(subSecs);
2214                     while (sub > 1000) {
2215                         sub /= 10;
2216                     }
2217                     msecs += sub;
2218                 } catch (NumberFormatException e) {
2219                     // Ignored
2220                 }
2221             }
2222             return msecs;
2223         } catch (IllegalArgumentException e) {
2224             return -1;
2225         }
2226     }
2227 
2228     /**
2229      * Returns number of milliseconds since Jan. 1, 1970, midnight UTC.
2230      * Returns -1 if the date time information if not available.
2231      * @hide
2232      */
2233     @UnsupportedAppUsage
getGpsDateTime()2234     public long getGpsDateTime() {
2235         String date = getAttribute(TAG_GPS_DATESTAMP);
2236         String time = getAttribute(TAG_GPS_TIMESTAMP);
2237         if (date == null || time == null
2238                 || (!sNonZeroTimePattern.matcher(date).matches()
2239                 && !sNonZeroTimePattern.matcher(time).matches())) {
2240             return -1;
2241         }
2242 
2243         String dateTimeString = date + ' ' + time;
2244 
2245         ParsePosition pos = new ParsePosition(0);
2246         try {
2247             Date datetime = sFormatter.parse(dateTimeString, pos);
2248             if (datetime == null) return -1;
2249             return datetime.getTime();
2250         } catch (IllegalArgumentException e) {
2251             return -1;
2252         }
2253     }
2254 
2255     /** {@hide} */
2256     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
convertRationalLatLonToFloat(String rationalString, String ref)2257     public static float convertRationalLatLonToFloat(String rationalString, String ref) {
2258         try {
2259             String [] parts = rationalString.split(",");
2260 
2261             String [] pair;
2262             pair = parts[0].split("/");
2263             double degrees = Double.parseDouble(pair[0].trim())
2264                     / Double.parseDouble(pair[1].trim());
2265 
2266             pair = parts[1].split("/");
2267             double minutes = Double.parseDouble(pair[0].trim())
2268                     / Double.parseDouble(pair[1].trim());
2269 
2270             pair = parts[2].split("/");
2271             double seconds = Double.parseDouble(pair[0].trim())
2272                     / Double.parseDouble(pair[1].trim());
2273 
2274             double result = degrees + (minutes / 60.0) + (seconds / 3600.0);
2275             if ((ref.equals("S") || ref.equals("W"))) {
2276                 return (float) -result;
2277             }
2278             return (float) result;
2279         } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
2280             // Not valid
2281             throw new IllegalArgumentException();
2282         }
2283     }
2284 
initForFilename(String filename)2285     private void initForFilename(String filename) throws IOException {
2286         FileInputStream in = null;
2287         mAssetInputStream = null;
2288         mFilename = filename;
2289         mIsInputStream = false;
2290         try {
2291             in = new FileInputStream(filename);
2292             if (isSeekableFD(in.getFD())) {
2293                 mSeekableFileDescriptor = in.getFD();
2294             } else {
2295                 mSeekableFileDescriptor = null;
2296             }
2297             loadAttributes(in);
2298         } finally {
2299             IoUtils.closeQuietly(in);
2300         }
2301     }
2302 
2303     // Checks the type of image file
getMimeType(BufferedInputStream in)2304     private int getMimeType(BufferedInputStream in) throws IOException {
2305         in.mark(SIGNATURE_CHECK_SIZE);
2306         byte[] signatureCheckBytes = new byte[SIGNATURE_CHECK_SIZE];
2307         in.read(signatureCheckBytes);
2308         in.reset();
2309         if (isJpegFormat(signatureCheckBytes)) {
2310             return IMAGE_TYPE_JPEG;
2311         } else if (isRafFormat(signatureCheckBytes)) {
2312             return IMAGE_TYPE_RAF;
2313         } else if (isHeifFormat(signatureCheckBytes)) {
2314             return IMAGE_TYPE_HEIF;
2315         } else if (isOrfFormat(signatureCheckBytes)) {
2316             return IMAGE_TYPE_ORF;
2317         } else if (isRw2Format(signatureCheckBytes)) {
2318             return IMAGE_TYPE_RW2;
2319         }
2320         // Certain file formats (PEF) are identified in readImageFileDirectory()
2321         return IMAGE_TYPE_UNKNOWN;
2322     }
2323 
2324     /**
2325      * This method looks at the first 3 bytes to determine if this file is a JPEG file.
2326      * See http://www.media.mit.edu/pia/Research/deepview/exif.html, "JPEG format and Marker"
2327      */
isJpegFormat(byte[] signatureCheckBytes)2328     private static boolean isJpegFormat(byte[] signatureCheckBytes) throws IOException {
2329         for (int i = 0; i < JPEG_SIGNATURE.length; i++) {
2330             if (signatureCheckBytes[i] != JPEG_SIGNATURE[i]) {
2331                 return false;
2332             }
2333         }
2334         return true;
2335     }
2336 
2337     /**
2338      * This method looks at the first 15 bytes to determine if this file is a RAF file.
2339      * There is no official specification for RAF files from Fuji, but there is an online archive of
2340      * image file specifications:
2341      * http://fileformats.archiveteam.org/wiki/Fujifilm_RAF
2342      */
isRafFormat(byte[] signatureCheckBytes)2343     private boolean isRafFormat(byte[] signatureCheckBytes) throws IOException {
2344         byte[] rafSignatureBytes = RAF_SIGNATURE.getBytes();
2345         for (int i = 0; i < rafSignatureBytes.length; i++) {
2346             if (signatureCheckBytes[i] != rafSignatureBytes[i]) {
2347                 return false;
2348             }
2349         }
2350         return true;
2351     }
2352 
isHeifFormat(byte[] signatureCheckBytes)2353     private boolean isHeifFormat(byte[] signatureCheckBytes) throws IOException {
2354         ByteOrderedDataInputStream signatureInputStream = null;
2355         try {
2356             signatureInputStream = new ByteOrderedDataInputStream(signatureCheckBytes);
2357             signatureInputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
2358 
2359             long chunkSize = signatureInputStream.readInt();
2360             byte[] chunkType = new byte[4];
2361             signatureInputStream.read(chunkType);
2362 
2363             if (!Arrays.equals(chunkType, HEIF_TYPE_FTYP)) {
2364                 return false;
2365             }
2366 
2367             long chunkDataOffset = 8;
2368             if (chunkSize == 1) {
2369                 // This indicates that the next 8 bytes represent the chunk size,
2370                 // and chunk data comes after that.
2371                 chunkSize = signatureInputStream.readLong();
2372                 if (chunkSize < 16) {
2373                     // The smallest valid chunk is 16 bytes long in this case.
2374                     return false;
2375                 }
2376                 chunkDataOffset += 8;
2377             }
2378 
2379             // only sniff up to signatureCheckBytes.length
2380             if (chunkSize > signatureCheckBytes.length) {
2381                 chunkSize = signatureCheckBytes.length;
2382             }
2383 
2384             long chunkDataSize = chunkSize - chunkDataOffset;
2385 
2386             // It should at least have major brand (4-byte) and minor version (4-byte).
2387             // The rest of the chunk (if any) is a list of (4-byte) compatible brands.
2388             if (chunkDataSize < 8) {
2389                 return false;
2390             }
2391 
2392             byte[] brand = new byte[4];
2393             boolean isMif1 = false;
2394             boolean isHeic = false;
2395             for (long i = 0; i < chunkDataSize / 4;  ++i) {
2396                 if (signatureInputStream.read(brand) != brand.length) {
2397                     return false;
2398                 }
2399                 if (i == 1) {
2400                     // Skip this index, it refers to the minorVersion, not a brand.
2401                     continue;
2402                 }
2403                 if (Arrays.equals(brand, HEIF_BRAND_MIF1)) {
2404                     isMif1 = true;
2405                 } else if (Arrays.equals(brand, HEIF_BRAND_HEIC)) {
2406                     isHeic = true;
2407                 }
2408                 if (isMif1 && isHeic) {
2409                     return true;
2410                 }
2411             }
2412         } catch (Exception e) {
2413             if (DEBUG) {
2414                 Log.d(TAG, "Exception parsing HEIF file type box.", e);
2415             }
2416         } finally {
2417             if (signatureInputStream != null) {
2418                 signatureInputStream.close();
2419                 signatureInputStream = null;
2420             }
2421         }
2422         return false;
2423     }
2424 
2425     /**
2426      * ORF has a similar structure to TIFF but it contains a different signature at the TIFF Header.
2427      * This method looks at the 2 bytes following the Byte Order bytes to determine if this file is
2428      * an ORF file.
2429      * There is no official specification for ORF files from Olympus, but there is an online archive
2430      * of image file specifications:
2431      * http://fileformats.archiveteam.org/wiki/Olympus_ORF
2432      */
isOrfFormat(byte[] signatureCheckBytes)2433     private boolean isOrfFormat(byte[] signatureCheckBytes) throws IOException {
2434         ByteOrderedDataInputStream signatureInputStream =
2435                 new ByteOrderedDataInputStream(signatureCheckBytes);
2436         // Read byte order
2437         mExifByteOrder = readByteOrder(signatureInputStream);
2438         // Set byte order
2439         signatureInputStream.setByteOrder(mExifByteOrder);
2440 
2441         short orfSignature = signatureInputStream.readShort();
2442         if (orfSignature == ORF_SIGNATURE_1 || orfSignature == ORF_SIGNATURE_2) {
2443             return true;
2444         }
2445         return false;
2446     }
2447 
2448     /**
2449      * RW2 is TIFF-based, but stores 0x55 signature byte instead of 0x42 at the header
2450      * See http://lclevy.free.fr/raw/
2451      */
isRw2Format(byte[] signatureCheckBytes)2452     private boolean isRw2Format(byte[] signatureCheckBytes) throws IOException {
2453         ByteOrderedDataInputStream signatureInputStream =
2454                 new ByteOrderedDataInputStream(signatureCheckBytes);
2455         // Read byte order
2456         mExifByteOrder = readByteOrder(signatureInputStream);
2457         // Set byte order
2458         signatureInputStream.setByteOrder(mExifByteOrder);
2459 
2460         short signatureByte = signatureInputStream.readShort();
2461         if (signatureByte == RW2_SIGNATURE) {
2462             return true;
2463         }
2464         return false;
2465     }
2466 
2467     /**
2468      * Loads EXIF attributes from a JPEG input stream.
2469      *
2470      * @param in The input stream that starts with the JPEG data.
2471      * @param jpegOffset The offset value in input stream for JPEG data.
2472      * @param imageType The image type from which to retrieve metadata. Use IFD_TYPE_PRIMARY for
2473      *                   primary image, IFD_TYPE_PREVIEW for preview image, and
2474      *                   IFD_TYPE_THUMBNAIL for thumbnail image.
2475      * @throws IOException If the data contains invalid JPEG markers, offsets, or length values.
2476      */
getJpegAttributes(ByteOrderedDataInputStream in, int jpegOffset, int imageType)2477     private void getJpegAttributes(ByteOrderedDataInputStream in, int jpegOffset, int imageType)
2478             throws IOException {
2479         // See JPEG File Interchange Format Specification, "JFIF Specification"
2480         if (DEBUG) {
2481             Log.d(TAG, "getJpegAttributes starting with: " + in);
2482         }
2483 
2484         // JPEG uses Big Endian by default. See https://people.cs.umass.edu/~verts/cs32/endian.html
2485         in.setByteOrder(ByteOrder.BIG_ENDIAN);
2486 
2487         // Skip to JPEG data
2488         in.seek(jpegOffset);
2489         int bytesRead = jpegOffset;
2490 
2491         byte marker;
2492         if ((marker = in.readByte()) != MARKER) {
2493             throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
2494         }
2495         ++bytesRead;
2496         if (in.readByte() != MARKER_SOI) {
2497             throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
2498         }
2499         ++bytesRead;
2500         while (true) {
2501             marker = in.readByte();
2502             if (marker != MARKER) {
2503                 throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff));
2504             }
2505             ++bytesRead;
2506             marker = in.readByte();
2507             if (DEBUG) {
2508                 Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff));
2509             }
2510             ++bytesRead;
2511 
2512             // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and
2513             // the image data will terminate right after.
2514             if (marker == MARKER_EOI || marker == MARKER_SOS) {
2515                 break;
2516             }
2517             int length = in.readUnsignedShort() - 2;
2518             bytesRead += 2;
2519             if (DEBUG) {
2520                 Log.d(TAG, "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: "
2521                         + (length + 2) + ")");
2522             }
2523             if (length < 0) {
2524                 throw new IOException("Invalid length");
2525             }
2526             switch (marker) {
2527                 case MARKER_APP1: {
2528                     final int start = bytesRead;
2529                     final byte[] bytes = new byte[length];
2530                     in.readFully(bytes);
2531                     bytesRead += length;
2532                     length = 0;
2533 
2534                     if (ArrayUtils.startsWith(bytes, IDENTIFIER_EXIF_APP1)) {
2535                         final long offset = start + IDENTIFIER_EXIF_APP1.length;
2536                         final byte[] value = Arrays.copyOfRange(bytes,
2537                                 IDENTIFIER_EXIF_APP1.length, bytes.length);
2538 
2539                         readExifSegment(value, imageType);
2540 
2541                         // Save offset values for createJpegThumbnailBitmap() function
2542                         mExifOffset = (int) offset;
2543                     } else if (ArrayUtils.startsWith(bytes, IDENTIFIER_XMP_APP1)) {
2544                         // See XMP Specification Part 3: Storage in Files, 1.1.3 JPEG, Table 6
2545                         final long offset = start + IDENTIFIER_XMP_APP1.length;
2546                         final byte[] value = Arrays.copyOfRange(bytes,
2547                                 IDENTIFIER_XMP_APP1.length, bytes.length);
2548 
2549                         if (getAttribute(TAG_XMP) == null) {
2550                             mAttributes[IFD_TYPE_PRIMARY].put(TAG_XMP, new ExifAttribute(
2551                                     IFD_FORMAT_BYTE, value.length, offset, value));
2552                         }
2553                     }
2554                     break;
2555                 }
2556 
2557                 case MARKER_COM: {
2558                     byte[] bytes = new byte[length];
2559                     if (in.read(bytes) != length) {
2560                         throw new IOException("Invalid exif");
2561                     }
2562                     length = 0;
2563                     if (getAttribute(TAG_USER_COMMENT) == null) {
2564                         mAttributes[IFD_TYPE_EXIF].put(TAG_USER_COMMENT, ExifAttribute.createString(
2565                                 new String(bytes, ASCII)));
2566                     }
2567                     break;
2568                 }
2569 
2570                 case MARKER_SOF0:
2571                 case MARKER_SOF1:
2572                 case MARKER_SOF2:
2573                 case MARKER_SOF3:
2574                 case MARKER_SOF5:
2575                 case MARKER_SOF6:
2576                 case MARKER_SOF7:
2577                 case MARKER_SOF9:
2578                 case MARKER_SOF10:
2579                 case MARKER_SOF11:
2580                 case MARKER_SOF13:
2581                 case MARKER_SOF14:
2582                 case MARKER_SOF15: {
2583                     if (in.skipBytes(1) != 1) {
2584                         throw new IOException("Invalid SOFx");
2585                     }
2586                     mAttributes[imageType].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong(
2587                             in.readUnsignedShort(), mExifByteOrder));
2588                     mAttributes[imageType].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong(
2589                             in.readUnsignedShort(), mExifByteOrder));
2590                     length -= 5;
2591                     break;
2592                 }
2593 
2594                 default: {
2595                     break;
2596                 }
2597             }
2598             if (length < 0) {
2599                 throw new IOException("Invalid length");
2600             }
2601             if (in.skipBytes(length) != length) {
2602                 throw new IOException("Invalid JPEG segment");
2603             }
2604             bytesRead += length;
2605         }
2606         // Restore original byte order
2607         in.setByteOrder(mExifByteOrder);
2608     }
2609 
getRawAttributes(ByteOrderedDataInputStream in)2610     private void getRawAttributes(ByteOrderedDataInputStream in) throws IOException {
2611         // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
2612         parseTiffHeaders(in, in.available());
2613 
2614         // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6.
2615         readImageFileDirectory(in, IFD_TYPE_PRIMARY);
2616 
2617         // Update ImageLength/Width tags for all image data.
2618         updateImageSizeValues(in, IFD_TYPE_PRIMARY);
2619         updateImageSizeValues(in, IFD_TYPE_PREVIEW);
2620         updateImageSizeValues(in, IFD_TYPE_THUMBNAIL);
2621 
2622         // Check if each image data is in valid position.
2623         validateImages(in);
2624 
2625         if (mMimeType == IMAGE_TYPE_PEF) {
2626             // PEF files contain a MakerNote data, which contains the data for ColorSpace tag.
2627             // See http://lclevy.free.fr/raw/ and piex.cc PefGetPreviewData()
2628             ExifAttribute makerNoteAttribute =
2629                     (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE);
2630             if (makerNoteAttribute != null) {
2631                 // Create an ordered DataInputStream for MakerNote
2632                 ByteOrderedDataInputStream makerNoteDataInputStream =
2633                         new ByteOrderedDataInputStream(makerNoteAttribute.bytes);
2634                 makerNoteDataInputStream.setByteOrder(mExifByteOrder);
2635 
2636                 // Seek to MakerNote data
2637                 makerNoteDataInputStream.seek(PEF_MAKER_NOTE_SKIP_SIZE);
2638 
2639                 // Read IFD data from MakerNote
2640                 readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_PEF);
2641 
2642                 // Update ColorSpace tag
2643                 ExifAttribute colorSpaceAttribute =
2644                         (ExifAttribute) mAttributes[IFD_TYPE_PEF].get(TAG_COLOR_SPACE);
2645                 if (colorSpaceAttribute != null) {
2646                     mAttributes[IFD_TYPE_EXIF].put(TAG_COLOR_SPACE, colorSpaceAttribute);
2647                 }
2648             }
2649         }
2650     }
2651 
2652     /**
2653      * RAF files contains a JPEG and a CFA data.
2654      * The JPEG contains two images, a preview and a thumbnail, while the CFA contains a RAW image.
2655      * This method looks at the first 160 bytes of a RAF file to retrieve the offset and length
2656      * values for the JPEG and CFA data.
2657      * Using that data, it parses the JPEG data to retrieve the preview and thumbnail image data,
2658      * then parses the CFA metadata to retrieve the primary image length/width values.
2659      * For data format details, see http://fileformats.archiveteam.org/wiki/Fujifilm_RAF
2660      */
getRafAttributes(ByteOrderedDataInputStream in)2661     private void getRafAttributes(ByteOrderedDataInputStream in) throws IOException {
2662         // Retrieve offset & length values
2663         in.skipBytes(RAF_OFFSET_TO_JPEG_IMAGE_OFFSET);
2664         byte[] jpegOffsetBytes = new byte[4];
2665         byte[] cfaHeaderOffsetBytes = new byte[4];
2666         in.read(jpegOffsetBytes);
2667         // Skip JPEG length value since it is not needed
2668         in.skipBytes(RAF_JPEG_LENGTH_VALUE_SIZE);
2669         in.read(cfaHeaderOffsetBytes);
2670         int rafJpegOffset = ByteBuffer.wrap(jpegOffsetBytes).getInt();
2671         int rafCfaHeaderOffset = ByteBuffer.wrap(cfaHeaderOffsetBytes).getInt();
2672 
2673         // Retrieve JPEG image metadata
2674         getJpegAttributes(in, rafJpegOffset, IFD_TYPE_PREVIEW);
2675 
2676         // Skip to CFA header offset.
2677         in.seek(rafCfaHeaderOffset);
2678 
2679         // Retrieve primary image length/width values, if TAG_RAF_IMAGE_SIZE exists
2680         in.setByteOrder(ByteOrder.BIG_ENDIAN);
2681         int numberOfDirectoryEntry = in.readInt();
2682         if (DEBUG) {
2683             Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
2684         }
2685         // CFA stores some metadata about the RAW image. Since CFA uses proprietary tags, can only
2686         // find and retrieve image size information tags, while skipping others.
2687         // See piex.cc RafGetDimension()
2688         for (int i = 0; i < numberOfDirectoryEntry; ++i) {
2689             int tagNumber = in.readUnsignedShort();
2690             int numberOfBytes = in.readUnsignedShort();
2691             if (tagNumber == TAG_RAF_IMAGE_SIZE.number) {
2692                 int imageLength = in.readShort();
2693                 int imageWidth = in.readShort();
2694                 ExifAttribute imageLengthAttribute =
2695                         ExifAttribute.createUShort(imageLength, mExifByteOrder);
2696                 ExifAttribute imageWidthAttribute =
2697                         ExifAttribute.createUShort(imageWidth, mExifByteOrder);
2698                 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, imageLengthAttribute);
2699                 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, imageWidthAttribute);
2700                 if (DEBUG) {
2701                     Log.d(TAG, "Updated to length: " + imageLength + ", width: " + imageWidth);
2702                 }
2703                 return;
2704             }
2705             in.skipBytes(numberOfBytes);
2706         }
2707     }
2708 
getHeifAttributes(ByteOrderedDataInputStream in)2709     private void getHeifAttributes(ByteOrderedDataInputStream in) throws IOException {
2710         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
2711         try {
2712             retriever.setDataSource(new MediaDataSource() {
2713                 long mPosition;
2714 
2715                 @Override
2716                 public void close() throws IOException {}
2717 
2718                 @Override
2719                 public int readAt(long position, byte[] buffer, int offset, int size)
2720                         throws IOException {
2721                     if (size == 0) {
2722                         return 0;
2723                     }
2724                     if (position < 0) {
2725                         return -1;
2726                     }
2727                     try {
2728                         if (mPosition != position) {
2729                             // We don't allow seek to positions after the available bytes,
2730                             // the input stream won't be able to seek back then.
2731                             // However, if we hit an exception before (mPosition set to -1),
2732                             // let it try the seek in hope it might recover.
2733                             if (mPosition >= 0 && position >= mPosition + in.available()) {
2734                                 return -1;
2735                             }
2736                             in.seek(position);
2737                             mPosition = position;
2738                         }
2739 
2740                         // If the read will cause us to go over the available bytes,
2741                         // reduce the size so that we stay in the available range.
2742                         // Otherwise the input stream may not be able to seek back.
2743                         if (size > in.available()) {
2744                             size = in.available();
2745                         }
2746 
2747                         int bytesRead = in.read(buffer, offset, size);
2748                         if (bytesRead >= 0) {
2749                             mPosition += bytesRead;
2750                             return bytesRead;
2751                         }
2752                     } catch (IOException e) {}
2753                     mPosition = -1; // need to seek on next read
2754                     return -1;
2755                 }
2756 
2757                 @Override
2758                 public long getSize() throws IOException {
2759                     return -1;
2760                 }
2761             });
2762 
2763             String exifOffsetStr = retriever.extractMetadata(
2764                     MediaMetadataRetriever.METADATA_KEY_EXIF_OFFSET);
2765             String exifLengthStr = retriever.extractMetadata(
2766                     MediaMetadataRetriever.METADATA_KEY_EXIF_LENGTH);
2767             String hasImage = retriever.extractMetadata(
2768                     MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
2769             String hasVideo = retriever.extractMetadata(
2770                     MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
2771 
2772             String width = null;
2773             String height = null;
2774             String rotation = null;
2775             final String METADATA_VALUE_YES = "yes";
2776             // If the file has both image and video, prefer image info over video info.
2777             // App querying ExifInterface is most likely using the bitmap path which
2778             // picks the image first.
2779             if (METADATA_VALUE_YES.equals(hasImage)) {
2780                 width = retriever.extractMetadata(
2781                         MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH);
2782                 height = retriever.extractMetadata(
2783                         MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT);
2784                 rotation = retriever.extractMetadata(
2785                         MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION);
2786             } else if (METADATA_VALUE_YES.equals(hasVideo)) {
2787                 width = retriever.extractMetadata(
2788                         MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
2789                 height = retriever.extractMetadata(
2790                         MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
2791                 rotation = retriever.extractMetadata(
2792                         MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
2793             }
2794 
2795             if (width != null) {
2796                 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH,
2797                         ExifAttribute.createUShort(Integer.parseInt(width), mExifByteOrder));
2798             }
2799 
2800             if (height != null) {
2801                 mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH,
2802                         ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder));
2803             }
2804 
2805             if (rotation != null) {
2806                 int orientation = ExifInterface.ORIENTATION_NORMAL;
2807 
2808                 // all rotation angles in CW
2809                 switch (Integer.parseInt(rotation)) {
2810                     case 90:
2811                         orientation = ExifInterface.ORIENTATION_ROTATE_90;
2812                         break;
2813                     case 180:
2814                         orientation = ExifInterface.ORIENTATION_ROTATE_180;
2815                         break;
2816                     case 270:
2817                         orientation = ExifInterface.ORIENTATION_ROTATE_270;
2818                         break;
2819                 }
2820 
2821                 mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
2822                         ExifAttribute.createUShort(orientation, mExifByteOrder));
2823             }
2824 
2825             if (exifOffsetStr != null && exifLengthStr != null) {
2826                 int offset = Integer.parseInt(exifOffsetStr);
2827                 int length = Integer.parseInt(exifLengthStr);
2828                 if (length <= 6) {
2829                     throw new IOException("Invalid exif length");
2830                 }
2831                 in.seek(offset);
2832                 byte[] identifier = new byte[6];
2833                 if (in.read(identifier) != 6) {
2834                     throw new IOException("Can't read identifier");
2835                 }
2836                 offset += 6;
2837                 length -= 6;
2838                 if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
2839                     throw new IOException("Invalid identifier");
2840                 }
2841 
2842                 byte[] bytes = new byte[length];
2843                 if (in.read(bytes) != length) {
2844                     throw new IOException("Can't read exif");
2845                 }
2846                 readExifSegment(bytes, IFD_TYPE_PRIMARY);
2847             }
2848 
2849             if (DEBUG) {
2850                 Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation);
2851             }
2852         } finally {
2853             retriever.release();
2854         }
2855     }
2856 
2857     /**
2858      * ORF files contains a primary image data and a MakerNote data that contains preview/thumbnail
2859      * images. Both data takes the form of IFDs and can therefore be read with the
2860      * readImageFileDirectory() method.
2861      * This method reads all the necessary data and updates the primary/preview/thumbnail image
2862      * information according to the GetOlympusPreviewImage() method in piex.cc.
2863      * For data format details, see the following:
2864      * http://fileformats.archiveteam.org/wiki/Olympus_ORF
2865      * https://libopenraw.freedesktop.org/wiki/Olympus_ORF
2866      */
getOrfAttributes(ByteOrderedDataInputStream in)2867     private void getOrfAttributes(ByteOrderedDataInputStream in) throws IOException {
2868         // Retrieve primary image data
2869         // Other Exif data will be located in the Makernote.
2870         getRawAttributes(in);
2871 
2872         // Additionally retrieve preview/thumbnail information from MakerNote tag, which contains
2873         // proprietary tags and therefore does not have offical documentation
2874         // See GetOlympusPreviewImage() in piex.cc & http://www.exiv2.org/tags-olympus.html
2875         ExifAttribute makerNoteAttribute =
2876                 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE);
2877         if (makerNoteAttribute != null) {
2878             // Create an ordered DataInputStream for MakerNote
2879             ByteOrderedDataInputStream makerNoteDataInputStream =
2880                     new ByteOrderedDataInputStream(makerNoteAttribute.bytes);
2881             makerNoteDataInputStream.setByteOrder(mExifByteOrder);
2882 
2883             // There are two types of headers for Olympus MakerNotes
2884             // See http://www.exiv2.org/makernote.html#R1
2885             byte[] makerNoteHeader1Bytes = new byte[ORF_MAKER_NOTE_HEADER_1.length];
2886             makerNoteDataInputStream.readFully(makerNoteHeader1Bytes);
2887             makerNoteDataInputStream.seek(0);
2888             byte[] makerNoteHeader2Bytes = new byte[ORF_MAKER_NOTE_HEADER_2.length];
2889             makerNoteDataInputStream.readFully(makerNoteHeader2Bytes);
2890             // Skip the corresponding amount of bytes for each header type
2891             if (Arrays.equals(makerNoteHeader1Bytes, ORF_MAKER_NOTE_HEADER_1)) {
2892                 makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_1_SIZE);
2893             } else if (Arrays.equals(makerNoteHeader2Bytes, ORF_MAKER_NOTE_HEADER_2)) {
2894                 makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_2_SIZE);
2895             }
2896 
2897             // Read IFD data from MakerNote
2898             readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_ORF_MAKER_NOTE);
2899 
2900             // Retrieve & update preview image offset & length values
2901             ExifAttribute imageLengthAttribute = (ExifAttribute)
2902                     mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_START);
2903             ExifAttribute bitsPerSampleAttribute = (ExifAttribute)
2904                     mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_LENGTH);
2905 
2906             if (imageLengthAttribute != null && bitsPerSampleAttribute != null) {
2907                 mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT,
2908                         imageLengthAttribute);
2909                 mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
2910                         bitsPerSampleAttribute);
2911             }
2912 
2913             // TODO: Check this behavior in other ORF files
2914             // Retrieve primary image length & width values
2915             // See piex.cc GetOlympusPreviewImage()
2916             ExifAttribute aspectFrameAttribute = (ExifAttribute)
2917                     mAttributes[IFD_TYPE_ORF_IMAGE_PROCESSING].get(TAG_ORF_ASPECT_FRAME);
2918             if (aspectFrameAttribute != null) {
2919                 int[] aspectFrameValues = new int[4];
2920                 aspectFrameValues = (int[]) aspectFrameAttribute.getValue(mExifByteOrder);
2921                 if (aspectFrameValues[2] > aspectFrameValues[0] &&
2922                         aspectFrameValues[3] > aspectFrameValues[1]) {
2923                     int primaryImageWidth = aspectFrameValues[2] - aspectFrameValues[0] + 1;
2924                     int primaryImageLength = aspectFrameValues[3] - aspectFrameValues[1] + 1;
2925                     // Swap width & length values
2926                     if (primaryImageWidth < primaryImageLength) {
2927                         primaryImageWidth += primaryImageLength;
2928                         primaryImageLength = primaryImageWidth - primaryImageLength;
2929                         primaryImageWidth -= primaryImageLength;
2930                     }
2931                     ExifAttribute primaryImageWidthAttribute =
2932                             ExifAttribute.createUShort(primaryImageWidth, mExifByteOrder);
2933                     ExifAttribute primaryImageLengthAttribute =
2934                             ExifAttribute.createUShort(primaryImageLength, mExifByteOrder);
2935 
2936                     mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, primaryImageWidthAttribute);
2937                     mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, primaryImageLengthAttribute);
2938                 }
2939             }
2940         }
2941     }
2942 
2943     // RW2 contains the primary image data in IFD0 and the preview and/or thumbnail image data in
2944     // the JpgFromRaw tag
2945     // See https://libopenraw.freedesktop.org/wiki/Panasonic_RAW/ and piex.cc Rw2GetPreviewData()
getRw2Attributes(ByteOrderedDataInputStream in)2946     private void getRw2Attributes(ByteOrderedDataInputStream in) throws IOException {
2947         // Retrieve primary image data
2948         getRawAttributes(in);
2949 
2950         // Retrieve preview and/or thumbnail image data
2951         ExifAttribute jpgFromRawAttribute =
2952                 (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_JPG_FROM_RAW);
2953         if (jpgFromRawAttribute != null) {
2954             getJpegAttributes(in, mRw2JpgFromRawOffset, IFD_TYPE_PREVIEW);
2955         }
2956 
2957         // Set ISO tag value if necessary
2958         ExifAttribute rw2IsoAttribute =
2959                 (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_ISO);
2960         ExifAttribute exifIsoAttribute =
2961                 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_ISO_SPEED_RATINGS);
2962         if (rw2IsoAttribute != null && exifIsoAttribute == null) {
2963             // Place this attribute only if it doesn't exist
2964             mAttributes[IFD_TYPE_EXIF].put(TAG_ISO_SPEED_RATINGS, rw2IsoAttribute);
2965         }
2966     }
2967 
2968     // Stores a new JPEG image with EXIF attributes into a given output stream.
saveJpegAttributes(InputStream inputStream, OutputStream outputStream)2969     private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream)
2970             throws IOException {
2971         // See JPEG File Interchange Format Specification, "JFIF Specification"
2972         if (DEBUG) {
2973             Log.d(TAG, "saveJpegAttributes starting with (inputStream: " + inputStream
2974                     + ", outputStream: " + outputStream + ")");
2975         }
2976         DataInputStream dataInputStream = new DataInputStream(inputStream);
2977         ByteOrderedDataOutputStream dataOutputStream =
2978                 new ByteOrderedDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN);
2979         if (dataInputStream.readByte() != MARKER) {
2980             throw new IOException("Invalid marker");
2981         }
2982         dataOutputStream.writeByte(MARKER);
2983         if (dataInputStream.readByte() != MARKER_SOI) {
2984             throw new IOException("Invalid marker");
2985         }
2986         dataOutputStream.writeByte(MARKER_SOI);
2987 
2988         // Write EXIF APP1 segment
2989         dataOutputStream.writeByte(MARKER);
2990         dataOutputStream.writeByte(MARKER_APP1);
2991         writeExifSegment(dataOutputStream, 6);
2992 
2993         byte[] bytes = new byte[4096];
2994 
2995         while (true) {
2996             byte marker = dataInputStream.readByte();
2997             if (marker != MARKER) {
2998                 throw new IOException("Invalid marker");
2999             }
3000             marker = dataInputStream.readByte();
3001             switch (marker) {
3002                 case MARKER_APP1: {
3003                     int length = dataInputStream.readUnsignedShort() - 2;
3004                     if (length < 0) {
3005                         throw new IOException("Invalid length");
3006                     }
3007                     byte[] identifier = new byte[6];
3008                     if (length >= 6) {
3009                         if (dataInputStream.read(identifier) != 6) {
3010                             throw new IOException("Invalid exif");
3011                         }
3012                         if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
3013                             // Skip the original EXIF APP1 segment.
3014                             if (dataInputStream.skipBytes(length - 6) != length - 6) {
3015                                 throw new IOException("Invalid length");
3016                             }
3017                             break;
3018                         }
3019                     }
3020                     // Copy non-EXIF APP1 segment.
3021                     dataOutputStream.writeByte(MARKER);
3022                     dataOutputStream.writeByte(marker);
3023                     dataOutputStream.writeUnsignedShort(length + 2);
3024                     if (length >= 6) {
3025                         length -= 6;
3026                         dataOutputStream.write(identifier);
3027                     }
3028                     int read;
3029                     while (length > 0 && (read = dataInputStream.read(
3030                             bytes, 0, Math.min(length, bytes.length))) >= 0) {
3031                         dataOutputStream.write(bytes, 0, read);
3032                         length -= read;
3033                     }
3034                     break;
3035                 }
3036                 case MARKER_EOI:
3037                 case MARKER_SOS: {
3038                     dataOutputStream.writeByte(MARKER);
3039                     dataOutputStream.writeByte(marker);
3040                     // Copy all the remaining data
3041                     Streams.copy(dataInputStream, dataOutputStream);
3042                     return;
3043                 }
3044                 default: {
3045                     // Copy JPEG segment
3046                     dataOutputStream.writeByte(MARKER);
3047                     dataOutputStream.writeByte(marker);
3048                     int length = dataInputStream.readUnsignedShort();
3049                     dataOutputStream.writeUnsignedShort(length);
3050                     length -= 2;
3051                     if (length < 0) {
3052                         throw new IOException("Invalid length");
3053                     }
3054                     int read;
3055                     while (length > 0 && (read = dataInputStream.read(
3056                             bytes, 0, Math.min(length, bytes.length))) >= 0) {
3057                         dataOutputStream.write(bytes, 0, read);
3058                         length -= read;
3059                     }
3060                     break;
3061                 }
3062             }
3063         }
3064     }
3065 
3066     // Reads the given EXIF byte area and save its tag data into attributes.
readExifSegment(byte[] exifBytes, int imageType)3067     private void readExifSegment(byte[] exifBytes, int imageType) throws IOException {
3068         ByteOrderedDataInputStream dataInputStream =
3069                 new ByteOrderedDataInputStream(exifBytes);
3070 
3071         // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
3072         parseTiffHeaders(dataInputStream, exifBytes.length);
3073 
3074         // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6.
3075         readImageFileDirectory(dataInputStream, imageType);
3076     }
3077 
addDefaultValuesForCompatibility()3078     private void addDefaultValuesForCompatibility() {
3079         // If DATETIME tag has no value, then set the value to DATETIME_ORIGINAL tag's.
3080         String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL);
3081         if (valueOfDateTimeOriginal != null && getAttribute(TAG_DATETIME) == null) {
3082             mAttributes[IFD_TYPE_PRIMARY].put(TAG_DATETIME,
3083                     ExifAttribute.createString(valueOfDateTimeOriginal));
3084         }
3085 
3086         // Add the default value.
3087         if (getAttribute(TAG_IMAGE_WIDTH) == null) {
3088             mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH,
3089                     ExifAttribute.createULong(0, mExifByteOrder));
3090         }
3091         if (getAttribute(TAG_IMAGE_LENGTH) == null) {
3092             mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH,
3093                     ExifAttribute.createULong(0, mExifByteOrder));
3094         }
3095         if (getAttribute(TAG_ORIENTATION) == null) {
3096             mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
3097                     ExifAttribute.createUShort(0, mExifByteOrder));
3098         }
3099         if (getAttribute(TAG_LIGHT_SOURCE) == null) {
3100             mAttributes[IFD_TYPE_EXIF].put(TAG_LIGHT_SOURCE,
3101                     ExifAttribute.createULong(0, mExifByteOrder));
3102         }
3103     }
3104 
readByteOrder(ByteOrderedDataInputStream dataInputStream)3105     private ByteOrder readByteOrder(ByteOrderedDataInputStream dataInputStream)
3106             throws IOException {
3107         // Read byte order.
3108         short byteOrder = dataInputStream.readShort();
3109         switch (byteOrder) {
3110             case BYTE_ALIGN_II:
3111                 if (DEBUG) {
3112                     Log.d(TAG, "readExifSegment: Byte Align II");
3113                 }
3114                 return ByteOrder.LITTLE_ENDIAN;
3115             case BYTE_ALIGN_MM:
3116                 if (DEBUG) {
3117                     Log.d(TAG, "readExifSegment: Byte Align MM");
3118                 }
3119                 return ByteOrder.BIG_ENDIAN;
3120             default:
3121                 throw new IOException("Invalid byte order: " + Integer.toHexString(byteOrder));
3122         }
3123     }
3124 
parseTiffHeaders(ByteOrderedDataInputStream dataInputStream, int exifBytesLength)3125     private void parseTiffHeaders(ByteOrderedDataInputStream dataInputStream,
3126             int exifBytesLength) throws IOException {
3127         // Read byte order
3128         mExifByteOrder = readByteOrder(dataInputStream);
3129         // Set byte order
3130         dataInputStream.setByteOrder(mExifByteOrder);
3131 
3132         // Check start code
3133         int startCode = dataInputStream.readUnsignedShort();
3134         if (mMimeType != IMAGE_TYPE_ORF && mMimeType != IMAGE_TYPE_RW2 && startCode != START_CODE) {
3135             throw new IOException("Invalid start code: " + Integer.toHexString(startCode));
3136         }
3137 
3138         // Read and skip to first ifd offset
3139         int firstIfdOffset = dataInputStream.readInt();
3140         if (firstIfdOffset < 8 || firstIfdOffset >= exifBytesLength) {
3141             throw new IOException("Invalid first Ifd offset: " + firstIfdOffset);
3142         }
3143         firstIfdOffset -= 8;
3144         if (firstIfdOffset > 0) {
3145             if (dataInputStream.skipBytes(firstIfdOffset) != firstIfdOffset) {
3146                 throw new IOException("Couldn't jump to first Ifd: " + firstIfdOffset);
3147             }
3148         }
3149     }
3150 
3151     // Reads image file directory, which is a tag group in EXIF.
readImageFileDirectory(ByteOrderedDataInputStream dataInputStream, @IfdType int ifdType)3152     private void readImageFileDirectory(ByteOrderedDataInputStream dataInputStream,
3153             @IfdType int ifdType) throws IOException {
3154         // Save offset of current IFD to prevent reading an IFD that is already read.
3155         mHandledIfdOffsets.add(dataInputStream.mPosition);
3156 
3157         if (dataInputStream.mPosition + 2 > dataInputStream.mLength) {
3158             // Return if there is no data from the offset.
3159             return;
3160         }
3161         // See TIFF 6.0 Section 2: TIFF Structure, Figure 1.
3162         short numberOfDirectoryEntry = dataInputStream.readShort();
3163         if (dataInputStream.mPosition + 12 * numberOfDirectoryEntry > dataInputStream.mLength
3164                 || numberOfDirectoryEntry <= 0) {
3165             // Return if the size of entries is either too big or negative.
3166             return;
3167         }
3168 
3169         if (DEBUG) {
3170             Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
3171         }
3172 
3173         // See TIFF 6.0 Section 2: TIFF Structure, "Image File Directory".
3174         for (short i = 0; i < numberOfDirectoryEntry; ++i) {
3175             int tagNumber = dataInputStream.readUnsignedShort();
3176             int dataFormat = dataInputStream.readUnsignedShort();
3177             int numberOfComponents = dataInputStream.readInt();
3178             // Next four bytes is for data offset or value.
3179             long nextEntryOffset = dataInputStream.peek() + 4;
3180 
3181             // Look up a corresponding tag from tag number
3182             ExifTag tag = (ExifTag) sExifTagMapsForReading[ifdType].get(tagNumber);
3183 
3184             if (DEBUG) {
3185                 Log.d(TAG, String.format("ifdType: %d, tagNumber: %d, tagName: %s, dataFormat: %d, "
3186                         + "numberOfComponents: %d", ifdType, tagNumber,
3187                         tag != null ? tag.name : null, dataFormat, numberOfComponents));
3188             }
3189 
3190             long byteCount = 0;
3191             boolean valid = false;
3192             if (tag == null) {
3193                 if (DEBUG) {
3194                     Log.d(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber);
3195                 }
3196             } else if (dataFormat <= 0 || dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) {
3197                 if (DEBUG) {
3198                     Log.d(TAG, "Skip the tag entry since data format is invalid: " + dataFormat);
3199                 }
3200             } else {
3201                 byteCount = (long) numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat];
3202                 if (byteCount < 0 || byteCount > Integer.MAX_VALUE) {
3203                     if (DEBUG) {
3204                         Log.d(TAG, "Skip the tag entry since the number of components is invalid: "
3205                                 + numberOfComponents);
3206                     }
3207                 } else {
3208                     valid = true;
3209                 }
3210             }
3211             if (!valid) {
3212                 dataInputStream.seek(nextEntryOffset);
3213                 continue;
3214             }
3215 
3216             // Read a value from data field or seek to the value offset which is stored in data
3217             // field if the size of the entry value is bigger than 4.
3218             if (byteCount > 4) {
3219                 int offset = dataInputStream.readInt();
3220                 if (DEBUG) {
3221                     Log.d(TAG, "seek to data offset: " + offset);
3222                 }
3223                 if (mMimeType == IMAGE_TYPE_ORF) {
3224                     if (tag.name == TAG_MAKER_NOTE) {
3225                         // Save offset value for reading thumbnail
3226                         mOrfMakerNoteOffset = offset;
3227                     } else if (ifdType == IFD_TYPE_ORF_MAKER_NOTE
3228                             && tag.name == TAG_ORF_THUMBNAIL_IMAGE) {
3229                         // Retrieve & update values for thumbnail offset and length values for ORF
3230                         mOrfThumbnailOffset = offset;
3231                         mOrfThumbnailLength = numberOfComponents;
3232 
3233                         ExifAttribute compressionAttribute =
3234                                 ExifAttribute.createUShort(DATA_JPEG, mExifByteOrder);
3235                         ExifAttribute jpegInterchangeFormatAttribute =
3236                                 ExifAttribute.createULong(mOrfThumbnailOffset, mExifByteOrder);
3237                         ExifAttribute jpegInterchangeFormatLengthAttribute =
3238                                 ExifAttribute.createULong(mOrfThumbnailLength, mExifByteOrder);
3239 
3240                         mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_COMPRESSION, compressionAttribute);
3241                         mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT,
3242                                 jpegInterchangeFormatAttribute);
3243                         mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
3244                                 jpegInterchangeFormatLengthAttribute);
3245                     }
3246                 } else if (mMimeType == IMAGE_TYPE_RW2) {
3247                     if (tag.name == TAG_RW2_JPG_FROM_RAW) {
3248                         mRw2JpgFromRawOffset = offset;
3249                     }
3250                 }
3251                 if (offset + byteCount <= dataInputStream.mLength) {
3252                     dataInputStream.seek(offset);
3253                 } else {
3254                     // Skip if invalid data offset.
3255                     if (DEBUG) {
3256                         Log.d(TAG, "Skip the tag entry since data offset is invalid: " + offset);
3257                     }
3258                     dataInputStream.seek(nextEntryOffset);
3259                     continue;
3260                 }
3261             }
3262 
3263             // Recursively parse IFD when a IFD pointer tag appears.
3264             Integer nextIfdType = sExifPointerTagMap.get(tagNumber);
3265             if (DEBUG) {
3266                 Log.d(TAG, "nextIfdType: " + nextIfdType + " byteCount: " + byteCount);
3267             }
3268 
3269             if (nextIfdType != null) {
3270                 long offset = -1L;
3271                 // Get offset from data field
3272                 switch (dataFormat) {
3273                     case IFD_FORMAT_USHORT: {
3274                         offset = dataInputStream.readUnsignedShort();
3275                         break;
3276                     }
3277                     case IFD_FORMAT_SSHORT: {
3278                         offset = dataInputStream.readShort();
3279                         break;
3280                     }
3281                     case IFD_FORMAT_ULONG: {
3282                         offset = dataInputStream.readUnsignedInt();
3283                         break;
3284                     }
3285                     case IFD_FORMAT_SLONG:
3286                     case IFD_FORMAT_IFD: {
3287                         offset = dataInputStream.readInt();
3288                         break;
3289                     }
3290                     default: {
3291                         // Nothing to do
3292                         break;
3293                     }
3294                 }
3295                 if (DEBUG) {
3296                     Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tag.name));
3297                 }
3298 
3299                 // Check if the next IFD offset
3300                 // 1. Exists within the boundaries of the input stream
3301                 // 2. Does not point to a previously read IFD.
3302                 if (offset > 0L && offset < dataInputStream.mLength) {
3303                     if (!mHandledIfdOffsets.contains((int) offset)) {
3304                         dataInputStream.seek(offset);
3305                         readImageFileDirectory(dataInputStream, nextIfdType);
3306                     } else {
3307                         if (DEBUG) {
3308                             Log.d(TAG, "Skip jump into the IFD since it has already been read: "
3309                                     + "IfdType " + nextIfdType + " (at " + offset + ")");
3310                         }
3311                     }
3312                 } else {
3313                     if (DEBUG) {
3314                         Log.d(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
3315                     }
3316                 }
3317 
3318                 dataInputStream.seek(nextEntryOffset);
3319                 continue;
3320             }
3321 
3322             final int bytesOffset = dataInputStream.peek();
3323             final byte[] bytes = new byte[(int) byteCount];
3324             dataInputStream.readFully(bytes);
3325             ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents,
3326                     bytesOffset, bytes);
3327             mAttributes[ifdType].put(tag.name, attribute);
3328 
3329             // DNG files have a DNG Version tag specifying the version of specifications that the
3330             // image file is following.
3331             // See http://fileformats.archiveteam.org/wiki/DNG
3332             if (tag.name == TAG_DNG_VERSION) {
3333                 mMimeType = IMAGE_TYPE_DNG;
3334             }
3335 
3336             // PEF files have a Make or Model tag that begins with "PENTAX" or a compression tag
3337             // that is 65535.
3338             // See http://fileformats.archiveteam.org/wiki/Pentax_PEF
3339             if (((tag.name == TAG_MAKE || tag.name == TAG_MODEL)
3340                     && attribute.getStringValue(mExifByteOrder).contains(PEF_SIGNATURE))
3341                     || (tag.name == TAG_COMPRESSION
3342                     && attribute.getIntValue(mExifByteOrder) == 65535)) {
3343                 mMimeType = IMAGE_TYPE_PEF;
3344             }
3345 
3346             // Seek to next tag offset
3347             if (dataInputStream.peek() != nextEntryOffset) {
3348                 dataInputStream.seek(nextEntryOffset);
3349             }
3350         }
3351 
3352         if (dataInputStream.peek() + 4 <= dataInputStream.mLength) {
3353             int nextIfdOffset = dataInputStream.readInt();
3354             if (DEBUG) {
3355                 Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset));
3356             }
3357             // Check if the next IFD offset
3358             // 1. Exists within the boundaries of the input stream
3359             // 2. Does not point to a previously read IFD.
3360             if (nextIfdOffset > 0L && nextIfdOffset < dataInputStream.mLength) {
3361                 if (!mHandledIfdOffsets.contains(nextIfdOffset)) {
3362                     dataInputStream.seek(nextIfdOffset);
3363                     // Do not overwrite thumbnail IFD data if it alreay exists.
3364                     if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
3365                         readImageFileDirectory(dataInputStream, IFD_TYPE_THUMBNAIL);
3366                     } else if (mAttributes[IFD_TYPE_PREVIEW].isEmpty()) {
3367                         readImageFileDirectory(dataInputStream, IFD_TYPE_PREVIEW);
3368                     }
3369                 } else {
3370                     if (DEBUG) {
3371                         Log.d(TAG, "Stop reading file since re-reading an IFD may cause an "
3372                                 + "infinite loop: " + nextIfdOffset);
3373                     }
3374                 }
3375             } else {
3376                 if (DEBUG) {
3377                     Log.d(TAG, "Stop reading file since a wrong offset may cause an infinite loop: "
3378                             + nextIfdOffset);
3379                 }
3380             }
3381         }
3382     }
3383 
3384     /**
3385      * JPEG compressed images do not contain IMAGE_LENGTH & IMAGE_WIDTH tags.
3386      * This value uses JpegInterchangeFormat(JPEG data offset) value, and calls getJpegAttributes()
3387      * to locate SOF(Start of Frame) marker and update the image length & width values.
3388      * See JEITA CP-3451C Table 5 and Section 4.8.1. B.
3389      */
retrieveJpegImageSize(ByteOrderedDataInputStream in, int imageType)3390     private void retrieveJpegImageSize(ByteOrderedDataInputStream in, int imageType)
3391             throws IOException {
3392         // Check if image already has IMAGE_LENGTH & IMAGE_WIDTH values
3393         ExifAttribute imageLengthAttribute =
3394                 (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_LENGTH);
3395         ExifAttribute imageWidthAttribute =
3396                 (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_WIDTH);
3397 
3398         if (imageLengthAttribute == null || imageWidthAttribute == null) {
3399             // Find if offset for JPEG data exists
3400             ExifAttribute jpegInterchangeFormatAttribute =
3401                     (ExifAttribute) mAttributes[imageType].get(TAG_JPEG_INTERCHANGE_FORMAT);
3402             if (jpegInterchangeFormatAttribute != null) {
3403                 int jpegInterchangeFormat =
3404                         jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder);
3405 
3406                 // Searches for SOF marker in JPEG data and updates IMAGE_LENGTH & IMAGE_WIDTH tags
3407                 getJpegAttributes(in, jpegInterchangeFormat, imageType);
3408             }
3409         }
3410     }
3411 
3412     // Sets thumbnail offset & length attributes based on JpegInterchangeFormat or StripOffsets tags
setThumbnailData(ByteOrderedDataInputStream in)3413     private void setThumbnailData(ByteOrderedDataInputStream in) throws IOException {
3414         HashMap thumbnailData = mAttributes[IFD_TYPE_THUMBNAIL];
3415 
3416         ExifAttribute compressionAttribute =
3417                 (ExifAttribute) thumbnailData.get(TAG_COMPRESSION);
3418         if (compressionAttribute != null) {
3419             mThumbnailCompression = compressionAttribute.getIntValue(mExifByteOrder);
3420             switch (mThumbnailCompression) {
3421                 case DATA_JPEG: {
3422                     handleThumbnailFromJfif(in, thumbnailData);
3423                     break;
3424                 }
3425                 case DATA_UNCOMPRESSED:
3426                 case DATA_JPEG_COMPRESSED: {
3427                     if (isSupportedDataType(thumbnailData)) {
3428                         handleThumbnailFromStrips(in, thumbnailData);
3429                     }
3430                     break;
3431                 }
3432             }
3433         } else {
3434             // Thumbnail data may not contain Compression tag value
3435             handleThumbnailFromJfif(in, thumbnailData);
3436         }
3437     }
3438 
3439     // Check JpegInterchangeFormat(JFIF) tags to retrieve thumbnail offset & length values
3440     // and reads the corresponding bytes if stream does not support seek function
handleThumbnailFromJfif(ByteOrderedDataInputStream in, HashMap thumbnailData)3441     private void handleThumbnailFromJfif(ByteOrderedDataInputStream in, HashMap thumbnailData)
3442             throws IOException {
3443         ExifAttribute jpegInterchangeFormatAttribute =
3444                 (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT);
3445         ExifAttribute jpegInterchangeFormatLengthAttribute =
3446                 (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
3447         if (jpegInterchangeFormatAttribute != null
3448                 && jpegInterchangeFormatLengthAttribute != null) {
3449             int thumbnailOffset = jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder);
3450             int thumbnailLength = jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder);
3451 
3452             // The following code limits the size of thumbnail size not to overflow EXIF data area.
3453             thumbnailLength = Math.min(thumbnailLength, in.getLength() - thumbnailOffset);
3454             if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF
3455                     || mMimeType == IMAGE_TYPE_RW2) {
3456                 thumbnailOffset += mExifOffset;
3457             } else if (mMimeType == IMAGE_TYPE_ORF) {
3458                 // Update offset value since RAF files have IFD data preceding MakerNote data.
3459                 thumbnailOffset += mOrfMakerNoteOffset;
3460             }
3461             if (DEBUG) {
3462                 Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset
3463                         + ", length: " + thumbnailLength);
3464             }
3465             if (thumbnailOffset > 0 && thumbnailLength > 0) {
3466                 mHasThumbnail = true;
3467                 mThumbnailOffset = thumbnailOffset;
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[thumbnailLength];
3475                     in.seek(thumbnailOffset);
3476                     in.readFully(thumbnailBytes);
3477                     mThumbnailBytes = thumbnailBytes;
3478                 }
3479             }
3480         }
3481     }
3482 
3483     // Check StripOffsets & StripByteCounts tags to retrieve thumbnail offset & length values
handleThumbnailFromStrips(ByteOrderedDataInputStream in, HashMap thumbnailData)3484     private void handleThumbnailFromStrips(ByteOrderedDataInputStream in, HashMap thumbnailData)
3485             throws IOException {
3486         ExifAttribute stripOffsetsAttribute =
3487                 (ExifAttribute) thumbnailData.get(TAG_STRIP_OFFSETS);
3488         ExifAttribute stripByteCountsAttribute =
3489                 (ExifAttribute) thumbnailData.get(TAG_STRIP_BYTE_COUNTS);
3490 
3491         if (stripOffsetsAttribute != null && stripByteCountsAttribute != null) {
3492             long[] stripOffsets =
3493                     convertToLongArray(stripOffsetsAttribute.getValue(mExifByteOrder));
3494             long[] stripByteCounts =
3495                     convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder));
3496 
3497             if (stripOffsets == null) {
3498                 Log.w(TAG, "stripOffsets should not be null.");
3499                 return;
3500             }
3501             if (stripByteCounts == null) {
3502                 Log.w(TAG, "stripByteCounts should not be null.");
3503                 return;
3504             }
3505 
3506             // Set thumbnail byte array data for non-consecutive strip bytes
3507             byte[] totalStripBytes =
3508                     new byte[(int) Arrays.stream(stripByteCounts).sum()];
3509 
3510             int bytesRead = 0;
3511             int bytesAdded = 0;
3512             for (int i = 0; i < stripOffsets.length; i++) {
3513                 int stripOffset = (int) stripOffsets[i];
3514                 int stripByteCount = (int) stripByteCounts[i];
3515 
3516                 // Skip to offset
3517                 int skipBytes = stripOffset - bytesRead;
3518                 if (skipBytes < 0) {
3519                     Log.d(TAG, "Invalid strip offset value");
3520                 }
3521                 in.seek(skipBytes);
3522                 bytesRead += skipBytes;
3523 
3524                 // Read strip bytes
3525                 byte[] stripBytes = new byte[stripByteCount];
3526                 in.read(stripBytes);
3527                 bytesRead += stripByteCount;
3528 
3529                 // Add bytes to array
3530                 System.arraycopy(stripBytes, 0, totalStripBytes, bytesAdded,
3531                         stripBytes.length);
3532                 bytesAdded += stripBytes.length;
3533             }
3534 
3535             mHasThumbnail = true;
3536             mThumbnailBytes = totalStripBytes;
3537             mThumbnailLength = totalStripBytes.length;
3538         }
3539     }
3540 
3541     // Check if thumbnail data type is currently supported or not
isSupportedDataType(HashMap thumbnailData)3542     private boolean isSupportedDataType(HashMap thumbnailData) throws IOException {
3543         ExifAttribute bitsPerSampleAttribute =
3544                 (ExifAttribute) thumbnailData.get(TAG_BITS_PER_SAMPLE);
3545         if (bitsPerSampleAttribute != null) {
3546             int[] bitsPerSampleValue = (int[]) bitsPerSampleAttribute.getValue(mExifByteOrder);
3547 
3548             if (Arrays.equals(BITS_PER_SAMPLE_RGB, bitsPerSampleValue)) {
3549                 return true;
3550             }
3551 
3552             // See DNG Specification 1.4.0.0. Section 3, Compression.
3553             if (mMimeType == IMAGE_TYPE_DNG) {
3554                 ExifAttribute photometricInterpretationAttribute =
3555                         (ExifAttribute) thumbnailData.get(TAG_PHOTOMETRIC_INTERPRETATION);
3556                 if (photometricInterpretationAttribute != null) {
3557                     int photometricInterpretationValue
3558                             = photometricInterpretationAttribute.getIntValue(mExifByteOrder);
3559                     if ((photometricInterpretationValue == PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO
3560                             && Arrays.equals(bitsPerSampleValue, BITS_PER_SAMPLE_GREYSCALE_2))
3561                             || ((photometricInterpretationValue == PHOTOMETRIC_INTERPRETATION_YCBCR)
3562                             && (Arrays.equals(bitsPerSampleValue, BITS_PER_SAMPLE_RGB)))) {
3563                         return true;
3564                     } else {
3565                         // TODO: Add support for lossless Huffman JPEG data
3566                     }
3567                 }
3568             }
3569         }
3570         if (DEBUG) {
3571             Log.d(TAG, "Unsupported data type value");
3572         }
3573         return false;
3574     }
3575 
3576     // Returns true if the image length and width values are <= 512.
3577     // See Section 4.8 of http://standardsproposals.bsigroup.com/Home/getPDF/567
isThumbnail(HashMap map)3578     private boolean isThumbnail(HashMap map) throws IOException {
3579         ExifAttribute imageLengthAttribute = (ExifAttribute) map.get(TAG_IMAGE_LENGTH);
3580         ExifAttribute imageWidthAttribute = (ExifAttribute) map.get(TAG_IMAGE_WIDTH);
3581 
3582         if (imageLengthAttribute != null && imageWidthAttribute != null) {
3583             int imageLengthValue = imageLengthAttribute.getIntValue(mExifByteOrder);
3584             int imageWidthValue = imageWidthAttribute.getIntValue(mExifByteOrder);
3585             if (imageLengthValue <= MAX_THUMBNAIL_SIZE && imageWidthValue <= MAX_THUMBNAIL_SIZE) {
3586                 return true;
3587             }
3588         }
3589         return false;
3590     }
3591 
3592     // Validate primary, preview, thumbnail image data by comparing image size
validateImages(InputStream in)3593     private void validateImages(InputStream in) throws IOException {
3594         // Swap images based on size (primary > preview > thumbnail)
3595         swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_PREVIEW);
3596         swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_THUMBNAIL);
3597         swapBasedOnImageSize(IFD_TYPE_PREVIEW, IFD_TYPE_THUMBNAIL);
3598 
3599         // Check if image has PixelXDimension/PixelYDimension tags, which contain valid image
3600         // sizes, excluding padding at the right end or bottom end of the image to make sure that
3601         // the values are multiples of 64. See JEITA CP-3451C Table 5 and Section 4.8.1. B.
3602         ExifAttribute pixelXDimAttribute =
3603                 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_X_DIMENSION);
3604         ExifAttribute pixelYDimAttribute =
3605                 (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_Y_DIMENSION);
3606         if (pixelXDimAttribute != null && pixelYDimAttribute != null) {
3607             mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, pixelXDimAttribute);
3608             mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, pixelYDimAttribute);
3609         }
3610 
3611         // Check whether thumbnail image exists and whether preview image satisfies the thumbnail
3612         // image requirements
3613         if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
3614             if (isThumbnail(mAttributes[IFD_TYPE_PREVIEW])) {
3615                 mAttributes[IFD_TYPE_THUMBNAIL] = mAttributes[IFD_TYPE_PREVIEW];
3616                 mAttributes[IFD_TYPE_PREVIEW] = new HashMap();
3617             }
3618         }
3619 
3620         // Check if the thumbnail image satisfies the thumbnail size requirements
3621         if (!isThumbnail(mAttributes[IFD_TYPE_THUMBNAIL])) {
3622             Log.d(TAG, "No image meets the size requirements of a thumbnail image.");
3623         }
3624     }
3625 
3626     /**
3627      * If image is uncompressed, ImageWidth/Length tags are used to store size info.
3628      * However, uncompressed images often store extra pixels around the edges of the final image,
3629      * which results in larger values for TAG_IMAGE_WIDTH and TAG_IMAGE_LENGTH tags.
3630      * This method corrects those tag values by checking first the values of TAG_DEFAULT_CROP_SIZE
3631      * See DNG Specification 1.4.0.0. Section 4. (DefaultCropSize)
3632      *
3633      * If image is a RW2 file, valid image sizes are stored in SensorBorder tags.
3634      * See tiff_parser.cc GetFullDimension32()
3635      * */
updateImageSizeValues(ByteOrderedDataInputStream in, int imageType)3636     private void updateImageSizeValues(ByteOrderedDataInputStream in, int imageType)
3637             throws IOException {
3638         // Uncompressed image valid image size values
3639         ExifAttribute defaultCropSizeAttribute =
3640                 (ExifAttribute) mAttributes[imageType].get(TAG_DEFAULT_CROP_SIZE);
3641         // RW2 image valid image size values
3642         ExifAttribute topBorderAttribute =
3643                 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_TOP_BORDER);
3644         ExifAttribute leftBorderAttribute =
3645                 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_LEFT_BORDER);
3646         ExifAttribute bottomBorderAttribute =
3647                 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_BOTTOM_BORDER);
3648         ExifAttribute rightBorderAttribute =
3649                 (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_RIGHT_BORDER);
3650 
3651         if (defaultCropSizeAttribute != null) {
3652             // Update for uncompressed image
3653             ExifAttribute defaultCropSizeXAttribute, defaultCropSizeYAttribute;
3654             if (defaultCropSizeAttribute.format == IFD_FORMAT_URATIONAL) {
3655                 Rational[] defaultCropSizeValue =
3656                         (Rational[]) defaultCropSizeAttribute.getValue(mExifByteOrder);
3657                 defaultCropSizeXAttribute =
3658                         ExifAttribute.createURational(defaultCropSizeValue[0], mExifByteOrder);
3659                 defaultCropSizeYAttribute =
3660                         ExifAttribute.createURational(defaultCropSizeValue[1], mExifByteOrder);
3661             } else {
3662                 int[] defaultCropSizeValue =
3663                         (int[]) defaultCropSizeAttribute.getValue(mExifByteOrder);
3664                 defaultCropSizeXAttribute =
3665                         ExifAttribute.createUShort(defaultCropSizeValue[0], mExifByteOrder);
3666                 defaultCropSizeYAttribute =
3667                         ExifAttribute.createUShort(defaultCropSizeValue[1], mExifByteOrder);
3668             }
3669             mAttributes[imageType].put(TAG_IMAGE_WIDTH, defaultCropSizeXAttribute);
3670             mAttributes[imageType].put(TAG_IMAGE_LENGTH, defaultCropSizeYAttribute);
3671         } else if (topBorderAttribute != null && leftBorderAttribute != null &&
3672                 bottomBorderAttribute != null && rightBorderAttribute != null) {
3673             // Update for RW2 image
3674             int topBorderValue = topBorderAttribute.getIntValue(mExifByteOrder);
3675             int bottomBorderValue = bottomBorderAttribute.getIntValue(mExifByteOrder);
3676             int rightBorderValue = rightBorderAttribute.getIntValue(mExifByteOrder);
3677             int leftBorderValue = leftBorderAttribute.getIntValue(mExifByteOrder);
3678             if (bottomBorderValue > topBorderValue && rightBorderValue > leftBorderValue) {
3679                 int length = bottomBorderValue - topBorderValue;
3680                 int width = rightBorderValue - leftBorderValue;
3681                 ExifAttribute imageLengthAttribute =
3682                         ExifAttribute.createUShort(length, mExifByteOrder);
3683                 ExifAttribute imageWidthAttribute =
3684                         ExifAttribute.createUShort(width, mExifByteOrder);
3685                 mAttributes[imageType].put(TAG_IMAGE_LENGTH, imageLengthAttribute);
3686                 mAttributes[imageType].put(TAG_IMAGE_WIDTH, imageWidthAttribute);
3687             }
3688         } else {
3689             retrieveJpegImageSize(in, imageType);
3690         }
3691     }
3692 
3693     // Writes an Exif segment into the given output stream.
writeExifSegment(ByteOrderedDataOutputStream dataOutputStream, int exifOffsetFromBeginning)3694     private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream,
3695             int exifOffsetFromBeginning) throws IOException {
3696         // The following variables are for calculating each IFD tag group size in bytes.
3697         int[] ifdOffsets = new int[EXIF_TAGS.length];
3698         int[] ifdDataSizes = new int[EXIF_TAGS.length];
3699 
3700         // Remove IFD pointer tags (we'll re-add it later.)
3701         for (ExifTag tag : EXIF_POINTER_TAGS) {
3702             removeAttribute(tag.name);
3703         }
3704         // Remove old thumbnail data
3705         removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
3706         removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
3707 
3708         // Remove null value tags.
3709         for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
3710             for (Object obj : mAttributes[ifdType].entrySet().toArray()) {
3711                 final Map.Entry entry = (Map.Entry) obj;
3712                 if (entry.getValue() == null) {
3713                     mAttributes[ifdType].remove(entry.getKey());
3714                 }
3715             }
3716         }
3717 
3718         // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD
3719         // offset when there is one or more tags in the thumbnail IFD.
3720         if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) {
3721             mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name,
3722                     ExifAttribute.createULong(0, mExifByteOrder));
3723         }
3724         if (!mAttributes[IFD_TYPE_GPS].isEmpty()) {
3725             mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name,
3726                     ExifAttribute.createULong(0, mExifByteOrder));
3727         }
3728         if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) {
3729             mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name,
3730                     ExifAttribute.createULong(0, mExifByteOrder));
3731         }
3732         if (mHasThumbnail) {
3733             mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
3734                     ExifAttribute.createULong(0, mExifByteOrder));
3735             mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
3736                     ExifAttribute.createULong(mThumbnailLength, mExifByteOrder));
3737         }
3738 
3739         // Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
3740         // value which has a bigger size than 4 bytes.
3741         for (int i = 0; i < EXIF_TAGS.length; ++i) {
3742             int sum = 0;
3743             for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
3744                 final ExifAttribute exifAttribute = (ExifAttribute) entry.getValue();
3745                 final int size = exifAttribute.size();
3746                 if (size > 4) {
3747                     sum += size;
3748                 }
3749             }
3750             ifdDataSizes[i] += sum;
3751         }
3752 
3753         // Calculate IFD offsets.
3754         int position = 8;
3755         for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
3756             if (!mAttributes[ifdType].isEmpty()) {
3757                 ifdOffsets[ifdType] = position;
3758                 position += 2 + mAttributes[ifdType].size() * 12 + 4 + ifdDataSizes[ifdType];
3759             }
3760         }
3761         if (mHasThumbnail) {
3762             int thumbnailOffset = position;
3763             mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
3764                     ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
3765             mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset;
3766             position += mThumbnailLength;
3767         }
3768 
3769         // Calculate the total size
3770         int totalSize = position + 8;  // eight bytes is for header part.
3771         if (DEBUG) {
3772             Log.d(TAG, "totalSize length: " + totalSize);
3773             for (int i = 0; i < EXIF_TAGS.length; ++i) {
3774                 Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d",
3775                         i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i]));
3776             }
3777         }
3778 
3779         // Update IFD pointer tags with the calculated offsets.
3780         if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) {
3781             mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name,
3782                     ExifAttribute.createULong(ifdOffsets[IFD_TYPE_EXIF], mExifByteOrder));
3783         }
3784         if (!mAttributes[IFD_TYPE_GPS].isEmpty()) {
3785             mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name,
3786                     ExifAttribute.createULong(ifdOffsets[IFD_TYPE_GPS], mExifByteOrder));
3787         }
3788         if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) {
3789             mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name, ExifAttribute.createULong(
3790                     ifdOffsets[IFD_TYPE_INTEROPERABILITY], mExifByteOrder));
3791         }
3792 
3793         // Write TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
3794         dataOutputStream.writeUnsignedShort(totalSize);
3795         dataOutputStream.write(IDENTIFIER_EXIF_APP1);
3796         dataOutputStream.writeShort(mExifByteOrder == ByteOrder.BIG_ENDIAN
3797                 ? BYTE_ALIGN_MM : BYTE_ALIGN_II);
3798         dataOutputStream.setByteOrder(mExifByteOrder);
3799         dataOutputStream.writeUnsignedShort(START_CODE);
3800         dataOutputStream.writeUnsignedInt(IFD_OFFSET);
3801 
3802         // Write IFD groups. See JEITA CP-3451C Section 4.5.8. Figure 9.
3803         for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
3804             if (!mAttributes[ifdType].isEmpty()) {
3805                 // See JEITA CP-3451C Section 4.6.2: IFD structure.
3806                 // Write entry count
3807                 dataOutputStream.writeUnsignedShort(mAttributes[ifdType].size());
3808 
3809                 // Write entry info
3810                 int dataOffset = ifdOffsets[ifdType] + 2 + mAttributes[ifdType].size() * 12 + 4;
3811                 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[ifdType].entrySet()) {
3812                     // Convert tag name to tag number.
3813                     final ExifTag tag =
3814                             (ExifTag) sExifTagMapsForWriting[ifdType].get(entry.getKey());
3815                     final int tagNumber = tag.number;
3816                     final ExifAttribute attribute = (ExifAttribute) entry.getValue();
3817                     final int size = attribute.size();
3818 
3819                     dataOutputStream.writeUnsignedShort(tagNumber);
3820                     dataOutputStream.writeUnsignedShort(attribute.format);
3821                     dataOutputStream.writeInt(attribute.numberOfComponents);
3822                     if (size > 4) {
3823                         dataOutputStream.writeUnsignedInt(dataOffset);
3824                         dataOffset += size;
3825                     } else {
3826                         dataOutputStream.write(attribute.bytes);
3827                         // Fill zero up to 4 bytes
3828                         if (size < 4) {
3829                             for (int i = size; i < 4; ++i) {
3830                                 dataOutputStream.writeByte(0);
3831                             }
3832                         }
3833                     }
3834                 }
3835 
3836                 // Write the next offset. It writes the offset of thumbnail IFD if there is one or
3837                 // more tags in the thumbnail IFD when the current IFD is the primary image TIFF
3838                 // IFD; Otherwise 0.
3839                 if (ifdType == 0 && !mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
3840                     dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_TYPE_THUMBNAIL]);
3841                 } else {
3842                     dataOutputStream.writeUnsignedInt(0);
3843                 }
3844 
3845                 // Write values of data field exceeding 4 bytes after the next offset.
3846                 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[ifdType].entrySet()) {
3847                     ExifAttribute attribute = (ExifAttribute) entry.getValue();
3848 
3849                     if (attribute.bytes.length > 4) {
3850                         dataOutputStream.write(attribute.bytes, 0, attribute.bytes.length);
3851                     }
3852                 }
3853             }
3854         }
3855 
3856         // Write thumbnail
3857         if (mHasThumbnail) {
3858             dataOutputStream.write(getThumbnailBytes());
3859         }
3860 
3861         // Reset the byte order to big endian in order to write remaining parts of the JPEG file.
3862         dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
3863 
3864         return totalSize;
3865     }
3866 
3867     /**
3868      * Determines the data format of EXIF entry value.
3869      *
3870      * @param entryValue The value to be determined.
3871      * @return Returns two data formats gussed as a pair in integer. If there is no two candidate
3872                data formats for the given entry value, returns {@code -1} in the second of the pair.
3873      */
guessDataFormat(String entryValue)3874     private static Pair<Integer, Integer> guessDataFormat(String entryValue) {
3875         // See TIFF 6.0 Section 2, "Image File Directory".
3876         // Take the first component if there are more than one component.
3877         if (entryValue.contains(",")) {
3878             String[] entryValues = entryValue.split(",");
3879             Pair<Integer, Integer> dataFormat = guessDataFormat(entryValues[0]);
3880             if (dataFormat.first == IFD_FORMAT_STRING) {
3881                 return dataFormat;
3882             }
3883             for (int i = 1; i < entryValues.length; ++i) {
3884                 final Pair<Integer, Integer> guessDataFormat = guessDataFormat(entryValues[i]);
3885                 int first = -1, second = -1;
3886                 if (guessDataFormat.first == dataFormat.first
3887                         || guessDataFormat.second == dataFormat.first) {
3888                     first = dataFormat.first;
3889                 }
3890                 if (dataFormat.second != -1 && (guessDataFormat.first == dataFormat.second
3891                         || guessDataFormat.second == dataFormat.second)) {
3892                     second = dataFormat.second;
3893                 }
3894                 if (first == -1 && second == -1) {
3895                     return new Pair<>(IFD_FORMAT_STRING, -1);
3896                 }
3897                 if (first == -1) {
3898                     dataFormat = new Pair<>(second, -1);
3899                     continue;
3900                 }
3901                 if (second == -1) {
3902                     dataFormat = new Pair<>(first, -1);
3903                     continue;
3904                 }
3905             }
3906             return dataFormat;
3907         }
3908 
3909         if (entryValue.contains("/")) {
3910             String[] rationalNumber = entryValue.split("/");
3911             if (rationalNumber.length == 2) {
3912                 try {
3913                     long numerator = (long) Double.parseDouble(rationalNumber[0]);
3914                     long denominator = (long) Double.parseDouble(rationalNumber[1]);
3915                     if (numerator < 0L || denominator < 0L) {
3916                         return new Pair<>(IFD_FORMAT_SRATIONAL, -1);
3917                     }
3918                     if (numerator > Integer.MAX_VALUE || denominator > Integer.MAX_VALUE) {
3919                         return new Pair<>(IFD_FORMAT_URATIONAL, -1);
3920                     }
3921                     return new Pair<>(IFD_FORMAT_SRATIONAL, IFD_FORMAT_URATIONAL);
3922                 } catch (NumberFormatException e)  {
3923                     // Ignored
3924                 }
3925             }
3926             return new Pair<>(IFD_FORMAT_STRING, -1);
3927         }
3928         try {
3929             Long longValue = Long.parseLong(entryValue);
3930             if (longValue >= 0 && longValue <= 65535) {
3931                 return new Pair<>(IFD_FORMAT_USHORT, IFD_FORMAT_ULONG);
3932             }
3933             if (longValue < 0) {
3934                 return new Pair<>(IFD_FORMAT_SLONG, -1);
3935             }
3936             return new Pair<>(IFD_FORMAT_ULONG, -1);
3937         } catch (NumberFormatException e) {
3938             // Ignored
3939         }
3940         try {
3941             Double.parseDouble(entryValue);
3942             return new Pair<>(IFD_FORMAT_DOUBLE, -1);
3943         } catch (NumberFormatException e) {
3944             // Ignored
3945         }
3946         return new Pair<>(IFD_FORMAT_STRING, -1);
3947     }
3948 
3949     // An input stream to parse EXIF data area, which can be written in either little or big endian
3950     // order.
3951     private static class ByteOrderedDataInputStream extends InputStream implements DataInput {
3952         private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN;
3953         private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN;
3954 
3955         private DataInputStream mDataInputStream;
3956         private InputStream mInputStream;
3957         private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN;
3958         private final int mLength;
3959         private int mPosition;
3960 
ByteOrderedDataInputStream(InputStream in)3961         public ByteOrderedDataInputStream(InputStream in) throws IOException {
3962             mInputStream = in;
3963             mDataInputStream = new DataInputStream(in);
3964             mLength = mDataInputStream.available();
3965             mPosition = 0;
3966             mDataInputStream.mark(mLength);
3967         }
3968 
ByteOrderedDataInputStream(byte[] bytes)3969         public ByteOrderedDataInputStream(byte[] bytes) throws IOException {
3970             this(new ByteArrayInputStream(bytes));
3971         }
3972 
setByteOrder(ByteOrder byteOrder)3973         public void setByteOrder(ByteOrder byteOrder) {
3974             mByteOrder = byteOrder;
3975         }
3976 
seek(long byteCount)3977         public void seek(long byteCount) throws IOException {
3978             if (mPosition > byteCount) {
3979                 mPosition = 0;
3980                 mDataInputStream.reset();
3981                 mDataInputStream.mark(mLength);
3982             } else {
3983                 byteCount -= mPosition;
3984             }
3985 
3986             if (skipBytes((int) byteCount) != (int) byteCount) {
3987                 throw new IOException("Couldn't seek up to the byteCount");
3988             }
3989         }
3990 
peek()3991         public int peek() {
3992             return mPosition;
3993         }
3994 
3995         @Override
available()3996         public int available() throws IOException {
3997             return mDataInputStream.available();
3998         }
3999 
4000         @Override
read()4001         public int read() throws IOException {
4002             ++mPosition;
4003             return mDataInputStream.read();
4004         }
4005 
4006         @Override
readUnsignedByte()4007         public int readUnsignedByte() throws IOException {
4008             ++mPosition;
4009             return mDataInputStream.readUnsignedByte();
4010         }
4011 
4012         @Override
readLine()4013         public String readLine() throws IOException {
4014             Log.d(TAG, "Currently unsupported");
4015             return null;
4016         }
4017 
4018         @Override
readBoolean()4019         public boolean readBoolean() throws IOException {
4020             ++mPosition;
4021             return mDataInputStream.readBoolean();
4022         }
4023 
4024         @Override
readChar()4025         public char readChar() throws IOException {
4026             mPosition += 2;
4027             return mDataInputStream.readChar();
4028         }
4029 
4030         @Override
readUTF()4031         public String readUTF() throws IOException {
4032             mPosition += 2;
4033             return mDataInputStream.readUTF();
4034         }
4035 
4036         @Override
readFully(byte[] buffer, int offset, int length)4037         public void readFully(byte[] buffer, int offset, int length) throws IOException {
4038             mPosition += length;
4039             if (mPosition > mLength) {
4040                 throw new EOFException();
4041             }
4042             if (mDataInputStream.read(buffer, offset, length) != length) {
4043                 throw new IOException("Couldn't read up to the length of buffer");
4044             }
4045         }
4046 
4047         @Override
readFully(byte[] buffer)4048         public void readFully(byte[] buffer) throws IOException {
4049             mPosition += buffer.length;
4050             if (mPosition > mLength) {
4051                 throw new EOFException();
4052             }
4053             if (mDataInputStream.read(buffer, 0, buffer.length) != buffer.length) {
4054                 throw new IOException("Couldn't read up to the length of buffer");
4055             }
4056         }
4057 
4058         @Override
readByte()4059         public byte readByte() throws IOException {
4060             ++mPosition;
4061             if (mPosition > mLength) {
4062                 throw new EOFException();
4063             }
4064             int ch = mDataInputStream.read();
4065             if (ch < 0) {
4066                 throw new EOFException();
4067             }
4068             return (byte) ch;
4069         }
4070 
4071         @Override
readShort()4072         public short readShort() throws IOException {
4073             mPosition += 2;
4074             if (mPosition > mLength) {
4075                 throw new EOFException();
4076             }
4077             int ch1 = mDataInputStream.read();
4078             int ch2 = mDataInputStream.read();
4079             if ((ch1 | ch2) < 0) {
4080                 throw new EOFException();
4081             }
4082             if (mByteOrder == LITTLE_ENDIAN) {
4083                 return (short) ((ch2 << 8) + (ch1));
4084             } else if (mByteOrder == BIG_ENDIAN) {
4085                 return (short) ((ch1 << 8) + (ch2));
4086             }
4087             throw new IOException("Invalid byte order: " + mByteOrder);
4088         }
4089 
4090         @Override
readInt()4091         public int readInt() throws IOException {
4092             mPosition += 4;
4093             if (mPosition > mLength) {
4094                 throw new EOFException();
4095             }
4096             int ch1 = mDataInputStream.read();
4097             int ch2 = mDataInputStream.read();
4098             int ch3 = mDataInputStream.read();
4099             int ch4 = mDataInputStream.read();
4100             if ((ch1 | ch2 | ch3 | ch4) < 0) {
4101                 throw new EOFException();
4102             }
4103             if (mByteOrder == LITTLE_ENDIAN) {
4104                 return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1);
4105             } else if (mByteOrder == BIG_ENDIAN) {
4106                 return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4);
4107             }
4108             throw new IOException("Invalid byte order: " + mByteOrder);
4109         }
4110 
4111         @Override
skipBytes(int byteCount)4112         public int skipBytes(int byteCount) throws IOException {
4113             int totalSkip = Math.min(byteCount, mLength - mPosition);
4114             int skipped = 0;
4115             while (skipped < totalSkip) {
4116                 skipped += mDataInputStream.skipBytes(totalSkip - skipped);
4117             }
4118             mPosition += skipped;
4119             return skipped;
4120         }
4121 
readUnsignedShort()4122         public int readUnsignedShort() throws IOException {
4123             mPosition += 2;
4124             if (mPosition > mLength) {
4125                 throw new EOFException();
4126             }
4127             int ch1 = mDataInputStream.read();
4128             int ch2 = mDataInputStream.read();
4129             if ((ch1 | ch2) < 0) {
4130                 throw new EOFException();
4131             }
4132             if (mByteOrder == LITTLE_ENDIAN) {
4133                 return ((ch2 << 8) + (ch1));
4134             } else if (mByteOrder == BIG_ENDIAN) {
4135                 return ((ch1 << 8) + (ch2));
4136             }
4137             throw new IOException("Invalid byte order: " + mByteOrder);
4138         }
4139 
readUnsignedInt()4140         public long readUnsignedInt() throws IOException {
4141             return readInt() & 0xffffffffL;
4142         }
4143 
4144         @Override
readLong()4145         public long readLong() throws IOException {
4146             mPosition += 8;
4147             if (mPosition > mLength) {
4148                 throw new EOFException();
4149             }
4150             int ch1 = mDataInputStream.read();
4151             int ch2 = mDataInputStream.read();
4152             int ch3 = mDataInputStream.read();
4153             int ch4 = mDataInputStream.read();
4154             int ch5 = mDataInputStream.read();
4155             int ch6 = mDataInputStream.read();
4156             int ch7 = mDataInputStream.read();
4157             int ch8 = mDataInputStream.read();
4158             if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) {
4159                 throw new EOFException();
4160             }
4161             if (mByteOrder == LITTLE_ENDIAN) {
4162                 return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40)
4163                         + ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16)
4164                         + ((long) ch2 << 8) + (long) ch1);
4165             } else if (mByteOrder == BIG_ENDIAN) {
4166                 return (((long) ch1 << 56) + ((long) ch2 << 48) + ((long) ch3 << 40)
4167                         + ((long) ch4 << 32) + ((long) ch5 << 24) + ((long) ch6 << 16)
4168                         + ((long) ch7 << 8) + (long) ch8);
4169             }
4170             throw new IOException("Invalid byte order: " + mByteOrder);
4171         }
4172 
4173         @Override
readFloat()4174         public float readFloat() throws IOException {
4175             return Float.intBitsToFloat(readInt());
4176         }
4177 
4178         @Override
readDouble()4179         public double readDouble() throws IOException {
4180             return Double.longBitsToDouble(readLong());
4181         }
4182 
getLength()4183         public int getLength() {
4184             return mLength;
4185         }
4186     }
4187 
4188     // An output stream to write EXIF data area, which can be written in either little or big endian
4189     // order.
4190     private static class ByteOrderedDataOutputStream extends FilterOutputStream {
4191         private final OutputStream mOutputStream;
4192         private ByteOrder mByteOrder;
4193 
ByteOrderedDataOutputStream(OutputStream out, ByteOrder byteOrder)4194         public ByteOrderedDataOutputStream(OutputStream out, ByteOrder byteOrder) {
4195             super(out);
4196             mOutputStream = out;
4197             mByteOrder = byteOrder;
4198         }
4199 
setByteOrder(ByteOrder byteOrder)4200         public void setByteOrder(ByteOrder byteOrder) {
4201             mByteOrder = byteOrder;
4202         }
4203 
write(byte[] bytes)4204         public void write(byte[] bytes) throws IOException {
4205             mOutputStream.write(bytes);
4206         }
4207 
write(byte[] bytes, int offset, int length)4208         public void write(byte[] bytes, int offset, int length) throws IOException {
4209             mOutputStream.write(bytes, offset, length);
4210         }
4211 
writeByte(int val)4212         public void writeByte(int val) throws IOException {
4213             mOutputStream.write(val);
4214         }
4215 
writeShort(short val)4216         public void writeShort(short val) throws IOException {
4217             if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
4218                 mOutputStream.write((val >>> 0) & 0xFF);
4219                 mOutputStream.write((val >>> 8) & 0xFF);
4220             } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
4221                 mOutputStream.write((val >>> 8) & 0xFF);
4222                 mOutputStream.write((val >>> 0) & 0xFF);
4223             }
4224         }
4225 
writeInt(int val)4226         public void writeInt(int val) throws IOException {
4227             if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
4228                 mOutputStream.write((val >>> 0) & 0xFF);
4229                 mOutputStream.write((val >>> 8) & 0xFF);
4230                 mOutputStream.write((val >>> 16) & 0xFF);
4231                 mOutputStream.write((val >>> 24) & 0xFF);
4232             } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
4233                 mOutputStream.write((val >>> 24) & 0xFF);
4234                 mOutputStream.write((val >>> 16) & 0xFF);
4235                 mOutputStream.write((val >>> 8) & 0xFF);
4236                 mOutputStream.write((val >>> 0) & 0xFF);
4237             }
4238         }
4239 
writeUnsignedShort(int val)4240         public void writeUnsignedShort(int val) throws IOException {
4241             writeShort((short) val);
4242         }
4243 
writeUnsignedInt(long val)4244         public void writeUnsignedInt(long val) throws IOException {
4245             writeInt((int) val);
4246         }
4247     }
4248 
4249     // Swaps image data based on image size
swapBasedOnImageSize(@fdType int firstIfdType, @IfdType int secondIfdType)4250     private void swapBasedOnImageSize(@IfdType int firstIfdType, @IfdType int secondIfdType)
4251             throws IOException {
4252         if (mAttributes[firstIfdType].isEmpty() || mAttributes[secondIfdType].isEmpty()) {
4253             if (DEBUG) {
4254                 Log.d(TAG, "Cannot perform swap since only one image data exists");
4255             }
4256             return;
4257         }
4258 
4259         ExifAttribute firstImageLengthAttribute =
4260                 (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_LENGTH);
4261         ExifAttribute firstImageWidthAttribute =
4262                 (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_WIDTH);
4263         ExifAttribute secondImageLengthAttribute =
4264                 (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_LENGTH);
4265         ExifAttribute secondImageWidthAttribute =
4266                 (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_WIDTH);
4267 
4268         if (firstImageLengthAttribute == null || firstImageWidthAttribute == null) {
4269             if (DEBUG) {
4270                 Log.d(TAG, "First image does not contain valid size information");
4271             }
4272         } else if (secondImageLengthAttribute == null || secondImageWidthAttribute == null) {
4273             if (DEBUG) {
4274                 Log.d(TAG, "Second image does not contain valid size information");
4275             }
4276         } else {
4277             int firstImageLengthValue = firstImageLengthAttribute.getIntValue(mExifByteOrder);
4278             int firstImageWidthValue = firstImageWidthAttribute.getIntValue(mExifByteOrder);
4279             int secondImageLengthValue = secondImageLengthAttribute.getIntValue(mExifByteOrder);
4280             int secondImageWidthValue = secondImageWidthAttribute.getIntValue(mExifByteOrder);
4281 
4282             if (firstImageLengthValue < secondImageLengthValue &&
4283                     firstImageWidthValue < secondImageWidthValue) {
4284                 HashMap tempMap = mAttributes[firstIfdType];
4285                 mAttributes[firstIfdType] = mAttributes[secondIfdType];
4286                 mAttributes[secondIfdType] = tempMap;
4287             }
4288         }
4289     }
4290 
4291     // Checks if there is a match
containsMatch(byte[] mainBytes, byte[] findBytes)4292     private boolean containsMatch(byte[] mainBytes, byte[] findBytes) {
4293         for (int i = 0; i < mainBytes.length - findBytes.length; i++) {
4294             for (int j = 0; j < findBytes.length; j++) {
4295                 if (mainBytes[i + j] != findBytes[j]) {
4296                     break;
4297                 }
4298                 if (j == findBytes.length - 1) {
4299                     return true;
4300                 }
4301             }
4302         }
4303         return false;
4304     }
4305 
4306     /**
4307      * Convert given int[] to long[]. If long[] is given, just return it.
4308      * Return null for other types of input.
4309      */
convertToLongArray(Object inputObj)4310     private static long[] convertToLongArray(Object inputObj) {
4311         if (inputObj instanceof int[]) {
4312             int[] input = (int[]) inputObj;
4313             long[] result = new long[input.length];
4314             for (int i = 0; i < input.length; i++) {
4315                 result[i] = input[i];
4316             }
4317             return result;
4318         } else if (inputObj instanceof long[]) {
4319             return (long[]) inputObj;
4320         }
4321         return null;
4322     }
4323 }
4324