Xamarin.Android: Adjusting legacy storage permissions for Android 13 deployments
0 reputation · 26 Sept 2021, 18:53 UTC
0 reputation · 26 Sept 2021, 18:53 UTC
The primary objective is to guarantee that a Xamarin.Android application targeting API level 33 can successfully install and launch on Android 13 devices. The current build process preserves the legacy READ_EXTERNAL_STORAGE permission declared in the app’s manifest, yet Android 13 requires the new READ_MEDIA_IMAGES (and related media permissions) for media access. When the legacy permission is present, the OS rejects the request, causing launch failures or silent crashes, as evidenced by logcat entries such as Permission denied: android.permission.READ_MEDIA_IMAGES.
Constraints include the fact that Xamarin.Android 12.x does not automatically translate deprecated permission names, and no migration guide is published. Developers must decide whether to manually update the manifest or rely on a potential undocumented build flag. The unresolved decision is whether Xamarin should provide automatic mapping of legacy permissions to the new media permission set.
Questions for investigation:
READ_EXTERNAL_STORAGE to the appropriate media permissions when targeting API level 33?32775 reputation · 26 Sept 2021, 20:02 UTC
1. Automatic mapping: The current Xamarin.Android 12.x SDK does not translate READ_EXTERNAL_STORAGE to the new READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, or READ_MEDIA_AUDIO permissions when you target API 33. The manifest is passed to AAPT unchanged, so the old permission is still requested and Android 13 will reject the request.
2. Recommended workflow:
READ_EXTERNAL_STORAGE (and WRITE_EXTERNAL_STORAGE) from AndroidManifest.xml.
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
Build.VERSION.SDK_INT >= 33.
uses-sdk element to split the manifest via Gradle’s productFlavors or manifestPlaceholders).MediaStore for media files. If you need arbitrary file access, consider migrating to app‑specific directories or request MANAGE_EXTERNAL_STORAGE with a Play Store justification.3. Build‑time flag: No undocumented flag exists in Xamarin.Android to enable legacy‑to‑new permission mapping. The migration must be done manually, either by editing the manifest or by using a Gradle manifest merge strategy.
Android 13 removes the legacy external‑storage model. Apps that still declare READ_EXTERNAL_STORAGE will see the permission dialog denied, and any attempt to open a media file will throw Permission denied: android.permission.READ_MEDIA_IMAGES in logcat. The only way to obtain media files is through the scoped media permissions or MANAGE_EXTERNAL_STORAGE (the latter requires Play Store approval).
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.app">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<!-- Legacy for API < 33 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:required="false" />
<!-- New media permissions for API 33+ -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
</manifest>
if (Build.VERSION.SdkInt >= 33)
{
// Request READ_MEDIA_IMAGES, etc.
ActivityCompat.RequestPermissions(this, new string[] { Android.Manifest.Permission.ReadMediaImages }, RequestCode);
}
else
{
// Request legacy READ_EXTERNAL_STORAGE
ActivityCompat.RequestPermissions(this, new string[] { Android.Manifest.Permission.ReadExternalStorage }, RequestCode);
}
Environment.GetExternalStoragePublicDirectory with MediaStore.Images.Media.GetContentUri or ContentResolver.OpenInputStream for media files.
READ_EXTERNAL_STORAGE warnings.MANAGE_EXTERNAL_STORAGE.
MANAGE_EXTERNAL_STORAGEIf your app still needs to read/write arbitrary files (e.g., PDFs, documents) outside the app’s sandbox, you must migrate to app‑specific directories or request MANAGE_EXTERNAL_STORAGE. This permission is highly scrutinized by Google Play, so provide a clear justification and privacy policy. Only use it as a last resort.
Could you confirm whether your application accesses non‑media files (e.g., PDFs, documents) that would require broad storage access? The answer will determine if the migration can rely solely on media permissions or if MANAGE_EXTERNAL_STORAGE is necessary.
AndroidManifest.xml and project properties.READ_EXTERNAL_STORAGE is present only for API < 33 (android:required="false").Build.VERSION.SDK_INT checks.MediaStore APIs.Permission denied: android.permission.READ_EXTERNAL_STORAGE errors after migration.Use comments to ask for clarification. Post a solution as an answer.
32,775 reputation · 26 Sept 2021, 22:36 UTC
To avoid maintaining multiple manifest files or complex Gradle flavors, a practical way to handle the transition is using the android:maxSdkVersion attribute. This allows you to keep both the legacy and granular permissions in a single manifest without causing conflicts on Android 13+ devices.
<!-- For Android 12 and below -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!-- For Android 13 and above -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
When targeting API 33 in Xamarin.Android, the OS will ignore the legacy permission on newer devices due to the maxSdkVersion limit, preventing the silent crashes mentioned. To verify the final result, check the merged manifest in the obj/Debug/ or obj/Release/ folder to ensure the attributes are preserved during the build process.