Skip to content
This repository has been archived by the owner on May 15, 2024. It is now read-only.

[Bug] Storage permissions Android 13 api 33 #2041

Open
ChrisDox opened this issue Sep 7, 2022 · 103 comments
Open

[Bug] Storage permissions Android 13 api 33 #2041

ChrisDox opened this issue Sep 7, 2022 · 103 comments
Labels
bug Something isn't working

Comments

@ChrisDox
Copy link

ChrisDox commented Sep 7, 2022

Description

I have a strange thing when My TARGET ANDROID VERSION is Android 13.0 (API Level 33)

When i use CrossMedia plugin, i want to ask manual permissions to external storage
When i do

await Permissions.RequestAsync<Permissions.StorageRead>(); OR
await Permissions.RequestAsync<Permissions.StorageWrite>();

--> NO displayAlert comes to ask permission and the result is always "Denied"

for other asking like Camera, the displayAlert comes normally and it's ok.

To test i use xamarin essential mediaPicker
when i do
var photo = await MediaPicker.CapturePhotoAsync();

First i have normal alert with "Allow ... to take pictures an record video"
After that i have a PermissionException

StorageWrite permission was not granted: Denied
at Xamarin.Essentials.Permissions.EnsureGrantedAsync[TPermission] () [0x00066] in D:\a\_work\1\s\Xamarin.Essentials\Permissions\Permissions.shared.cs:29 
  at Xamarin.Essentials.MediaPicker.PlatformCaptureAsync (Xamarin.Essentials.MediaPickerOptions options, System.Boolean photo) [0x0007e] in D:\a\_work\1\s\Xamarin.Essentials\MediaPicker\MediaPicker.android.cs:59 

....
And NOT storage permission asking alert

WHEN My TARGET ANDROID VERSION is Android 12.1 (API Level 32) ALL WORKS FINE

In my manifest i have

	<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.CAMERA" />
        .....
        <queries>
            <intent>
              <action android:name="android.media.action.IMAGE_CAPTURE" />
            </intent>
         </queries>

Expected Behavior

Alert with Storage authorization asking

Actual Behavior

PermissionException
StorageWrite permission was not granted: Denied

Basic Information

  • Version with issue: xamarin essential 1.7.3

  • Last known good version:

  • IDE: visual studio mac 2022 17.3.3 (build 10)

  • Platform Target Frameworks:

    • Android: 13 api 33
  • Affected Devices: emulator pixel 5 on api 33 , real pixel 6 on api 33

@ChrisDox ChrisDox added the bug Something isn't working label Sep 7, 2022
@RemcoDEV
Copy link

This is the case for more type of permissions when targeting API 33.

@ataparia
Copy link

Having similar issue on our end. Can someone from the Xamarin team look in this please?

@plebnz
Copy link

plebnz commented Sep 26, 2022

Sounds like this could be sorted at same time as #2037

@dartur123
Copy link

Also still experiencing this issue.

@andycnguyen
Copy link

We're encountering this issue as well

@lazmeister
Copy link

us as well

@thomasgalliker
Copy link

Same here. You can use following code to reproduce the problem.

public static async Task<bool> HasStorageReadPermissionsAsync()
{
    var permission = await Permissions.CheckStatusAsync<Permissions.StorageRead>();
    // permission is "Denied" if we check for the very first time
    if (permission != PermissionStatus.Granted)
    {
        // The following call should display the permission dialog;
        // It works perfectly fine on <= API 28 but when I run it on API 33, it just returns "Denied"
        // similar to what ChrisDoc documented above.
        var requestStatus = await Permissions.RequestAsync<Permissions.StorageRead>();
        if (requestStatus == PermissionStatus.Granted)
        {
            return true;
        }
    }
    else
    {
        return true;
    }

    return false;
}

@jamesnwarner
Copy link

We're also encountering this issue.
It appears to make the camera completely unusable on any device using Android 13 (API 33). Has anyone got a workaround for using the camera?

@okdicom
Copy link

okdicom commented Oct 21, 2022 via email

@jamesnwarner
Copy link

To provide further context to the Xamarin team, since Android 11 (API level 30), the WRITE_EXTERNAL_STORAGE permission does not have any effect on the app's access to storage - https://developer.android.com/training/data-storage#permissions

