Home | History | Annotate | Download | only in cts
      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 
    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 
    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 
    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 
    226     private void assertIntTag(ExifInterface exifInterface, String tag, int expectedValue) {
    227         int intValue = exifInterface.getAttributeInt(tag, 0);
    228         assertEquals(expectedValue, intValue);
    229     }
    230 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    421     public void testReadExifDataFromExifByteOrderIIJpeg() throws Throwable {
    422         testExifInterfaceForJpeg(EXIF_BYTE_ORDER_II_JPEG, R.array.exifbyteorderii_jpg);
    423     }
    424 
    425     public void testReadExifDataFromExifByteOrderMMJpeg() throws Throwable {
    426         testExifInterfaceForJpeg(EXIF_BYTE_ORDER_MM_JPEG, R.array.exifbyteordermm_jpg);
    427     }
    428 
    429     public void testReadExifDataFromLgG4Iso800Dng() throws Throwable {
    430         testExifInterfaceForRaw(LG_G4_ISO_800_DNG, R.array.lg_g4_iso_800_dng);
    431     }
    432 
    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 
    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 
    450     public void testReadExifDataFromSonyRX100Arw() throws Throwable {
    451         testExifInterfaceForRaw(SONY_RX_100_ARW, R.array.sony_rx_100_arw);
    452     }
    453 
    454     public void testReadExifDataFromCanonG7XCr2() throws Throwable {
    455         testExifInterfaceForRaw(CANON_G7X_CR2, R.array.canon_g7x_cr2);
    456     }
    457 
    458     public void testReadExifDataFromFujiX20Raf() throws Throwable {
    459         testExifInterfaceForRaw(FUJI_X20_RAF, R.array.fuji_x20_raf);
    460     }
    461 
    462     public void testReadExifDataFromNikon1AW1Nef() throws Throwable {
    463         testExifInterfaceForRaw(NIKON_1AW1_NEF, R.array.nikon_1aw1_nef);
    464     }
    465 
    466     public void testReadExifDataFromNikonP330Nrw() throws Throwable {
    467         testExifInterfaceForRaw(NIKON_P330_NRW, R.array.nikon_p330_nrw);
    468     }
    469 
    470     public void testReadExifDataFromOlympusEPL3Orf() throws Throwable {
    471         testExifInterfaceForRaw(OLYMPUS_E_PL3_ORF, R.array.olympus_e_pl3_orf);
    472     }
    473 
    474     public void testReadExifDataFromPanasonicGM5Rw2() throws Throwable {
    475         testExifInterfaceForRaw(PANASONIC_GM5_RW2, R.array.panasonic_gm5_rw2);
    476     }
    477 
    478     public void testReadExifDataFromPentaxK5Pef() throws Throwable {
    479         testExifInterfaceForRaw(PENTAX_K5_PEF, R.array.pentax_k5_pef);
    480     }
    481 
    482     public void testReadExifDataFromSamsungNX3000Srw() throws Throwable {
    483         testExifInterfaceForRaw(SAMSUNG_NX3000_SRW, R.array.samsung_nx3000_srw);
    484     }
    485 
    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