EXIF Metadata Redaction

JPEG images can have EXIF metadata “tags”. For example, one important one is orientation, indicating how the device was being held at the time the picture was taken. This allows image-viewing code to rotate the image as needed to properly orient it for viewing.

“Geotagged” photos represent another set of EXIF tags. A camera app can elect to include location information in photos as tags. This enables a lot of interesting features and services, such as allowing a user to browse a photo gallery via a map instead of only chronologically.

However, geotagged photos represent semi-intentional leaks of location information. For example, if a photo was created five minutes ago and has GPS coordinates, it is reasonable to think that the device is still in the general vicinity. This is less true of a photo created five months ago… but if there are a lot of photos in a similar area, there is a decent chance that the user lives in that area and is taking photos of local events.

As a result, in Android 10, access to this information is much more restricted than it had been.

At least, in theory it is.

Individual Files

According to the documentation, EXIF data is supposed to be redacted when reading in images.

However, that does not seem to be working, at least for:

However, it does work, by default, for a Uri from the MediaStore. For example, consider this function:

fun gimmeTehTags(image: Uri) {
  context.contentResolver.openInputStream(image)?.use { src ->
    val exif = ExifInterface(src)
    val location = exif.latLong
  }
}

We get a valid location for images that have those EXIF tags if the Uri:

By default, we get null if the Uri came from the MediaStore.

However, we still can get the location information, even from the MediaStore. This requires two things, in theory:

  1. Your app needs to hold the ACCESS_MEDIA_LOCATION permission (which is dangerous and needs to go through runtime permissions)
  2. Your app needs to call MediaStore.setRequireOriginal(), supplying the Uri for which you would like the location — this method then returns a decorated Uri that can be used with openInputStream()

Then, if you use openInputStream() for the setRequireOriginal()-supplied Uri, you will get a stream that includes the location EXIF tags.

MediaStore

As part of indexing the images available on external storage, the MediaStore used to examine the EXIF headers and save location data. That could then be accessed by querying the MediaStore for MediaStore.Images.Media.LATITUDE and MediaStore.Images.Media.LONGITUDE values.

However, that is not done on Android 10, and therefore you will be unable to obtain this data. This is not dependent upon targetSdkVersionMediaStore simply does not seem to aggregate this data.

As a result, your only option appears to be to get the Uri for each individual image and use ExifInterface, as shown above. This is far slower than obtaining the data directly from MediaStore, so ideally you are not attempting to get this data in bulk.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.