It appears that on devices using Android 13, the popup dialog to request WRITE_EXTERNAL_STORAGE permission will no longer display. Regardless of whether the code requests it or not.

Therefore it is impossible for users using this version of Android to accept the permission. The fact that the MediaPicker.CapturePhotoAsync() ensures that this permission has been granted means that this method just doesn't work on Android 13.

The Expected Behaviour in the original bug should be that the camera just works, without requiring the WRITE_EXTERNAL_STORAGE permission. That part should be handled in the code if the developer chooses to write the file to storage.

@brayden-marshall
Copy link

We're seeing this issue as well.

I was able to use the workaround that okdicom suggested, but that's not ideal since it involves a separate package, and a beta version at that.

@Goksly
Copy link

Goksly commented Nov 4, 2022

Adding this to the Android manifest may help:

<queries>
  <intent>
    <action android:name="android.media.action.IMAGE_CAPTURE" />
  </intent>
</queries>

It's already in the person's manifest - so no it won't.

@tylershelton810
Copy link

We are also experiencing issues with Android 13 permissions. Will try placing them in the manifest as some have suggested.

@DS2107
Copy link

DS2107 commented Nov 25, 2022

I have the same problem, I have given all the permissions, but the permission to use the files does not appear and I cannot request access to the files from the user.

@Sebastian1989101
Copy link

Same issue here... a shame that something like this is not fixed asap...

@it11111111
Copy link

We are also having the same issue here

@dimonovdd
Copy link
Contributor

Hi, Can you try that plugin?

If the problem is not solved, create new issue in that repository. I'll try to fix it.

Don't forget to provide a sample project with this error and the reproduction steps.

@aismaniotto
Copy link

+1

@softsan
Copy link

softsan commented Dec 14, 2022

Any updates on this or work around?

@irongut
Copy link

irongut commented Dec 14, 2022

The Android camera will not work without asking for media permission. On Android 13 when targetting Android 13 the media permission is not asked for making it impossible to use the camera.

Google will require Android 13 as a minumum target version on 1st November 2023.

So unless this issue is fixed, Xamarin.Android and Xamarin.Forms will be End Of Life on 1st November 2023 no matter what date Microsoft claim.

@grabnerM
Copy link

grabnerM commented Dec 21, 2022

I am not sure if this is necessarily a xamarin problem. We are facing the same problem. Before we switched to the xamarin.essentials permission request, we used to open the app settings. There the user had to grant the permission by himself.
To come back to the problem, on my OnePlus 8T I cant grant the permissions in the settings. Basically there i cant find the storage permission.

EDIT:
We are facing this issue in our file downloader. Up to Android 12 we could simply asked the user for permission to download files. (Therefor we added "READ_EXTERNAL_STORAGE" and "WRITE_EXTERNAL_STORAGE" to our AndroidManifest.)

After searching for a solution for this problem for a whole day, i found that Google wanted to increase the security of the apps for Android 13+. So now the user need to give you the permissions for images, videos and audio files seperatly. Unfortunatly Xamarin.Essentials has not gotten updated yet.

@daesang
Copy link

daesang commented Dec 27, 2022

public static async Task AskForRequiredStoragePermission()
{
try
{
if (DeviceInfo.Platform == DevicePlatform.Android && DeviceInfo.Version.Major >= 13)
{
return true;
}

            var status = await Permissions.CheckStatusAsync<Permissions.StorageRead>();
            if (status == PermissionStatus.Granted)
            {
                return true;
            }

            if (status == PermissionStatus.Denied && DeviceInfo.Platform == DevicePlatform.iOS)
            {
                // Prompt the user to turn on in settings
                // On iOS once a permission has been denied it may not be requested again from the application
                return false;
            }

            await Permissions.RequestAsync<Permissions.StorageRead>();
            status = await Permissions.CheckStatusAsync<Permissions.StorageRead>();
            if (status == PermissionStatus.Granted)
                return true;
        }
        catch (Exception ex)
        {
            //Something went wrong
        }
        return false;
    }

@eamonnalphin
Copy link

