Android framework version
net10.0-android
Affected platform version
Microsoft.Android.Ref.36 36.1.53 + Microsoft.Android.Runtime.36.android 36.1.53
Description
Android.Content.Res.AssetFileDescriptor.CreateInputStream() and CreateOutputStream() hide the concrete Java stream types.
The Android Java API returns:
public FileInputStream createInputStream() throws IOException
public FileOutputStream createOutputStream() throws IOException
AOSP source:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/content/res/AssetFileDescriptor.java
The .NET Android binding invokes the correct Java methods/signatures:
createInputStream.()Ljava/io/FileInputStream;
createOutputStream.()Ljava/io/FileOutputStream;
but exposes both methods as:
System.IO.Stream AssetFileDescriptor.CreateInputStream()
System.IO.Stream AssetFileDescriptor.CreateOutputStream()
The returned stream is wrapped through:
Android.Runtime.InputStreamInvoker.FromJniHandle(...)
Android.Runtime.OutputStreamInvoker.FromJniHandle(...)
This loses access to the concrete Java.IO.FileInputStream / Java.IO.FileOutputStream APIs.
This matters for large file copies, especially SAF/content URI copies, because the concrete Java stream types expose Channel. With the current binding, code using the auto-close AssetFileDescriptor.CreateInputStream() / CreateOutputStream() APIs cannot use:
FileInputStream.Channel.TransferTo(..., FileOutputStream.Channel)
Changing the existing return type would probably be breaking, but would it be possible to expose strongly typed alternatives or otherwise make the underlying Java.IO.FileInputStream / Java.IO.FileOutputStream accessible without reflection?
Steps to Reproduce
- Open an
AssetFileDescriptor for a content URI:
using var afd = Application.Context.ContentResolver.OpenAssetFileDescriptor(uri, "w");
- Call
CreateOutputStream():
using var stream = afd.CreateOutputStream();
-
Observe that the returned type is System.IO.Stream, not Java.IO.FileOutputStream, even though the Android API method returns FileOutputStream.
-
Try to use FileOutputStream.Channel for a large file copy:
var output = (Java.IO.FileOutputStream)stream; // not possible
The only practical workaround is to bypass CreateOutputStream():
using var output = new Java.IO.FileOutputStream(afd.FileDescriptor);
but then the caller must explicitly close the AssetFileDescriptor.
Did you find any workaround?
Yes. The workaround is to avoid AssetFileDescriptor.CreateInputStream() / CreateOutputStream() and create Java file streams from the raw file descriptor:
using var afd = Application.Context.ContentResolver.OpenAssetFileDescriptor(uri, "w");
try
{
using var output = new Java.IO.FileOutputStream(afd.FileDescriptor);
// use output.Channel here
}
finally
{
afd.Close();
}
This preserves access to FileOutputStream.Channel, but it bypasses the Android auto-close stream APIs and requires explicit AssetFileDescriptor.Close() handling.
Relevant log output
Android framework version
net10.0-android
Affected platform version
Microsoft.Android.Ref.36 36.1.53 + Microsoft.Android.Runtime.36.android 36.1.53
Description
Android.Content.Res.AssetFileDescriptor.CreateInputStream()andCreateOutputStream()hide the concrete Java stream types.The Android Java API returns:
AOSP source:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/content/res/AssetFileDescriptor.java
The .NET Android binding invokes the correct Java methods/signatures:
but exposes both methods as:
The returned stream is wrapped through:
This loses access to the concrete
Java.IO.FileInputStream/Java.IO.FileOutputStreamAPIs.This matters for large file copies, especially SAF/content URI copies, because the concrete Java stream types expose
Channel. With the current binding, code using the auto-closeAssetFileDescriptor.CreateInputStream()/CreateOutputStream()APIs cannot use:Changing the existing return type would probably be breaking, but would it be possible to expose strongly typed alternatives or otherwise make the underlying
Java.IO.FileInputStream/Java.IO.FileOutputStreamaccessible without reflection?Steps to Reproduce
AssetFileDescriptorfor a content URI:CreateOutputStream():Observe that the returned type is
System.IO.Stream, notJava.IO.FileOutputStream, even though the Android API method returnsFileOutputStream.Try to use
FileOutputStream.Channelfor a large file copy:The only practical workaround is to bypass
CreateOutputStream():but then the caller must explicitly close the
AssetFileDescriptor.Did you find any workaround?
Yes. The workaround is to avoid
AssetFileDescriptor.CreateInputStream()/CreateOutputStream()and create Java file streams from the raw file descriptor:This preserves access to
FileOutputStream.Channel, but it bypasses the Android auto-close stream APIs and requires explicitAssetFileDescriptor.Close()handling.Relevant log output