I am unable to write file on physical SD card. I want to ask user for permission to write to whole SD card. User grants the permission. Now I want to create file in specific directory on SD card (directory already exists). When I try to open output stream I get exception:
Java.Lang.SecurityException: Permission Denial: writing com.android.externalstorage.ExternalStorageProvider uri content://com.android.externalstorage.documents/tree/0FFF170E%3AMyDir/MyFile.MyExt from pid=3563, uid=10082 requires android.permission.MANAGE_DOCUMENTS, or grantUriPermission()
Here is code of Activity:
using System.Linq;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.Net;
using Android.OS;
using Android.OS.Storage;
namespace MyApp
{
[Activity(Label = "MyApp", MainLauncher = true)]
public class MainActivity : Activity
{
private const int AccessRequest = 101;
private TaskCompletionSource<string> AccessIntentTaskCompletionSource { get; set; }
protected override async void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
string uriWithPermission = await AccessIntentAsync();
if (!string.IsNullOrEmpty(uriWithPermission))
{
string targetUri = uriWithPermission + "MyDir/MyFile.MyExt"; // MyDir exists on SD card
// Example of targetUri:
// content://com.android.externalstorage.documents/tree/0FFF-170E%3AMyDir/MyFile.MyExt
WriteFileToUri(targetUri);
}
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == AccessRequest)
{
string path = null;
if (resultCode == Result.Ok)
{
path = data.DataString;
Android.Net.Uri uriPath = data.Data;
ContentResolver.TakePersistableUriPermission(uriPath, ActivityFlags.GrantReadUriPermission | ActivityFlags.GrantWriteUriPermission);
}
this.AccessIntentTaskCompletionSource?.SetResult(path);
}
}
private async Task<string> AccessIntentAsync()
{
this.AccessIntentTaskCompletionSource = new TaskCompletionSource<string>();
StorageManager sm = GetSystemService(StorageService) as StorageManager;
if (sm != null)
{
StorageVolume volume = sm.StorageVolumes.FirstOrDefault(sv => !sv.IsPrimary); // Assume only one non-primary storage volume.
if (volume != null)
{
Intent intent = volume.CreateAccessIntent(null); // request access to the entire volume
StartActivityForResult(intent, AccessRequest);
return await this.AccessIntentTaskCompletionSource.Task;
}
}
return null;
}
private void WriteFileToUri(string targetUri)
{
Android.Net.Uri outputUri = Uri.Parse(targetUri);
byte[] source = System.Text.Encoding.UTF8.GetBytes("MyFileContent");
using (System.IO.Stream outputStream = this.ContentResolver.OpenOutputStream(outputUri)) // throws Exception
{
outputStream.Write(source, 0, source.Length);
outputStream.Close();
}
}
}
}
Here is Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="MyApp.MyApp" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="25" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:allowBackup="true" android:label="@string/app_name"></application>
</manifest>
I have tested it on emulator and real device. Both with Android 7.1.
SD card is mounted in "For transferring photos and media" mode.
EDIT: Ask for permission android.permission.WRITE_EXTERNAL_STORAGE at run time does not work. It works only if I want to write to PRIMARY external storage.