I'm still having this issue; asking for read & write permissions doesn't do anything. No popup, and the permission isn't even listed in the emulator permissions under the app.

@Its-AliRaza03
Copy link

It's working fine after updating to beta version 6.0.1.

@AnthonyWeirbach
Copy link

image

https://developer.android.com/about/versions/13/behavior-changes-13#granular-media-permissions

I guess doing something like this? But I am not sure if this would work for write scenarios and I couldn't find any permissions for write for these new permissions.

@zerokewl88
Copy link

zerokewl88 commented Sep 15, 2023

Well, there seems to be a answer here at the bottom of this thread, and also some feedback from a member of Xamarin team, and their 'response' which was "As mentioned in #2063 (comment) also about permissions, Xamarin.Essentials is in maintanance only and we're not looking to add new functionality unless we absolutely have to. New development will happen in .NET MAUI."

REF: #2065

@zerokewl88
Copy link

zerokewl88 commented Sep 15, 2023

OK - it's a simple fix to get around this, and it's working ok for me on API33.

You only have to update the following method, and then add the manifest entries - and your camera operations are working again on API 33.

  1. Android Main Activity updat the following Method to this.. (This tricks Android to accept a permission on a platform where it's no longer used - lol )
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any()))
            {
                var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE");
                var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");

                if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
                if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
            }

            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
  1. Manifest File
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.CAMERA" />
	<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
	<!--<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />-->
	<uses-feature android:name="android.hardware.location" android:required="false" />
	<uses-feature android:name="android.hardware.location.gps" android:required="false" />
	<uses-feature android:name="android.hardware.location.network" android:required="false" />
  
  <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
  <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>

Credit to @WanftMoon on this post (#2065) for pointing me in the right direction. Camera operations restored in API 33 - thank you.

@leffemedkniven
Copy link

OK - it's a simple fix to get around this, and it's working ok for me on API33.

You only have to update the following method, and then add the manifest entries - and your camera operations are working again on API 33.

1. Android Main Activity updat the following Method to this.. (This tricks Android to accept a permission on a platform where it's no longer used - lol )
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any()))
            {
                var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE");
                var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");

                if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
                if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
            }

            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
2. Manifest File
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.CAMERA" />
	<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
	<!--<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />-->
	<uses-feature android:name="android.hardware.location" android:required="false" />
	<uses-feature android:name="android.hardware.location.gps" android:required="false" />
	<uses-feature android:name="android.hardware.location.network" android:required="false" />
  
  <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
  <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>

