1 /*
2  * Copyright (C) 2016 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.cts;
18 
19 import android.content.res.TypedArray;
20 import android.graphics.Bitmap;
21 import android.graphics.BitmapFactory;
22 import android.media.ExifInterface;
23 import android.os.Environment;
24 import android.os.FileUtils;
25 import android.platform.test.annotations.AppModeFull;
26 import android.test.AndroidTestCase;
27 import android.util.Log;
28 import android.system.ErrnoException;
29 import android.system.Os;
30 import android.system.OsConstants;
31 
32 import java.io.BufferedInputStream;
33 import java.io.ByteArrayInputStream;
34 import java.io.File;
35 import java.io.FileDescriptor;
36 import java.io.FileInputStream;
37 import java.io.FileOutputStream;
38 import java.io.FileNotFoundException;
39 import java.io.InputStream;
40 import java.io.IOException;
41 import java.lang.reflect.Type;
42 
43 import libcore.io.IoUtils;
44 import libcore.io.Streams;
45 
46 @AppModeFull(reason = "Instant apps cannot access the SD card")
47 public class ExifInterfaceTest extends AndroidTestCase {
48     private static final String TAG = ExifInterface.class.getSimpleName();
49     private static final boolean VERBOSE = false;  // lots of logging
50 
51     private static final double DIFFERENCE_TOLERANCE = .001;
52 
53     private static final String EXTERNAL_BASE_DIRECTORY = "/test/images/";
54 
55     // This base directory is needed for the files listed below.
56     // These files will be available for download in Android O release.
57     // Link: https://source.android.com/compatibility/cts/downloads.html#cts-media-files
58     private static final String EXIF_BYTE_ORDER_II_JPEG = "image_exif_byte_order_ii.jpg";
59     private static final String EXIF_BYTE_ORDER_MM_JPEG = "image_exif_byte_order_mm.jpg";
60     private static final String LG_G4_ISO_800_DNG = "lg_g4_iso_800.dng";
61     private static final String SONY_RX_100_ARW = "sony_rx_100.arw";
62     private static final String CANON_G7X_CR2 = "canon_g7x.cr2";
63     private static final String FUJI_X20_RAF = "fuji_x20.raf";
64     private static final String NIKON_1AW1_NEF = "nikon_1aw1.nef";
65     private static final String NIKON_P330_NRW = "nikon_p330.nrw";
66     private static final String OLYMPUS_E_PL3_ORF = "olympus_e_pl3.orf";
67     private static final String PANASONIC_GM5_RW2 = "panasonic_gm5.rw2";
68     private static final String PENTAX_K5_PEF = "pentax_k5.pef";
69     private static final String SAMSUNG_NX3000_SRW = "samsung_nx3000.srw";
70     private static final String VOLANTIS_JPEG = "volantis.jpg";
71 
72     private static final String[] EXIF_TAGS = {
73             ExifInterface.TAG_MAKE,
74             ExifInterface.TAG_MODEL,
75             ExifInterface.TAG_F_NUMBER,
76             ExifInterface.TAG_DATETIME_ORIGINAL,
77             ExifInterface.TAG_EXPOSURE_TIME,
78             ExifInterface.TAG_FLASH,
79             ExifInterface.TAG_FOCAL_LENGTH,
80             ExifInterface.TAG_GPS_ALTITUDE,
81             ExifInterface.TAG_GPS_ALTITUDE_REF,
82             ExifInterface.TAG_GPS_DATESTAMP,
83             ExifInterface.TAG_GPS_LATITUDE,
84             ExifInterface.TAG_GPS_LATITUDE_REF,
85             ExifInterface.TAG_GPS_LONGITUDE,
86             ExifInterface.TAG_GPS_LONGITUDE_REF,
87             ExifInterface.TAG_GPS_PROCESSING_METHOD,
88             ExifInterface.TAG_GPS_TIMESTAMP,
89             ExifInterface.TAG_IMAGE_LENGTH,
90             ExifInterface.TAG_IMAGE_WIDTH,
91             ExifInterface.TAG_ISO_SPEED_RATINGS,
92             ExifInterface.TAG_ORIENTATION,
93             ExifInterface.TAG_WHITE_BALANCE
94     };
95 
96     private static class ExpectedValue {
97         // Thumbnail information.
98         public final boolean hasThumbnail;
99         public final int thumbnailWidth;
100         public final int thumbnailHeight;
101         public final boolean isThumbnailCompressed;
102 
103         // GPS information.
104         public final boolean hasLatLong;
105         public final float latitude;
106         public final float longitude;
107         public final float altitude;
108 
109         // Values.
110         public final String make;
111         public final String model;
112         public final float aperture;
113         public final String dateTimeOriginal;
114         public final float exposureTime;
115         public final float flash;
116         public final String focalLength;
117         public final String gpsAltitude;
118         public final String gpsAltitudeRef;
119         public final String gpsDatestamp;
120         public final String gpsLatitude;
121         public final String gpsLatitudeRef;
122         public final String gpsLongitude;
123         public final String gpsLongitudeRef;
124         public final String gpsProcessingMethod;
125         public final String gpsTimestamp;
126         public final int imageLength;
127         public final int imageWidth;
128         public final String iso;
129         public final int orientation;
130         public final int whiteBalance;
131 
getString(TypedArray typedArray, int index)132         private static String getString(TypedArray typedArray, int index) {
133             String stringValue = typedArray.getString(index);
134             if (stringValue == null || stringValue.equals("")) {
135                 return null;
136             }
137             return stringValue.trim();
138         }
139 
ExpectedValue(TypedArray typedArray)140         public ExpectedValue(TypedArray typedArray) {
141             int index = 0;
142 
143             // Reads thumbnail information.
144             hasThumbnail = typedArray.getBoolean(index++, false);
145             thumbnailWidth = typedArray.getInt(index++, 0);
146             thumbnailHeight = typedArray.getInt(index++, 0);
147             isThumbnailCompressed = typedArray.getBoolean(index++, false);
148 
149             // Reads GPS information.
150             hasLatLong = typedArray.getBoolean(index++, false);
151             latitude = typedArray.getFloat(index++, 0f);
152             longitude = typedArray.getFloat(index++, 0f);
153             altitude = typedArray.getFloat(index++, 0f);
154 
155             // Reads values.
156             make = getString(typedArray, index++);
157             model = getString(typedArray, index++);
158             aperture = typedArray.getFloat(index++, 0f);
159             dateTimeOriginal = getString(typedArray, index++);
160             exposureTime = typedArray.getFloat(index++, 0f);
161             flash = typedArray.getFloat(index++, 0f);
162             focalLength = getString(typedArray, index++);
163             gpsAltitude = getString(typedArray, index++);
164             gpsAltitudeRef = getString(typedArray, index++);
165             gpsDatestamp = getString(typedArray, index++);
166             gpsLatitude = getString(typedArray, index++);
167             gpsLatitudeRef = getString(typedArray, index++);
168             gpsLongitude = getString(typedArray, index++);
169             gpsLongitudeRef = getString(typedArray, index++);
170             gpsProcessingMethod = getString(typedArray, index++);
171             gpsTimestamp = getString(typedArray, index++);
172             imageLength = typedArray.getInt(index++, 0);
173             imageWidth = typedArray.getInt(index++, 0);
174             iso = getString(typedArray, index++);
175             orientation = typedArray.getInt(index++, 0);
176             whiteBalance = typedArray.getInt(index++, 0);
177 
178             typedArray.recycle();
179         }
180     }
181 
printExifTagsAndValues(String fileName, ExifInterface exifInterface)182     private void printExifTagsAndValues(String fileName, ExifInterface exifInterface) {
183         // Prints thumbnail information.
184         if (exifInterface.hasThumbnail()) {
185             byte[] thumbnailBytes = exifInterface.getThumbnailBytes();
186             if (thumbnailBytes != null) {
187                 Log.v(TAG, fileName + " Thumbnail size = " + thumbnailBytes.length);
188                 Bitmap bitmap = exifInterface.getThumbnailBitmap();
189                 if (bitmap == null) {
190                     Log.e(TAG, fileName + " Corrupted thumbnail!");
191                 } else {
192                     Log.v(TAG, fileName + " Thumbnail size: " + bitmap.getWidth() + ", "
193                             + bitmap.getHeight());
194                 }
195             } else {
196                 Log.e(TAG, fileName + " Unexpected result: No thumbnails were found. "
197                         + "A thumbnail is expected.");
198             }
199         } else {
200             if (exifInterface.getThumbnailBytes() != null) {
201                 Log.e(TAG, fileName + " Unexpected result: A thumbnail was found. "
202                         + "No thumbnail is expected.");
203             } else {
204                 Log.v(TAG, fileName + " No thumbnail");
205             }
206         }
207 
208         // Prints GPS information.
209         Log.v(TAG, fileName + " Altitude = " + exifInterface.getAltitude(.0));
210 
211         float[] latLong = new float[2];
212         if (exifInterface.getLatLong(latLong)) {
213             Log.v(TAG, fileName + " Latitude = " + latLong[0]);
214             Log.v(TAG, fileName + " Longitude = " + latLong[1]);
215         } else {
216             Log.v(TAG, fileName + " No latlong data");
217         }
218 
219         // Prints values.
220         for (String tagKey : EXIF_TAGS) {
221             String tagValue = exifInterface.getAttribute(tagKey);
222             Log.v(TAG, fileName + " Key{" + tagKey + "} = '" + tagValue + "'");
223         }
224     }
225 
assertIntTag(ExifInterface exifInterface, String tag, int expectedValue)226     private void assertIntTag(ExifInterface exifInterface, String tag, int expectedValue) {
227         int intValue = exifInterface.getAttributeInt(tag, 0);
228         assertEquals(expectedValue, intValue);
229     }
230 
assertFloatTag(ExifInterface exifInterface, String tag, float expectedValue)231     private void assertFloatTag(ExifInterface exifInterface, String tag, float expectedValue) {
232         double doubleValue = exifInterface.getAttributeDouble(tag, 0.0);
233         assertEquals(expectedValue, doubleValue, DIFFERENCE_TOLERANCE);
234     }
235 
assertStringTag(ExifInterface exifInterface, String tag, String expectedValue)236     private void assertStringTag(ExifInterface exifInterface, String tag, String expectedValue) {
237         String stringValue = exifInterface.getAttribute(tag);
238         if (stringValue != null) {
239             stringValue = stringValue.trim();
240         }
241         stringValue = (stringValue == "") ? null : stringValue;
242 
243         assertEquals(expectedValue, stringValue);
244     }
245 
compareWithExpectedValue(ExifInterface exifInterface, ExpectedValue expectedValue, String verboseTag)246     private void compareWithExpectedValue(ExifInterface exifInterface,
247             ExpectedValue expectedValue, String verboseTag) {
248         if (VERBOSE) {
249             printExifTagsAndValues(verboseTag, exifInterface);
250         }
251         // Checks a thumbnail image.
252         assertEquals(expectedValue.hasThumbnail, exifInterface.hasThumbnail());
253         if (expectedValue.hasThumbnail) {
254             byte[] thumbnailBytes = exifInterface.getThumbnailBytes();
255             assertNotNull(thumbnailBytes);
256             Bitmap thumbnailBitmap = exifInterface.getThumbnailBitmap();
257             assertNotNull(thumbnailBitmap);
258             assertEquals(expectedValue.thumbnailWidth, thumbnailBitmap.getWidth());
259             assertEquals(expectedValue.thumbnailHeight, thumbnailBitmap.getHeight());
260             assertEquals(expectedValue.isThumbnailCompressed,
261                     exifInterface.isThumbnailCompressed());
262         } else {
263             assertNull(exifInterface.getThumbnail());
264         }
265 
266         // Checks GPS information.
267         float[] latLong = new float[2];
268         assertEquals(expectedValue.hasLatLong, exifInterface.getLatLong(latLong));
269         if (expectedValue.hasLatLong) {
270             assertEquals(expectedValue.latitude, latLong[0], DIFFERENCE_TOLERANCE);
271             assertEquals(expectedValue.longitude, latLong[1], DIFFERENCE_TOLERANCE);
272         }
273         assertEquals(expectedValue.altitude, exifInterface.getAltitude(.0), DIFFERENCE_TOLERANCE);
274 
275         // Checks values.
276         assertStringTag(exifInterface, ExifInterface.TAG_MAKE, expectedValue.make);
277         assertStringTag(exifInterface, ExifInterface.TAG_MODEL, expectedValue.model);
278         assertFloatTag(exifInterface, ExifInterface.TAG_F_NUMBER, expectedValue.aperture);
279         assertStringTag(exifInterface, ExifInterface.TAG_DATETIME_ORIGINAL,
280                 expectedValue.dateTimeOriginal);
281         assertFloatTag(exifInterface, ExifInterface.TAG_EXPOSURE_TIME, expectedValue.exposureTime);
282         assertFloatTag(exifInterface, ExifInterface.TAG_FLASH, expectedValue.flash);
283         assertStringTag(exifInterface, ExifInterface.TAG_FOCAL_LENGTH, expectedValue.focalLength);
284         assertStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE, expectedValue.gpsAltitude);
285         assertStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE_REF,
286                 expectedValue.gpsAltitudeRef);
287         assertStringTag(exifInterface, ExifInterface.TAG_GPS_DATESTAMP, expectedValue.gpsDatestamp);
288         assertStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE, expectedValue.gpsLatitude);
289         assertStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE_REF,
290                 expectedValue.gpsLatitudeRef);
291         assertStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE, expectedValue.gpsLongitude);
292         assertStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE_REF,
293                 expectedValue.gpsLongitudeRef);
294         assertStringTag(exifInterface, ExifInterface.TAG_GPS_PROCESSING_METHOD,
295                 expectedValue.gpsProcessingMethod);
296         assertStringTag(exifInterface, ExifInterface.TAG_GPS_TIMESTAMP, expectedValue.gpsTimestamp);
297         assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_LENGTH, expectedValue.imageLength);
298         assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_WIDTH, expectedValue.imageWidth);
299         assertStringTag(exifInterface, ExifInterface.TAG_ISO_SPEED_RATINGS, expectedValue.iso);
300         assertIntTag(exifInterface, ExifInterface.TAG_ORIENTATION, expectedValue.orientation);
301         assertIntTag(exifInterface, ExifInterface.TAG_WHITE_BALANCE, expectedValue.whiteBalance);
302     }
303 
testExifInterfaceCommon(String fileName, ExpectedValue expectedValue)304     private void testExifInterfaceCommon(String fileName, ExpectedValue expectedValue)
305             throws IOException {
306         File imageFile = new File(Environment.getExternalStorageDirectory(), fileName);
307         String verboseTag = imageFile.getName();
308 
309         // Creates via path.
310         ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
311         assertNotNull(exifInterface);
312         compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
313 
314         InputStream in = null;
315         // Creates via InputStream.
316         try {
317             in = new BufferedInputStream(new FileInputStream(imageFile.getAbsolutePath()));
318             exifInterface = new ExifInterface(in);
319             compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
320         } finally {
321             IoUtils.closeQuietly(in);
322         }
323 
324         // Creates via FileDescriptor.
325         FileDescriptor fd = null;
326         try {
327             fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDONLY, 0600);
328             exifInterface = new ExifInterface(fd);
329             compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
330         } catch (ErrnoException e) {
331             throw e.rethrowAsIOException();
332         } finally {
333             IoUtils.closeQuietly(fd);
334         }
335     }
336 
testSaveAttributes_withFileName(String fileName, ExpectedValue expectedValue)337     private void testSaveAttributes_withFileName(String fileName, ExpectedValue expectedValue)
338             throws IOException {
339         File imageFile = new File(Environment.getExternalStorageDirectory(), fileName);
340         String verboseTag = imageFile.getName();
341 
342         ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
343         exifInterface.saveAttributes();
344         exifInterface = new ExifInterface(imageFile.getAbsolutePath());
345         compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
346 
347         // Test for modifying one attribute.
348         String backupValue = exifInterface.getAttribute(ExifInterface.TAG_MAKE);
349         exifInterface.setAttribute(ExifInterface.TAG_MAKE, "abc");
350         exifInterface.saveAttributes();
351         exifInterface = new ExifInterface(imageFile.getAbsolutePath());
352         assertEquals("abc", exifInterface.getAttribute(ExifInterface.TAG_MAKE));
353         // Restore the backup value.
354         exifInterface.setAttribute(ExifInterface.TAG_MAKE, backupValue);
355         exifInterface.saveAttributes();
356         exifInterface = new ExifInterface(imageFile.getAbsolutePath());
357         compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
358     }
359 
testSaveAttributes_withFileDescriptor(String fileName, ExpectedValue expectedValue)360     private void testSaveAttributes_withFileDescriptor(String fileName, ExpectedValue expectedValue)
361             throws IOException {
362         File imageFile = new File(Environment.getExternalStorageDirectory(), fileName);
363         String verboseTag = imageFile.getName();
364 
365         FileDescriptor fd = null;
366         try {
367             fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDWR, 0600);
368             ExifInterface exifInterface = new ExifInterface(fd);
369             exifInterface.saveAttributes();
370             Os.lseek(fd, 0, OsConstants.SEEK_SET);
371             exifInterface = new ExifInterface(fd);
372             compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
373 
374             // Test for modifying one attribute.
375             String backupValue = exifInterface.getAttribute(ExifInterface.TAG_MAKE);
376             exifInterface.setAttribute(ExifInterface.TAG_MAKE, "abc");
377             exifInterface.saveAttributes();
378             Os.lseek(fd, 0, OsConstants.SEEK_SET);
379             exifInterface = new ExifInterface(fd);
380             assertEquals("abc", exifInterface.getAttribute(ExifInterface.TAG_MAKE));
381             // Restore the backup value.
382             exifInterface.setAttribute(ExifInterface.TAG_MAKE, backupValue);
383             exifInterface.saveAttributes();
384             Os.lseek(fd, 0, OsConstants.SEEK_SET);
385             exifInterface = new ExifInterface(fd);
386             compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
387         } catch (ErrnoException e) {
388             throw e.rethrowAsIOException();
389         } finally {
390             IoUtils.closeQuietly(fd);
391         }
392     }
393 
testExifInterfaceForJpeg(String fileName, int typedArrayResourceId)394     private void testExifInterfaceForJpeg(String fileName, int typedArrayResourceId)
395             throws IOException {
396         ExpectedValue expectedValue = new ExpectedValue(
397                 getContext().getResources().obtainTypedArray(typedArrayResourceId));
398 
399         // Test for reading from external data storage.
400         fileName = EXTERNAL_BASE_DIRECTORY + fileName;
401         testExifInterfaceCommon(fileName, expectedValue);
402 
403         // Test for saving attributes.
404         testSaveAttributes_withFileName(fileName, expectedValue);
405         testSaveAttributes_withFileDescriptor(fileName, expectedValue);
406     }
407 
testExifInterfaceForRaw(String fileName, int typedArrayResourceId)408     private void testExifInterfaceForRaw(String fileName, int typedArrayResourceId)
409             throws IOException {
410         ExpectedValue expectedValue = new ExpectedValue(
411                 getContext().getResources().obtainTypedArray(typedArrayResourceId));
412 
413         // Test for reading from external data storage.
414         fileName = EXTERNAL_BASE_DIRECTORY + fileName;
415         testExifInterfaceCommon(fileName, expectedValue);
416 
417         // Since ExifInterface does not support for saving attributes for RAW files, do not test
418         // about writing back in here.
419     }
420 
testReadExifDataFromExifByteOrderIIJpeg()421     public void testReadExifDataFromExifByteOrderIIJpeg() throws Throwable {
422         testExifInterfaceForJpeg(EXIF_BYTE_ORDER_II_JPEG, R.array.exifbyteorderii_jpg);
423     }
424 
testReadExifDataFromExifByteOrderMMJpeg()425     public void testReadExifDataFromExifByteOrderMMJpeg() throws Throwable {
426         testExifInterfaceForJpeg(EXIF_BYTE_ORDER_MM_JPEG, R.array.exifbyteordermm_jpg);
427     }
428 
testReadExifDataFromLgG4Iso800Dng()429     public void testReadExifDataFromLgG4Iso800Dng() throws Throwable {
430         testExifInterfaceForRaw(LG_G4_ISO_800_DNG, R.array.lg_g4_iso_800_dng);
431     }
432 
testDoNotFailOnCorruptedImage()433     public void testDoNotFailOnCorruptedImage() throws Throwable {
434         // To keep the compatibility with old versions of ExifInterface, even on a corrupted image,
435         // it shouldn't raise any exceptions except an IOException when unable to open a file.
436         byte[] bytes = new byte[1024];
437         try {
438             new ExifInterface(new ByteArrayInputStream(bytes));
439             // Always success
440         } catch (IOException e) {
441             fail("Should not reach here!");
442         }
443     }
444 
testReadExifDataFromVolantisJpg()445     public void testReadExifDataFromVolantisJpg() throws Throwable {
446         // Test if it is possible to parse the volantis generated JPEG smoothly.
447         testExifInterfaceForJpeg(VOLANTIS_JPEG, R.array.volantis_jpg);
448     }
449 
testReadExifDataFromSonyRX100Arw()450     public void testReadExifDataFromSonyRX100Arw() throws Throwable {
451         testExifInterfaceForRaw(SONY_RX_100_ARW, R.array.sony_rx_100_arw);
452     }
453 
testReadExifDataFromCanonG7XCr2()454     public void testReadExifDataFromCanonG7XCr2() throws Throwable {
455         testExifInterfaceForRaw(CANON_G7X_CR2, R.array.canon_g7x_cr2);
456     }
457 
testReadExifDataFromFujiX20Raf()458     public void testReadExifDataFromFujiX20Raf() throws Throwable {
459         testExifInterfaceForRaw(FUJI_X20_RAF, R.array.fuji_x20_raf);
460     }
461 
testReadExifDataFromNikon1AW1Nef()462     public void testReadExifDataFromNikon1AW1Nef() throws Throwable {
463         testExifInterfaceForRaw(NIKON_1AW1_NEF, R.array.nikon_1aw1_nef);
464     }
465 
testReadExifDataFromNikonP330Nrw()466     public void testReadExifDataFromNikonP330Nrw() throws Throwable {
467         testExifInterfaceForRaw(NIKON_P330_NRW, R.array.nikon_p330_nrw);
468     }
469 
testReadExifDataFromOlympusEPL3Orf()470     public void testReadExifDataFromOlympusEPL3Orf() throws Throwable {
471         testExifInterfaceForRaw(OLYMPUS_E_PL3_ORF, R.array.olympus_e_pl3_orf);
472     }
473 
testReadExifDataFromPanasonicGM5Rw2()474     public void testReadExifDataFromPanasonicGM5Rw2() throws Throwable {
475         testExifInterfaceForRaw(PANASONIC_GM5_RW2, R.array.panasonic_gm5_rw2);
476     }
477 
testReadExifDataFromPentaxK5Pef()478     public void testReadExifDataFromPentaxK5Pef() throws Throwable {
479         testExifInterfaceForRaw(PENTAX_K5_PEF, R.array.pentax_k5_pef);
480     }
481 
testReadExifDataFromSamsungNX3000Srw()482     public void testReadExifDataFromSamsungNX3000Srw() throws Throwable {
483         testExifInterfaceForRaw(SAMSUNG_NX3000_SRW, R.array.samsung_nx3000_srw);
484     }
485 
testSetDateTime()486     public void testSetDateTime() throws IOException {
487         final String dateTimeValue = "2017:02:02 22:22:22";
488         final String dateTimeOriginalValue = "2017:01:01 11:11:11";
489 
490         File srcFile = new File(Environment.getExternalStorageDirectory(),
491                 EXTERNAL_BASE_DIRECTORY + EXIF_BYTE_ORDER_II_JPEG);
492         File imageFile = new File(Environment.getExternalStorageDirectory(),
493                 EXTERNAL_BASE_DIRECTORY + EXIF_BYTE_ORDER_II_JPEG + "_copied");
494 
495         FileUtils.copyFileOrThrow(srcFile, imageFile);
496         ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
497         exif.setAttribute(ExifInterface.TAG_DATETIME, dateTimeValue);
498         exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL, dateTimeOriginalValue);
499         exif.saveAttributes();
500 
501         // Check that the DATETIME value is not overwritten by DATETIME_ORIGINAL's value.
502         exif = new ExifInterface(imageFile.getAbsolutePath());
503         assertEquals(dateTimeValue, exif.getAttribute(ExifInterface.TAG_DATETIME));
504         assertEquals(dateTimeOriginalValue, exif.getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL));
505 
506         // Now remove the DATETIME value.
507         exif.setAttribute(ExifInterface.TAG_DATETIME, null);
508         exif.saveAttributes();
509 
510         // When the DATETIME has no value, then it should be set to DATETIME_ORIGINAL's value.
511         exif = new ExifInterface(imageFile.getAbsolutePath());
512         assertEquals(dateTimeOriginalValue, exif.getAttribute(ExifInterface.TAG_DATETIME));
513         imageFile.delete();
514     }
515 }
516