blob: e2905d440780d1d77b7d1abe5f67d9652b7cb54a [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media.cts;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.os.Environment;
import android.test.AndroidTestCase;
import android.util.Log;
import com.android.cts.media.R;
import libcore.io.IoUtils;
import libcore.io.Streams;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.lang.reflect.Type;
public class ExifInterfaceTest extends AndroidTestCase {
private static final String TAG = ExifInterface.class.getSimpleName();
private static final boolean VERBOSE = false; // lots of logging
private static final double DIFFERENCE_TOLERANCE = .001;
// List of files.
private static final String EXIF_BYTE_ORDER_II_JPEG = "image_exif_byte_order_ii.jpg";
private static final String EXIF_BYTE_ORDER_MM_JPEG = "image_exif_byte_order_mm.jpg";
private static final String EXIF_MALFORMED_JPEG = "image_exif_malformed.jpg";
private static final int[] IMAGE_RESOURCES = new int[] { R.raw.image_exif_byte_order_ii,
R.raw.image_exif_byte_order_mm, R.raw.image_exif_malformed };
private static final String[] IMAGE_FILENAMES = new String[] { EXIF_BYTE_ORDER_II_JPEG,
EXIF_BYTE_ORDER_MM_JPEG, EXIF_MALFORMED_JPEG };
private static final String[] EXIF_TAGS = {
ExifInterface.TAG_MAKE,
ExifInterface.TAG_MODEL,
ExifInterface.TAG_APERTURE,
ExifInterface.TAG_DATETIME,
ExifInterface.TAG_EXPOSURE_TIME,
ExifInterface.TAG_FLASH,
ExifInterface.TAG_FOCAL_LENGTH,
ExifInterface.TAG_GPS_ALTITUDE,
ExifInterface.TAG_GPS_ALTITUDE_REF,
ExifInterface.TAG_GPS_DATESTAMP,
ExifInterface.TAG_GPS_LATITUDE,
ExifInterface.TAG_GPS_LATITUDE_REF,
ExifInterface.TAG_GPS_LONGITUDE,
ExifInterface.TAG_GPS_LONGITUDE_REF,
ExifInterface.TAG_GPS_PROCESSING_METHOD,
ExifInterface.TAG_GPS_TIMESTAMP,
ExifInterface.TAG_IMAGE_LENGTH,
ExifInterface.TAG_IMAGE_WIDTH,
ExifInterface.TAG_ISO,
ExifInterface.TAG_ORIENTATION,
ExifInterface.TAG_WHITE_BALANCE
};
private static class ExpectedValue {
// Thumbnail information.
public final boolean hasThumbnail;
public final int thumbnailWidth;
public final int thumbnailHeight;
// GPS information.
public final boolean hasLatLong;
public final float latitude;
public final float longitude;
public final float altitude;
// Values.
public final String make;
public final String model;
public final float aperture;
public final String datetime;
public final float exposureTime;
public final float flash;
public final String focalLength;
public final String gpsAltitude;
public final String gpsAltitudeRef;
public final String gpsDatestamp;
public final String gpsLatitude;
public final String gpsLatitudeRef;
public final String gpsLongitude;
public final String gpsLongitudeRef;
public final String gpsProcessingMethod;
public final String gpsTimestamp;
public final int imageLength;
public final int imageWidth;
public final String iso;
public final int orientation;
public final int whiteBalance;
private static String getString(TypedArray typedArray, int index) {
String stringValue = typedArray.getString(index);
if (stringValue == null || stringValue.equals("")) {
return null;
}
return stringValue.trim();
}
public ExpectedValue(TypedArray typedArray) {
// Reads thumbnail information.
hasThumbnail = typedArray.getBoolean(0, false);
thumbnailWidth = typedArray.getInt(1, 0);
thumbnailHeight = typedArray.getInt(2, 0);
// Reads GPS information.
hasLatLong = typedArray.getBoolean(3, false);
latitude = typedArray.getFloat(4, 0f);
longitude = typedArray.getFloat(5, 0f);
altitude = typedArray.getFloat(6, 0f);
// Reads values.
make = getString(typedArray, 7);
model = getString(typedArray, 8);
aperture = typedArray.getFloat(9, 0f);
datetime = getString(typedArray, 10);
exposureTime = typedArray.getFloat(11, 0f);
flash = typedArray.getFloat(12, 0f);
focalLength = getString(typedArray, 13);
gpsAltitude = getString(typedArray, 14);
gpsAltitudeRef = getString(typedArray, 15);
gpsDatestamp = getString(typedArray, 16);
gpsLatitude = getString(typedArray, 17);
gpsLatitudeRef = getString(typedArray, 18);
gpsLongitude = getString(typedArray, 19);
gpsLongitudeRef = getString(typedArray, 20);
gpsProcessingMethod = getString(typedArray, 21);
gpsTimestamp = getString(typedArray, 22);
imageLength = typedArray.getInt(23, 0);
imageWidth = typedArray.getInt(24, 0);
iso = getString(typedArray, 25);
orientation = typedArray.getInt(26, 0);
whiteBalance = typedArray.getInt(27, 0);
typedArray.recycle();
}
}
@Override
protected void setUp() throws Exception {
for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
String outputPath = new File(Environment.getExternalStorageDirectory(),
IMAGE_FILENAMES[i]).getAbsolutePath();
InputStream inputStream = null;
FileOutputStream outputStream = null;
try {
inputStream = getContext().getResources().openRawResource(IMAGE_RESOURCES[i]);
outputStream = new FileOutputStream(outputPath);
Streams.copy(inputStream, outputStream);
} finally {
IoUtils.closeQuietly(inputStream);
IoUtils.closeQuietly(outputStream);
}
}
super.setUp();
}
@Override
protected void tearDown() throws Exception {
for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
String imageFilePath = new File(Environment.getExternalStorageDirectory(),
IMAGE_FILENAMES[i]).getAbsolutePath();
File imageFile = new File(imageFilePath);
if (imageFile.exists()) {
imageFile.delete();
}
}
super.tearDown();
}
public void testReadExifDataFromExifByteOrderIIJpeg() throws Throwable {
ExpectedValue expectedValue = new ExpectedValue(
getContext().getResources().obtainTypedArray(R.array.exifbyteorderii_jpg));
File imageFile = new File(
Environment.getExternalStorageDirectory(), EXIF_BYTE_ORDER_II_JPEG);
try {
// Test for reading from various inputs.
testExifInterfaceCommon(imageFile, expectedValue);
// Test for saving attributes.
testSaveAttributes(imageFile, expectedValue);
} catch (Exception e) {
assertTrue(false);
}
}
public void testReadExifDataFromExifByteOrderMMJpeg() throws Throwable {
ExpectedValue expectedValue = new ExpectedValue(
getContext().getResources().obtainTypedArray(R.array.exifbyteordermm_jpg));
File imageFile = new File(
Environment.getExternalStorageDirectory(), EXIF_BYTE_ORDER_MM_JPEG);
try {
// Test for reading from various inputs.
testExifInterfaceCommon(imageFile, expectedValue);
// Test for saving attributes.
testSaveAttributes(imageFile, expectedValue);
} catch (Exception e) {
assertTrue(false);
}
}
public void testReadExifDataFromMalformedJpeg() throws Throwable {
File imageFile = new File(Environment.getExternalStorageDirectory(), EXIF_MALFORMED_JPEG);
try {
ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
} catch (Exception e) {
assertTrue(false);
}
}
private void printExifTagsAndValues(String fileName, ExifInterface exifInterface) {
// Prints thumbnail information.
if (exifInterface.hasThumbnail()) {
byte[] thumbnailBytes = exifInterface.getThumbnail();
if (thumbnailBytes != null) {
Log.v(TAG, fileName + " Thumbnail size = " + thumbnailBytes.length);
Bitmap bitmap = BitmapFactory.decodeByteArray(
thumbnailBytes, 0, thumbnailBytes.length);
if (bitmap == null) {
Log.e(TAG, fileName + " Corrupted thumbnail!");
} else {
Log.v(TAG, fileName + " Thumbnail size: " + bitmap.getWidth() + ", "
+ bitmap.getHeight());
}
} else {
Log.e(TAG, fileName + " Unexpected result: No thumbnails were found. "
+ "A thumbnail is expected.");
}
} else {
if (exifInterface.getThumbnail() != null) {
Log.e(TAG, fileName + " Unexpected result: A thumbnail was found. "
+ "No thumbnail is expected.");
} else {
Log.v(TAG, fileName + " No thumbnail");
}
}
// Prints GPS information.
Log.v(TAG, fileName + " Altitude = " + exifInterface.getAltitude(.0));
float[] latLong = new float[2];
if (exifInterface.getLatLong(latLong)) {
Log.v(TAG, fileName + " Latitude = " + latLong[0]);
Log.v(TAG, fileName + " Longitude = " + latLong[1]);
} else {
Log.v(TAG, fileName + " No latlong data");
}
// Prints values.
for (String tagKey : EXIF_TAGS) {
String tagValue = exifInterface.getAttribute(tagKey);
Log.v(TAG, fileName + " Key{" + tagKey + "} = '" + tagValue + "'");
}
}
private void assertIntTag(ExifInterface exifInterface, String tag, int expectedValue) {
int intValue = exifInterface.getAttributeInt(tag, 0);
assertEquals(expectedValue, intValue);
}
private void assertFloatTag(ExifInterface exifInterface, String tag, float expectedValue) {
double doubleValue = exifInterface.getAttributeDouble(tag, expectedValue - 10);
if (doubleValue == expectedValue - 10) {
try {
doubleValue = Double.parseDouble(exifInterface.getAttribute(tag));
} catch (Exception e) {
doubleValue = 0.0;
}
}
assertEquals(expectedValue, doubleValue, DIFFERENCE_TOLERANCE);
}
private void assertStringTag(ExifInterface exifInterface, String tag, String expectedValue) {
String stringValue = exifInterface.getAttribute(tag);
if (stringValue != null) {
stringValue = stringValue.trim();
}
assertEquals(expectedValue, stringValue);
}
private void compareWithExpectedValue(ExifInterface exifInterface,
ExpectedValue expectedValue, String verboseTag) {
if (VERBOSE) {
printExifTagsAndValues(verboseTag, exifInterface);
}
// Checks a thumbnail image.
assertEquals(expectedValue.hasThumbnail, exifInterface.hasThumbnail());
if (expectedValue.hasThumbnail) {
byte[] thumbnailBytes = exifInterface.getThumbnail();
assertNotNull(thumbnailBytes);
Bitmap thumbnailBitmap =
BitmapFactory.decodeByteArray(thumbnailBytes, 0, thumbnailBytes.length);
assertNotNull(thumbnailBitmap);
assertEquals(expectedValue.thumbnailWidth, thumbnailBitmap.getWidth());
assertEquals(expectedValue.thumbnailHeight, thumbnailBitmap.getHeight());
} else {
assertNull(exifInterface.getThumbnail());
}
// Checks GPS information.
float[] latLong = new float[2];
assertEquals(expectedValue.hasLatLong, exifInterface.getLatLong(latLong));
if (expectedValue.hasLatLong) {
assertEquals(expectedValue.latitude, latLong[0], DIFFERENCE_TOLERANCE);
assertEquals(expectedValue.longitude, latLong[1], DIFFERENCE_TOLERANCE);
}
assertEquals(expectedValue.altitude, exifInterface.getAltitude(.0), DIFFERENCE_TOLERANCE);
// Checks values.
assertStringTag(exifInterface, ExifInterface.TAG_MAKE, expectedValue.make);
assertStringTag(exifInterface, ExifInterface.TAG_MODEL, expectedValue.model);
assertFloatTag(exifInterface, ExifInterface.TAG_APERTURE, expectedValue.aperture);
assertStringTag(exifInterface, ExifInterface.TAG_DATETIME, expectedValue.datetime);
assertFloatTag(exifInterface, ExifInterface.TAG_EXPOSURE_TIME, expectedValue.exposureTime);
assertFloatTag(exifInterface, ExifInterface.TAG_FLASH, expectedValue.flash);
assertStringTag(exifInterface, ExifInterface.TAG_FOCAL_LENGTH, expectedValue.focalLength);
assertStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE, expectedValue.gpsAltitude);
assertStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE_REF,
expectedValue.gpsAltitudeRef);
assertStringTag(exifInterface, ExifInterface.TAG_GPS_DATESTAMP, expectedValue.gpsDatestamp);
assertStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE, expectedValue.gpsLatitude);
assertStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE_REF,
expectedValue.gpsLatitudeRef);
assertStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE, expectedValue.gpsLongitude);
assertStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE_REF,
expectedValue.gpsLongitudeRef);
assertStringTag(exifInterface, ExifInterface.TAG_GPS_PROCESSING_METHOD,
expectedValue.gpsProcessingMethod);
assertStringTag(exifInterface, ExifInterface.TAG_GPS_TIMESTAMP, expectedValue.gpsTimestamp);
assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_LENGTH, expectedValue.imageLength);
assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_WIDTH, expectedValue.imageWidth);
assertStringTag(exifInterface, ExifInterface.TAG_ISO, expectedValue.iso);
assertIntTag(exifInterface, ExifInterface.TAG_ORIENTATION, expectedValue.orientation);
assertIntTag(exifInterface, ExifInterface.TAG_WHITE_BALANCE, expectedValue.whiteBalance);
}
private void testExifInterfaceCommon(File imageFile, ExpectedValue expectedValue)
throws IOException {
String verboseTag = imageFile.getName();
// Creates via path.
ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
assertNotNull(exifInterface);
compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
}
private void testSaveAttributes(File imageFile, ExpectedValue expectedValue)
throws IOException {
String verboseTag = imageFile.getName();
ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
exifInterface.saveAttributes();
exifInterface = new ExifInterface(imageFile.getAbsolutePath());
compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
// Test for modifying one attribute.
String backupValue = exifInterface.getAttribute(ExifInterface.TAG_MAKE);
exifInterface.setAttribute(ExifInterface.TAG_MAKE, "abc");
exifInterface.saveAttributes();
exifInterface = new ExifInterface(imageFile.getAbsolutePath());
assertEquals("abc", exifInterface.getAttribute(ExifInterface.TAG_MAKE));
// Restore the backup value.
exifInterface.setAttribute(ExifInterface.TAG_MAKE, backupValue);
exifInterface.saveAttributes();
exifInterface = new ExifInterface(imageFile.getAbsolutePath());
compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
}
}