Credit to @WanftMoon on this post (#2065) for pointing me in the right direction. Camera operations restored in API 33 - thank you.

Did you get this working in a release-version? It works for me, but only in debug. As if somethings wrong with the manifest-file.

@zerokewl88
Copy link

zerokewl88 commented Sep 15, 2023 via email

@zerokewl88
Copy link

@leffemedkniven Hi there - i've just reinstalled a release version, and confirmed this method above works ok.
Did you accept / create the permissions on startup ?

With what i have posted above, on startup, i have a check for permissions that requests the following.

try
                        {
                            cameraPermission = await CrossPermissions.Current.CheckPermissionStatusAsync<CameraPermission>();
                            if (cameraPermission != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
                            {
                                var status = await CrossPermissions.Current.RequestPermissionAsync<CameraPermission>();
                            }
                        }
                        catch (Exception ex)
                        {
                            Crashes.TrackError(ex);
                        }

                        

                        try
                        {
                            photosPermission = await CrossPermissions.Current.CheckPermissionStatusAsync<PhotosPermission>();
                            if (photosPermission != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
                            {
                                var status = await CrossPermissions.Current.RequestPermissionAsync<PhotosPermission>();
                            }
                        }
                        catch (Exception ex)
                        {
                            Crashes.TrackError(ex);
                        }

@leffemedkniven
Copy link

cameraPermission = await CrossPermissions.Current.CheckPermissionStatusAsync();
if (cameraPermission != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
{
var status = await CrossPermissions.Current.RequestPermissionAsync();
}

When adding this to the PageModel it doesn't pop-up at all, only in debug. Trying to add it to MainActivity.OnCreate doesn't work because it's not async.

Tried this in MainActivity aswell:

if (ActivityCompat.CheckSelfPermission(Xamarin.Essentials.Platform.CurrentActivity, Android.Manifest.Permission.ReadMediaImages) != Permission.Granted) { ActivityCompat.RequestPermissions(Xamarin.Essentials.Platform.CurrentActivity, new string[] { Android.Manifest.Permission.ReadMediaImages }, 100); }

It pops up in release, I allow it, but it doesn't work when trying to add pictures to external storage.

@zerokewl88
Copy link

I actually don't request permission for ReadMediaImages explicity during runtime, this is handled already in my manifest file without having to request it.

The permissions above that i'm requesting i've already included in the above post.

@HardikMadhwani
Copy link

I have had this same problem and I fixed it by adding the below code in the manifest file for Android.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_VIDEO"/>

does this mean that we don't need permission to write in android 13 onwards? we can directly write if required? if yes than how is this increasing security?

@willisra83
Copy link

willisra83 commented Sep 29, 2023

I actually don't request permission for ReadMediaImages explicity during runtime, this is handled already in my manifest file without having to request it.

The permissions above that i'm requesting i've already included in the above post.

So in the Android manifest, did you end up commenting out the old permissions (READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE)? And so this means you no longer need to check CrossPermissions.Current.CheckPermissionStatusAsync<StoragePermission>() on Android?

And you still need the code below also?

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
        {
            if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any()))
            {
                var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE");
                var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");

                if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
                if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
            }

            Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }

Do you need any other changes to fix it? And know if this would still work with a min SDK of <33 specified? Or did you just specify both min SDK and target SDK as 33?

@luisrr1590
Copy link

I can confirm that this works for Android 13:

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any()))
{
var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE");
var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");

            if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
            if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
        }

        Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }

but, I cannot remove the old permissions from the Manifest nor set android:maxSdkVersion= because the plugin will fail when calling CheckStatusAsync.

@WanftMoon
Copy link

WanftMoon commented Oct 2, 2023

but, I cannot remove the old permissions from the Manifest nor set android:maxSdkVersion= because the plugin will fail when calling CheckStatusAsync.

Did you extend the permissions?

public interface IReadImagesVideoPermission
{
    Task<PermissionStatus> CheckStatusAsync();
    Task<PermissionStatus> RequestAsync();
}
public class ReadImagesVideoPermission : Xamarin.Essentials.Permissions.BasePlatformPermission, IReadImagesVideoPermission   
{
    public override (string androidPermission, bool isRuntime)[] RequiredPermissions => new List<(string androidPermission, bool isRuntime)> {
        ("android.permission.READ_MEDIA_IMAGES", true),
        ("android.permission.READ_MEDIA_VIDEO", true)
    }.ToArray();
}
//this is a check for android 13+
if (DeviceInfo.Platform.Equals(DevicePlatform.Android) && DeviceInfo.Version.Major >= 13)
{
    var readImagesVideoPermission = DependencyService.Get<IReadImagesVideoPermission>();

    var permission = await readImagesVideoPermission.CheckStatusAsync();

    if (permission != PermissionStatus.Granted)
    {
        // Prompt the user with additional information as to why the permission is needed
        if (rationale != null) await rationale();

        permission = await readImagesVideoPermission.RequestAsync();
    }

    return permission;
}

@craigwi
Copy link

craigwi commented Oct 2, 2023

In addition to the manifest changes, I found it simpler to create a subclass of StorageRead and put the sdk check there:

        public class ReadMedia : Permissions.StorageRead
        {
#if __ANDROID__
            public override (string androidPermission, bool isRuntime)[] RequiredPermissions
            {
                get
                {
                    // after sdk 33 (Android 13), this requires read two media values
                    if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Tiramisu)
                    {
                        return new (string, bool)[] { ( Android.Manifest.Permission.ReadMediaImages, true), ( Android.Manifest.Permission.ReadMediaVideo, true) };
                    }

                    // otherwise, just use the original single permission read external storage
                    return base.RequiredPermissions;
                }
            }
#endif
        }

Then use ReadMedia permission class anywhere I would use StorageRead.

@willisra83
Copy link

