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:
- Files that you can access on the filesystem
- Content that you can access via the Storage Access Framework
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
:
- Has the
file
scheme (e.g., a file on the filesystem that you can access) - Is from the Storage Access Framework
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:
- Your app needs to hold the
ACCESS_MEDIA_LOCATION
permission (which isdangerous
and needs to go through runtime permissions) - Your app needs to call
MediaStore.setRequireOriginal()
, supplying theUri
for which you would like the location — this method then returns a decoratedUri
that can be used withopenInputStream()
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 targetSdkVersion
— MediaStore
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.