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