but, I cannot remove the old permissions from the Manifest nor set android:maxSdkVersion= because the plugin will fail when calling CheckStatusAsync.

Did you extend the permissions?

public interface IReadImagesVideoPermission
{
    Task<PermissionStatus> CheckStatusAsync();
    Task<PermissionStatus> RequestAsync();
}
public class ReadImagesVideoPermission : Xamarin.Essentials.Permissions.BasePlatformPermission, IReadImagesVideoPermission   
{
    public override (string androidPermission, bool isRuntime)[] RequiredPermissions => new List<(string androidPermission, bool isRuntime)> {
        ("android.permission.READ_MEDIA_IMAGES", true),
        ("android.permission.READ_MEDIA_VIDEO", true)
    }.ToArray();
}
//this is a check for android 13+
if (DeviceInfo.Platform.Equals(DevicePlatform.Android) && DeviceInfo.Version.Major >= 13)
{
    var readImagesVideoPermission = DependencyService.Get<IReadImagesVideoPermission>();

    var permission = await readImagesVideoPermission.CheckStatusAsync();

    if (permission != PermissionStatus.Granted)
    {
        // Prompt the user with additional information as to why the permission is needed
        if (rationale != null) await rationale();

        permission = await readImagesVideoPermission.RequestAsync();
    }

    return permission;
}

Know when this base permission class functionality would/should be needed? Because with just simply using the code below, I'm able to upload photos on Android 13, 12, and 10 just fine. So it seems the above code isn't needed (unless I'm missing a scenario)...

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
        {
            if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any()))
            {
                var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE");
                var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");

                if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
                if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
            }

            Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }

My manifest

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

@maxib0n
Copy link

maxib0n commented Oct 18, 2023

This worked for me

public class AllFileSystemPermission : Permissions.BasePermission
    {
        public override Task<PermissionStatus> CheckStatusAsync()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                var readStatus = Permissions.CheckStatusAsync<Permissions.StorageRead>().Result;
                var writeStatus = Permissions.CheckStatusAsync<Permissions.StorageWrite>().Result;

                if (readStatus == PermissionStatus.Granted && writeStatus == PermissionStatus.Granted)
                    return Task.FromResult(PermissionStatus.Granted);

                if (readStatus == PermissionStatus.Denied || writeStatus == PermissionStatus.Denied)
                    return Task.FromResult(PermissionStatus.Denied);

                return Task.FromResult(PermissionStatus.Unknown);
            }
            else
            {
                var manageStatus = Platform.AppContext.CheckSelfPermission(Android.Manifest.Permission.ManageExternalStorage);
                return Task.FromResult(manageStatus == Permission.Granted ? PermissionStatus.Granted : PermissionStatus.Denied);
            }
        }

        public override async Task<PermissionStatus> RequestAsync()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                var readStatus = await Permissions.RequestAsync<Permissions.StorageRead>();
                var writeStatus = await Permissions.RequestAsync<Permissions.StorageWrite>();

                if (readStatus == PermissionStatus.Granted && writeStatus == PermissionStatus.Granted)
                    return PermissionStatus.Granted;

                if (readStatus == PermissionStatus.Denied || writeStatus == PermissionStatus.Denied)
                    return PermissionStatus.Denied;

                return PermissionStatus.Unknown;
            }
            else
            {
                if (Platform.AppContext.CheckSelfPermission(Android.Manifest.Permission.ManageExternalStorage) == Permission.Granted)
                    return PermissionStatus.Granted;

                var intent = new Intent(Android.Provider.Settings.ActionManageAppAllFilesAccessPermission);
                intent.AddCategory(Android.Content.Intent.CategoryDefault);
                intent.SetData(Android.Net.Uri.Parse($"package:{Platform.AppContext.PackageName}"));
                Platform.CurrentActivity.StartActivity(intent);

                return await CheckStatusAsync();
            }
        }
        public override void EnsureDeclared()
        {
            var packageName = Platform.AppContext.PackageName;
            var info = Platform.AppContext.PackageManager.GetPackageInfo(packageName, PackageInfoFlags.Permissions);

            var declaredPermissions = info?.RequestedPermissions?.ToList() ?? new List<string>();

            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                if (!declaredPermissions.Contains(Android.Manifest.Permission.ReadExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.ReadExternalStorage} in AndroidManifest.xml");
                if (!declaredPermissions.Contains(Android.Manifest.Permission.WriteExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.WriteExternalStorage} in AndroidManifest.xml");
            }
            else
            {
                if (!declaredPermissions.Contains(Android.Manifest.Permission.ManageExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.ManageExternalStorage} in AndroidManifest.xml");
            }
        }

        public override bool ShouldShowRationale()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                return Platform.CurrentActivity.ShouldShowRequestPermissionRationale(Android.Manifest.Permission.ReadExternalStorage)
                       || Platform.CurrentActivity.ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
            }
            else
            {
                return false;
            }
        }
        
    }

