1
0
Fork 0

Merge pull request #115751 from syntaxerror247/bad-file-descriptor

Android: Fix `Bad file descriptor` in SAF/MediaStore in long term access
This commit is contained in:
Thaddeus Crews 2026-02-02 12:57:25 -06:00
commit e6d4e7d37f
No known key found for this signature in database
GPG Key ID: 8C6E5FEB5FC03CCC
2 changed files with 38 additions and 4 deletions

View File

@ -37,6 +37,7 @@ import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.ParcelFileDescriptor
import android.provider.MediaStore
import android.util.Log
import androidx.annotation.RequiresApi
@ -45,6 +46,7 @@ import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.nio.channels.FileChannel
@ -53,7 +55,7 @@ import java.nio.channels.FileChannel
* under scoped storage via the MediaStore API.
*/
@RequiresApi(Build.VERSION_CODES.Q)
internal class MediaStoreData(context: Context, filePath: String, accessFlag: FileAccessFlags) :
internal class MediaStoreData(context: Context, private val filePath: String, accessFlag: FileAccessFlags) :
DataAccess.FileChannelDataAccess(filePath) {
private data class DataItem(
@ -248,6 +250,7 @@ internal class MediaStoreData(context: Context, filePath: String, accessFlag: Fi
private val id: Long
private val uri: Uri
override val fileChannel: FileChannel
private val parcelFileDescriptor: ParcelFileDescriptor
init {
val contentResolver = context.contentResolver
@ -282,7 +285,7 @@ internal class MediaStoreData(context: Context, filePath: String, accessFlag: Fi
id = dataItem.id
uri = dataItem.uri
val parcelFileDescriptor = contentResolver.openFileDescriptor(uri, accessFlag.getMode())
parcelFileDescriptor = contentResolver.openFileDescriptor(uri, accessFlag.getMode())
?: throw IllegalStateException("Unable to access file descriptor")
fileChannel = if (accessFlag == FileAccessFlags.READ) {
FileInputStream(parcelFileDescriptor.fileDescriptor).channel
@ -294,4 +297,18 @@ internal class MediaStoreData(context: Context, filePath: String, accessFlag: Fi
fileChannel.truncate(0)
}
}
override fun close() {
try {
fileChannel.close()
} catch (e: IOException) {
Log.w(TAG, "Exception when closing file $filePath.", e)
} finally {
try {
parcelFileDescriptor.close()
} catch (e: IOException) {
Log.w(TAG, "Exception when closing ParcelFileDescriptor for $filePath.", e)
}
}
}
}

View File

@ -32,19 +32,21 @@ package org.godotengine.godot.io.file
import android.content.Context
import android.net.Uri
import android.os.ParcelFileDescriptor
import android.provider.DocumentsContract
import android.util.Log
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.nio.channels.FileChannel
/**
* Implementation of [DataAccess] which handles file access via a content URI obtained using the Android
* Storage Access Framework (SAF).
*/
internal class SAFData(context: Context, path: String, accessFlag: FileAccessFlags) :
internal class SAFData(context: Context, private val path: String, accessFlag: FileAccessFlags) :
DataAccess.FileChannelDataAccess(path) {
companion object {
@ -173,9 +175,10 @@ internal class SAFData(context: Context, path: String, accessFlag: FileAccessFla
}
override val fileChannel: FileChannel
val parcelFileDescriptor: ParcelFileDescriptor
init {
val uri = resolvePath(context, path, accessFlag)
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, accessFlag.getMode())
parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, accessFlag.getMode())
?: throw IllegalStateException("Unable to access file descriptor")
fileChannel = if (accessFlag == FileAccessFlags.READ) {
FileInputStream(parcelFileDescriptor.fileDescriptor).channel
@ -187,4 +190,18 @@ internal class SAFData(context: Context, path: String, accessFlag: FileAccessFla
fileChannel.truncate(0)
}
}
override fun close() {
try {
fileChannel.close()
} catch (e: IOException) {
Log.w(TAG, "Exception when closing file $path.", e)
} finally {
try {
parcelFileDescriptor.close()
} catch (e: IOException) {
Log.w(TAG, "Exception when closing ParcelFileDescriptor for $path.", e)
}
}
}
}