Androidx Exifinterface crashes when try to saveAttributes. Write failed: EBADF (Bad file descriptor)

2.1k Views Asked by At

When trying to save any JPEG picture using saveAttributes() from androidx Exifinterface, my program crashes with the error "write failed: EBADF (Bad file descriptor)"

I can replicate the error starting fresh from a new project. I'm using Android Studio: new project->Empty Activity. I'm using the emulator to test.

Below is the full code with the only changes I made to the fresh Empty Activity template.

Using androidx Exifinterface, this code is correctly able to get Exif attributes. However, saveAttributes() crashes every time:

  • saveAttributes() crashes regardless if I first setAttribute() or not.
  • Which picture used does not matter. It crashes for every picture I've tried.
  • I'm using JPEG pictures. I have not tested with other mime types.
  • saveAttributes() throws: "Failed to save new file. Original file is stored in..."

I want to set Exif attributes of the picture and save into the original image file. What is the correct way?

[This post should be tagged androidx-interface. But that tag does not exist, and I don't have the reputation to add a tag. So I used tag android-interface, which does exist].


build.gradle (:app)

implementation "androidx.exifinterface:exifinterface:1.3.2"

AndroidManifest.xml:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />

MainActivity.kt:

    package com.example.test_exif_save
    
    import android.content.ContentUris
    import android.net.Uri
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.provider.MediaStore
    import android.util.Log
    import androidx.core.app.ActivityCompat
    import androidx.exifinterface.media.ExifInterface
    
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            
            // Simple method for PERMISSIONS just for quick testing
            val permissions = arrayOf(
                    android.Manifest.permission.READ_EXTERNAL_STORAGE,
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    android.Manifest.permission.ACCESS_MEDIA_LOCATION,
            )
            ActivityCompat.requestPermissions(this, permissions, 0)
    
            val contentUri: Uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    
            // id of arbitrary picture saved in Pictures/
            // The picture chosen does not matter. The same crash occurs for every picture.
            val picture_id: Long = 32
    
            val uri = ContentUris.withAppendedId(contentUri, picture_id)
            
            contentResolver.openInputStream(uri)?.use { stream ->
                val exifData = ExifInterface(stream)
    
                // Check that ExifInterface getAttribute works correctly
                val attr_model = exifData.getAttribute(ExifInterface.TAG_MODEL)
                val attr_datetime_original = exifData.getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL)
                val attr_image_width = exifData.getAttribute(ExifInterface.TAG_IMAGE_WIDTH)
                Log.i("ExifData", "Model: $attr_model")
                Log.i("ExifData", "Datetime original: $attr_datetime_original")
                Log.i("ExifData", "Width: $attr_image_width")
                
                // Try to save
                // Causes fatal exception. Error: ErrnoException: write failed: EBADF (Bad file descriptor)
                exifData.saveAttributes()
            }
        }
    }

The stack trace:

com.example.test_exif_save E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.test_exif_save, PID: 12188
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.test_exif_save/com.example.test_exif_save.MainActivity}: java.io.IOException: Failed to save new file. Original file is stored in /data/user/0/com.example.test_exif_save/cache/temp1652156935871844716tmp
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
     Caused by: java.io.IOException: Failed to save new file. Original file is stored in /data/user/0/com.example.test_exif_save/cache/temp1652156935871844716tmp
        at androidx.exifinterface.media.ExifInterface.saveAttributes(ExifInterface.java:4783)
        at com.example.test_exif_save.MainActivity.onCreate(MainActivity.kt:44)
        at android.app.Activity.performCreate(Activity.java:8000)
        at android.app.Activity.performCreate(Activity.java:7984)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.app.ActivityThread.main(ActivityThread.java:7656) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 
     Caused by: java.io.IOException: write failed: EBADF (Bad file descriptor)
        at libcore.io.IoBridge.write(IoBridge.java:540)
        at java.io.FileOutputStream.write(FileOutputStream.java:398)
        at androidx.exifinterface.media.ExifInterface.copy(ExifInterface.java:8087)
        at androidx.exifinterface.media.ExifInterface.saveAttributes(ExifInterface.java:4779)
        at com.example.test_exif_save.MainActivity.onCreate(MainActivity.kt:44) 
        at android.app.Activity.performCreate(Activity.java:8000) 
        at android.app.Activity.performCreate(Activity.java:7984) 
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309) 
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422) 
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.app.ActivityThread.main(ActivityThread.java:7656) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 
     Caused by: android.system.ErrnoException: write failed: EBADF (Bad file descriptor)
        at libcore.io.Linux.writeBytes(Native Method)
        at libcore.io.Linux.write(Linux.java:293)
        at libcore.io.ForwardingOs.write(ForwardingOs.java:240)
        at libcore.io.BlockGuardOs.write(BlockGuardOs.java:418)
        at libcore.io.ForwardingOs.write(ForwardingOs.java:240)
        at libcore.io.IoBridge.write(IoBridge.java:535)
        at java.io.FileOutputStream.write(FileOutputStream.java:398) 
        at androidx.exifinterface.media.ExifInterface.copy(ExifInterface.java:8087) 
        at androidx.exifinterface.media.ExifInterface.saveAttributes(ExifInterface.java:4779) 
        at com.example.test_exif_save.MainActivity.onCreate(MainActivity.kt:44) 
        at android.app.Activity.performCreate(Activity.java:8000) 
        at android.app.Activity.performCreate(Activity.java:7984) 
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309) 
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422) 
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.app.ActivityThread.main(ActivityThread.java:7656) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 
1

There are 1 best solutions below

2
On

I had the same problem. Fixed it by creating ExifInterface from a FileDescriptor instead of an InputStream. Replace

val exifData = ExifInterface(stream)

With

val exifData = ExifInterface(context.contentResolver.openFileDescriptor(new, "rw", null)!!.fileDescriptor)

(FileDescriptor nullability handling to your taste).