@MrRob02
Copy link

MrRob02 commented Oct 25, 2023

This worked for me

public class AllFileSystemPermission : Permissions.BasePermission
    {
        public override Task<PermissionStatus> CheckStatusAsync()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                var readStatus = Permissions.CheckStatusAsync<Permissions.StorageRead>().Result;
                var writeStatus = Permissions.CheckStatusAsync<Permissions.StorageWrite>().Result;

                if (readStatus == PermissionStatus.Granted && writeStatus == PermissionStatus.Granted)
                    return Task.FromResult(PermissionStatus.Granted);

                if (readStatus == PermissionStatus.Denied || writeStatus == PermissionStatus.Denied)
                    return Task.FromResult(PermissionStatus.Denied);

                return Task.FromResult(PermissionStatus.Unknown);
            }
            else
            {
                var manageStatus = Platform.AppContext.CheckSelfPermission(Android.Manifest.Permission.ManageExternalStorage);
                return Task.FromResult(manageStatus == Permission.Granted ? PermissionStatus.Granted : PermissionStatus.Denied);
            }
        }

        public override async Task<PermissionStatus> RequestAsync()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                var readStatus = await Permissions.RequestAsync<Permissions.StorageRead>();
                var writeStatus = await Permissions.RequestAsync<Permissions.StorageWrite>();

                if (readStatus == PermissionStatus.Granted && writeStatus == PermissionStatus.Granted)
                    return PermissionStatus.Granted;

                if (readStatus == PermissionStatus.Denied || writeStatus == PermissionStatus.Denied)
                    return PermissionStatus.Denied;

                return PermissionStatus.Unknown;
            }
            else
            {
                if (Platform.AppContext.CheckSelfPermission(Android.Manifest.Permission.ManageExternalStorage) == Permission.Granted)
                    return PermissionStatus.Granted;

                var intent = new Intent(Android.Provider.Settings.ActionManageAppAllFilesAccessPermission);
                intent.AddCategory(Android.Content.Intent.CategoryDefault);
                intent.SetData(Android.Net.Uri.Parse($"package:{Platform.AppContext.PackageName}"));
                Platform.CurrentActivity.StartActivity(intent);

                return await CheckStatusAsync();
            }
        }
        public override void EnsureDeclared()
        {
            var packageName = Platform.AppContext.PackageName;
            var info = Platform.AppContext.PackageManager.GetPackageInfo(packageName, PackageInfoFlags.Permissions);

            var declaredPermissions = info?.RequestedPermissions?.ToList() ?? new List<string>();

            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                if (!declaredPermissions.Contains(Android.Manifest.Permission.ReadExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.ReadExternalStorage} in AndroidManifest.xml");
                if (!declaredPermissions.Contains(Android.Manifest.Permission.WriteExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.WriteExternalStorage} in AndroidManifest.xml");
            }
            else
            {
                if (!declaredPermissions.Contains(Android.Manifest.Permission.ManageExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.ManageExternalStorage} in AndroidManifest.xml");
            }
        }

        public override bool ShouldShowRationale()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                return Platform.CurrentActivity.ShouldShowRequestPermissionRationale(Android.Manifest.Permission.ReadExternalStorage)
                       || Platform.CurrentActivity.ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
            }
            else
            {
                return false;
            }
        }
        
    }

Which is the Platform library?

@maxib0n
Copy link

maxib0n commented Nov 2, 2023

This worked for me

