dotnet / dotnet/android

AssetFileDescriptor.CreateInputStream/CreateOutputStream hide FileInputStream/FileOutputStream channel access

Open
#11,797 0 comments 0 reactions 0 assignees View on GitHub
Area: Mono.Android
Dominant language
C#
Stars
2.1k
Forks
579
Avg merge
1d 20h
Merged PRs (30d)
257

Description

### 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:

```java
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:

```text
createInputStream.()Ljava/io/FileInputStream;
createOutputStream.()Ljava/io/FileOutputStream;
```

but exposes both methods as:

```csharp
System.IO.Stream AssetFileDescriptor.CreateInputStream()
System.IO.Stream AssetFileDescriptor.CreateOutputStream()
```

The returned stream is wrapped through:

```csharp
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:

```csharp
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

1. Open an `AssetFileDescriptor` for a content URI:

```csharp
using var afd = Application.Context.ContentResolver.OpenAssetFileDescriptor(uri, "w");
```

2. Call `CreateOutputStream()`:

```csharp
using var stream = afd.CreateOutputStream();
```

3. Observe that the returned type is `System.IO.Stream`, not `Java.IO.FileOutputStream`, even though the Android API method returns `FileOutputStream`.

4. Try to use `FileOutputStream.Channel` for a large file copy:

```csharp
var output = (Java.IO.FileOutputStream)stream; // not possible
```

The only practical workaround is to bypass `CreateOutputStream()`:

```csharp
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:

```csharp
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

```shell

```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by comparing AssetFileDescriptor.CreateInputStream() and CreateOutputStream() with the AOSP AssetFileDescriptor methods and their Java signatures. Trace the binding wrappers through InputStreamInvoker.FromJniHandle and OutputStreamInvoker.FromJniHandle, then determine an API that exposes FileInputStream/FileOutputStream channel access without changing the existing return types or auto-close behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, csharp
Domain
api, mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.