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