public class AllFileSystemPermission : Permissions.BasePermission
    {
        public override Task<PermissionStatus> CheckStatusAsync()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                var readStatus = Permissions.CheckStatusAsync<Permissions.StorageRead>().Result;
                var writeStatus = Permissions.CheckStatusAsync<Permissions.StorageWrite>().Result;

                if (readStatus == PermissionStatus.Granted && writeStatus == PermissionStatus.Granted)
                    return Task.FromResult(PermissionStatus.Granted);

                if (readStatus == PermissionStatus.Denied || writeStatus == PermissionStatus.Denied)
                    return Task.FromResult(PermissionStatus.Denied);

                return Task.FromResult(PermissionStatus.Unknown);
            }
            else
            {
                var manageStatus = Platform.AppContext.CheckSelfPermission(Android.Manifest.Permission.ManageExternalStorage);
                return Task.FromResult(manageStatus == Permission.Granted ? PermissionStatus.Granted : PermissionStatus.Denied);
            }
        }

        public override async Task<PermissionStatus> RequestAsync()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                var readStatus = await Permissions.RequestAsync<Permissions.StorageRead>();
                var writeStatus = await Permissions.RequestAsync<Permissions.StorageWrite>();

                if (readStatus == PermissionStatus.Granted && writeStatus == PermissionStatus.Granted)
                    return PermissionStatus.Granted;

                if (readStatus == PermissionStatus.Denied || writeStatus == PermissionStatus.Denied)
                    return PermissionStatus.Denied;

                return PermissionStatus.Unknown;
            }
            else
            {
                if (Platform.AppContext.CheckSelfPermission(Android.Manifest.Permission.ManageExternalStorage) == Permission.Granted)
                    return PermissionStatus.Granted;

                var intent = new Intent(Android.Provider.Settings.ActionManageAppAllFilesAccessPermission);
                intent.AddCategory(Android.Content.Intent.CategoryDefault);
                intent.SetData(Android.Net.Uri.Parse($"package:{Platform.AppContext.PackageName}"));
                Platform.CurrentActivity.StartActivity(intent);

                return await CheckStatusAsync();
            }
        }
        public override void EnsureDeclared()
        {
            var packageName = Platform.AppContext.PackageName;
            var info = Platform.AppContext.PackageManager.GetPackageInfo(packageName, PackageInfoFlags.Permissions);

            var declaredPermissions = info?.RequestedPermissions?.ToList() ?? new List<string>();

            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                if (!declaredPermissions.Contains(Android.Manifest.Permission.ReadExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.ReadExternalStorage} in AndroidManifest.xml");
                if (!declaredPermissions.Contains(Android.Manifest.Permission.WriteExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.WriteExternalStorage} in AndroidManifest.xml");
            }
            else
            {
                if (!declaredPermissions.Contains(Android.Manifest.Permission.ManageExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.ManageExternalStorage} in AndroidManifest.xml");
            }
        }

        public override bool ShouldShowRationale()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                return Platform.CurrentActivity.ShouldShowRequestPermissionRationale(Android.Manifest.Permission.ReadExternalStorage)
                       || Platform.CurrentActivity.ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
            }
            else
            {
                return false;
            }
        }
        
    }

Which is the Platform library?

the one from Xamarin.Essentials

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Android.OS;
using Android.Content.PM;
using Android.App;
using Android.Content;

@Rishi2611
Copy link

Rishi2611 commented Nov 21, 2023

Does any one found solution for this, I am getting same error?

@WanftMoon
Copy link

Does any one found solution for this, I am getting same error?

Yeah, if you look at the previous messages, the solution is more or less:

  1. add the new permissions in your manifest
  2. extend the base permissions and invoke them
  3. look at the workarounds, mostly this one (you might not need it, but i needed in my case):
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
            if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any()))
            {
                var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE");
                var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");

                if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
                if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
            }

            Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
} 

@juanes030
Copy link

Can someone help me solve the storage permission error, create a test app and I need to know what the code would be to request permission in Android 13. My mainactivity is

using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.OS;

