21

I'm trying to download a file and I'm getting System.UnauthorizedAccessException: Access to the path "/storage/emulated/0/Download/test.pdf" is denied. I have set required permission in Android Manifest file.

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

Download Path:

Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads)

If i use the below path as my download path i can able to download the file. But i cant able to share the PDF file to google drive, drop box or any other System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal)

I am using Xamarin.Forms v2.4.0.282 and Xamarin.Android.Support packages v25.4.0.2.

Note: The code was woking fine when use Xamarin.Forms version 2.3.5.256-pre2 and Xamarin.Android.Support packages v23.3.0 Please suggest your ideas to resolve the issue.

Thanks in Advance.

Melody
  • 1,203
  • 2
  • 17
  • 28
  • What version of Android are you on because anything api 23 and newer requires explicit permission granted by the user to access files – Nick Peppers Oct 24 '17 at 14:43
  • I am facing the issue from android v5.1 to v7.0 – Melody Oct 24 '17 at 14:51
  • 1
    On one of the devices I'd go into settings>apps click on the app and check to see if the storage permission is enabled and retry. If it afterward it works then you'll just need to prompt the user to enable permission – Nick Peppers Oct 24 '17 at 17:19
  • @Nick Thank you. It is working after enabled permission as you said. Now i have added the code to ask runtime permission. – Melody Oct 25 '17 at 08:18
  • The following answer could be helpful https://stackoverflow.com/a/66515458/7149454 – Nick Kovalsky Mar 07 '21 at 10:27

5 Answers5

36

Depending on the version of Android you are using even with the permissions added in the manifest in 6.0 or up the user has to explicitly enable the permission when the app runs and on lower versions permission is asked during install. For example, on startup of the app I created a method to check if it is enabled and request permission if it's not.

private void CheckAppPermissions()
{
    if ((int)Build.VERSION.SdkInt < 23)
    {
        return;
    }
    else
    {
        if (PackageManager.CheckPermission(Manifest.Permission.ReadExternalStorage, PackageName) != Permission.Granted
            && PackageManager.CheckPermission(Manifest.Permission.WriteExternalStorage, PackageName) != Permission.Granted)
        {
            var permissions = new string[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage };
            RequestPermissions(permissions, 1);
        }
     }
}

You can also use the support library to do this, which is simpler and allows you to not have to check the Android version. For more info check out google's documentation.

Nick Peppers
  • 3,161
  • 23
  • 27
23

If targeting API 29+, you will get the error even if you request the permission and user grants it, because they changed how storage works.

The correct solution is to look how it should be done on API 29+ and do it.

But if you are like me, tired of Android making things more complicated every day, just add android:requestLegacyExternalStorage="true" to your manifest <application> tag and you are saved until you start targeting API 30.

David Riha
  • 1,368
  • 14
  • 29
  • How do you do that? – Mashkur Aug 09 '20 at 10:58
  • Hi, I am now targeting API 30. My app was storing log file and taking database backup in location "/storage/emulated/0/SpecialDir/LogFile.txt". Now I am facing access denied issue. Can you please guide me what should i do. – user6159419 Oct 19 '20 at 11:51
  • 1
    you saved my day! Thanks. (Note: I'm using unity and got the access denial issue even after I gave external storage permission. Now it is fixed by adding 'android:requestLegacyExternalStorage="true"' to the application tag in my custom manifest) – Soorya Dec 16 '20 at 18:32
  • It is worth mentioning that this is just a temporary solution and its better to make your app compatible with Scoped Storage: After you update your app to target Android 11 (API level 30), the system ignores the requestLegacyExternalStorage attribute when your app is running on Android 11 devices, so your app must be ready to support scoped storage and to migrate app data for users on those devices. Android Source: https://developer.android.com/training/data-storage/use-cases#opt-out-scoped-storage – SnapADragon Jan 06 '21 at 09:55
  • 1
    @SnapADragon yes I did mention that. "you are saved until you start targeting API 30". – David Riha Jan 06 '21 at 18:08
9

Those of you who are facing this issue after your app is Targeting API29 or higher, please go to this link and check LandLu's Answer:

https://forums.xamarin.com/discussion/171039/saving-files-to-external-storage

Earlier I was accessing Folder path using return Android.OS.Environment.ExternalStorageDirectory.AbsolutePath; But using this line solved my problem: return Android.App.Application.Context.GetExternalFilesDir("").AbsolutePath;

Mashkur
  • 171
  • 1
  • 4
1

You need user's permission on run time even you have mentioned them in your manifest file if you are running Android api level 23 or greater. Check and if user has not yet granted granted READ_EXTERNAL_STORAGE & WRITE_EXTERNAL_STORAGE, use the bellow code;

var permissions = new string[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage };
            RequestPermissions(permissions, 77);

If i use the below path as my download path i can able to download the file. But i cant able to share the PDF file to google drive, drop box or any other System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal)

You are storing files on app's private storage. All files saved to the internal storage are private to your application and other applications ( google drive, drop box or any other ) cannot access them (nor can the user). You can use any public folder for that purpose;

var finalPath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads);
Baqer Naqvi
  • 6,011
  • 3
  • 50
  • 68
0

replace

Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;

with

Android.App.Application.Context.GetExternalFilesDir("").AbsolutePath;
Chris Catignani
  • 5,040
  • 16
  • 42
  • 49
Harrison Wu
  • 354
  • 2
  • 4
  • Hi! Adding a bit of context might improve the quality of the answer a lot imho. You might also find the infor in [here](https://stackoverflow.com/help/how-to-answer) useful for future answers, – Bedir Yilmaz Aug 08 '21 at 06:28