namespace MvvmPrueba.Droid
{
[Activity(Label = "MvvmPrueba", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);

        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
        LoadApplication(new App());
    }
    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
    {
        Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

}

@zerokewl88
Copy link

Scroll up.. there are examples there. look @ #2041 (comment)

@juanes030
Copy link

already add the following permissions


What would be the permission to write to the storage?

1 similar comment
@juanes030
Copy link

already add the following permissions


What would be the permission to write to the storage?

@WanftMoon
Copy link

already add the following permissions What would be the permission to write to the storage?

@juanes030
From what I understood, from android 10+ you don't need permissions to access your own files (https://developer.android.com/training/data-storage/shared/media#storage-permission)
But you need permissions to read files created by other apps.
(https://developer.android.com/about/versions/13/behavior-changes-13#granular-media-permissions(url))

with that said, i would recommend that you use at least these

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_VIDEO"/>

@juanes030
Copy link

I need to archivate from xamarin the permission called file ando multimedia content, like this in the attached image
Screenshot_20231124_112120_Permission controller

@raver99
Copy link

raver99 commented Dec 12, 2023

I think this has been fixed by #2073.

Worked for me by updating to Xamarin Essentials 1.7.5

@softlion
Copy link

I found an easy solution.

You don't need anything from above:

  1. Update to Xamarin Essentials 1.8.0 (latest version)
  2. Add the "Xamarin Essentials" nuget to the xamarin Android project.

Fixed. Now the file picker behaves as expected !
No other change.

Why ? Dunno why an older version of xamarin essentials is used by the android project (even when built by appcenter !). I verified and it's 1.8.0 everywhere.

@activa
Copy link

activa commented Dec 15, 2023

I found an easy solution.

You don't need anything from above:

  1. Update to Xamarin Essentials 1.8.0 (latest version)
  2. Add the "Xamarin Essentials" nuget to the xamarin Android project.

Fixed. Now the file picker behaves as expected ! No other change.

Why ? Dunno why an older version of xamarin essentials is used by the android project (even when built by appcenter !). I verified and it's 1.8.0 everywhere.

Xamarin Essentials 1.8 alone definitely doesn't fix the problem. You need one of the workarounds described earlier in this issue. If your app targets SDK 33 or higher, the app needs to request the READ_MEDIA_IMAGES permission.

@softlion
Copy link

Xamarin Essentials 1.8 alone definitely doesn't fix the problem. You need one of the workarounds described earlier in this issue. If your app targets SDK 33 or higher, the app needs to request the READ_MEDIA_IMAGES permission.

Not necessarily.
In my case I don't need to access images, so I don't need that permission.

@mmeniawy
Copy link

what if um not working with media files, just a document/pdf file that I need to download,
in my case I'm trying to save a pdf file in the downloads folder still, um getting the error of access denied.

@MuhammadFarooq025
Copy link

OK - it's a simple fix to get around this, and it's working ok for me on API33.

You only have to update the following method, and then add the manifest entries - and your camera operations are working again on API 33.

  1. Android Main Activity updat the following Method to this.. (This tricks Android to accept a permission on a platform where it's no longer used - lol )
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any()))
            {
                var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE");
                var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");

                if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
                if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
            }

            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
  1. Manifest File
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.CAMERA" />
	<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
	<!--<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />-->
	<uses-feature android:name="android.hardware.location" android:required="false" />
	<uses-feature android:name="android.hardware.location.gps" android:required="false" />
	<uses-feature android:name="android.hardware.location.network" android:required="false" />
  
  <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
  <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>

Credit to @WanftMoon on this post (#2065) for pointing me in the right direction. Camera operations restored in API 33 - thank you.

its working perfectly fine
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any()))
{
var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE");
var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");

            if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
            if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
        }
        else
        {
            var wIdx1 = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE");
            var rIdx1 = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");

            if (wIdx1 != -1 && wIdx1 < permissions.Length) grantResults[wIdx1] = Permission.Granted;
            if (rIdx1 != -1 && rIdx1 < permissions.Length) grantResults[rIdx1] = Permission.Granted;

            var wIdx = Array.IndexOf(permissions, "android.permission.READ_MEDIA_IMAGES");
            var rIdx = Array.IndexOf(permissions, "android.permission.READ_MEDIA_VIDEO");

            if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Denied;
            if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Denied;
        }
        Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests