130

There is a lint warning in AS with regards to android.permission.WRITE_EXTERNAL_STORAGE. The warning says that the permission will no longer provide write access when targeting Android 10 and above. Removal of the said permission can still write in internal storage folder Pictures/MY_APP_NAME to save images, but it only works on Android 10 (SDK 29) and/or above (haven't tested yet on Android R). When I tested it again on lower version such as Android M (SDK 23), saving images stop working so I decided to return the android.permission.WRITE_EXTERNAL_STORAGE thus the warning shows up again. Is it possible that the lint is just false positive that incorrectly diagnosed the problem on different cases? Because currently my support SDK starts with 21 up to the latest which is 30 but the lint only point out that it is no longer needed when targeting Android 10 (SDK 29) and did not consider looking back at the project's minimum SDK support.

Mihae Kheel
  • 2,441
  • 3
  • 14
  • 38
  • `can still write in internal storage folder Pictures/MY_APP_NAME` Strange. Please give full path of folder. – blackapps Oct 06 '20 at 07:44
  • You can still write to `Internal Storage/Pictures/MY_APP_NAME` using MediaStore, ContentResolver, and ContentValues in Android 10. – Mihae Kheel Oct 06 '20 at 07:48
  • The warning does not mean you must remove `WRITE_EXTERNAL_STORAGE`, it is warning you that Android 10+ requires other mechanisms to access external storage. – ephemient Oct 06 '20 at 07:51
  • @ephemient yes that is what I think as I tested it. So how can I remove the warning or should I just ignore it? – Mihae Kheel Oct 06 '20 at 07:53
  • `You can still write to Internal Storage/Pictures/MY_APP_NAME using MediaStore, ContentResolver, and ContentValues in Android 10.` Well please put that in your post as without mentioning this all it is pretty confusing as you have seen now. – blackapps Oct 06 '20 at 08:09
  • You don't need write permission for your app folder read/write. For shared files try ``. And for API 30+, you'll need `MANAGE_EXTERNAL_STORAGE` permission if you need access to non-media files. Check out my similar answer https://stackoverflow.com/a/63815403/3437352. – Siddharth Kamaria Oct 06 '20 at 08:13
  • 1
    @SiddharthKamaria yeah, I tried adding `android:maxSdkVersion="29"` but does not remove the warning. – Mihae Kheel Oct 06 '20 at 08:20
  • 1
    @MihaeKheel Weird enough my AS is not showing me lint warning for `WRITE_EXTERNAL_STORAGE` even without maxSdkVersion. Maybe try rebuilding or invalidating caches? – Siddharth Kamaria Oct 06 '20 at 08:24
  • 1
    @SiddharthKamaria thanks for pointing out but I already tried invalidating, clean build, and rebuild the warning still exist. Maybe it's due to us having a different AS version I am in Canary version of AS anyway. – Mihae Kheel Oct 06 '20 at 08:31
  • This Answer worked for me for android 11+ !! check it out, hopefully it'll work for you too https://stackoverflow.com/a/66366102/16657358(https://stackoverflow.com/a/66366102/16657358) [(btw, `int SDK_INT = 30;` it confused me lol so thought i should mention)] – Qwerty ztrewq Oct 31 '21 at 18:15
  • Isn't maxSdkVersion preventing users with higher Android version from installing the app? – The incredible Jan Apr 17 '23 at 10:13

5 Answers5

134

A workaround is to actually ignore the warning, as it is just informational and therefore harmless. By setting maxSdkVersion to 28 no need to worry anymore.

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28"
    tools:ignore="ScopedStorage" />

Note that using the android:requestLegacyExternalStorage flag as stated in other answers is not a solution, is just a temporary patch which will no longer work at all in Android 11 (API 30), and future versions

UPDATE, to clarify the doubts and confusions shown by some developers in the comments:

  • If using the requestLegacyExternalStorage flag in Android 10 (API 29) then request the WRITE_EXTERNAL_STORAGE permission as usual.

  • The flag requestLegacyExternalStorage does nothing in Android 11 (API 30), it is completely ignored, and there is not workaround for it.

  • WRITE_EXTERNAL_STORAGE does not give any privileges in Android 11 (API 30), it does nothing at all, therefore in API 11 you need to set the maxSdkVersion to 29.

  • If in Android 10 (API 29) you are also not using requestLegacyExternalStorage then set maxSdkVersion to 28 instead of 29.

  • Starting in Android 11 (API 30), the older File API can again be used but "only" when accessing the public "shared storage" folders (DCIM, Music, etc.), or your app "private" directory. For other locations the DocumentFile API is required.

  • Consider that the File API is now much slower in Android 11 (API 30), because has been refactored becoming essentially a wrapper. This is to enforce its usage just to the allowed locations. So, is no longer a fast system file API, is just a wrapper that internally delegates the work to the MediaStore. When using the File API in Android 11 or above you should consider the performance penalty hit, as according to the Android team it will be 2 to 3 times slower than if accessing directly the MediaStore.

PerracoLabs
  • 16,449
  • 15
  • 74
  • 127
  • thank you for your feedback, I am surprised to read that, as the documentation under https://developer.android.com/guide/topics/manifest/uses-sdk-element#max says that installation will not be possible and in case of revalidation after a system update the app will even be removed – Christian Jan 28 '21 at 15:38
  • 8
    @Christian Ok, now I understand your confusion. The link you mention refers to the "" element, and not to . These are 2 different type of elements, and "maxSdkVersion" is actually a common "attribute" which exists for any type of element. What you must "not do" is to declare "maxSdkVersion" in the "" element, but this is unrelated to permissions. Check the actual documentation where it explains how to use "maxSdkVersion" to limit a permission as per API level: https://developer.android.com/guide/topics/manifest/uses-permission-element – PerracoLabs Jan 28 '21 at 16:27
  • 1
    o.k., now I understand your idea. But still you will not get the legacy file access on Android 10 and above, correct? Like folder.listFiles() will not work – Christian Jan 28 '21 at 21:30
  • 1
    @Christian For Andorid 10 use requestLegacyExternalStorage. And for Android 11 the File API can be used "only" when accessing the public "shared storage" folders (DCIM, Music, etc.), or your "private" app directory. For other locations use the DocumentFile API. Note that the File API is slower in API 30, because has been refactored as a wrapper to enforce its usage to only the allowed locations. Internally delegates the work to the MediaStore. The flag "requestLegacyExternalStorage" no longer works in Android 11 as it was just temporary to give developers enough time to perform the migration. – PerracoLabs Jan 28 '21 at 22:47
  • @PerracoLabs I updated the code to be compliant with scoped storage and applied maxSdkVersion 28 to the "write external storage" permission. However, that permission still appears on the app settings page on devices running SDK 29. How to get rid of the permission on the SDK above 28? – Lingviston Apr 20 '21 at 13:05
  • @PerracoLabs I realized that it was still there because of the READ_EXTERNAL_STORAGE permission being present. – Lingviston Apr 20 '21 at 20:28
  • 1
    This did not mention about the possible problem setting maxSdkVersion to 28 since `ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED` will never return true in 29. What a mistake patch update I made in the store. – Bitwise DEVS May 01 '21 at 19:12
  • 1
    bad solution? It's will make crash in android 11 – famfamfam Nov 06 '21 at 17:10
  • PERMISSION_GRANTED for WRITE_EXTERNAL_STORAGE will always return false. So there is mistake in the answer. And the interesting thing is there is no proper solution about this issue anywhere. – Mehmet Gür Feb 10 '23 at 11:44
30

Try to add this in your Manifest:

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true" //Add this Line
android:label="@string/app_name">

 ---------<Activity, Sevices or Recivers>----------

</application>

and remove the READ_EXTERNAL_STORAGE permission again:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Aldinjo
  • 394
  • 1
  • 7
  • 21
Francesco Bocci
  • 827
  • 8
  • 19
  • 8
    No longer need `requestLegacyExternalStorage` and is not a good solution for Android 10 as there are APIs like MediaStore, ContentResolver, and ContentValues to use if you want to store your files in `Internal Storage/Pictures/MY_APP_NAME` – Mihae Kheel Oct 06 '20 at 07:51
  • 3
    No. It is pretty good solution. You can continue to use classic file paths. And on Android 11 you can write to Pictures folder just using classic file paths too. No need for mediastore and saf – blackapps Oct 06 '20 at 08:07
  • 2
    @blackapps please read https://developer.android.com/training/data-storage/use-cases – Mihae Kheel Oct 06 '20 at 08:13
  • 14
    `Caution: 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.` See also https://stackoverflow.com/questions/63364476/requestlegacyexternalstorage-is-not-working-in-android-11-api-30 – Mihae Kheel Oct 06 '20 at 08:26
  • 3
    Yes that legacy request does nothing for an Android 11 device. But it still works for an Android 10 device. So continue to use it i would say and that is what i said. – blackapps Oct 06 '20 at 08:34
  • 3
    And again: on an Android 11 device your app can just write to /storage/emulated/0/Pictures itself. No need for media store and saf. You do not need MANAGE_EXTERNAL_STORAGE for that as @Siddharth Kamaria is suggesting. – blackapps Oct 06 '20 at 08:36
  • 1
    Why is this contribution about READ_EXTERNAL_STORAGE? I don't believe that a read permission helps to get write access... – The incredible Jan Jan 12 '23 at 12:13
27

Using File-apis ?

Based on @PerracoLabs reply the following changes will make your app work on the devices running

  1. Android-9 and less
  2. Android-10
  3. Android-11 and above

File: "AndroidManifest.xml"

<!-- Without this folders will be inaccessible in Android-11 and above devices -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

<!-- Without this entry storage-permission entry will not be visible under app-info permissions list Android-10 and below -->
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"
tools:ignore="ScopedStorage"/>

<!-- Without this entry the folders will remain in-accessible in Android-10, even if WRITE_EXTERNAL_STORAGE as above is present. -->
<application
    android:requestLegacyExternalStorage="true"/>

Java Source:

  1. Don't forget to give Runtime-permissions, before doing file operation. - Needed for Android-10 and below. DONT request for runtime permissions for Android-11, they are to be requested for Android-10 and below.

  2. Navigate the user to app-permission page to allow him to enable file-permissions using the following code (needed for Android-11 and above support):

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && false == Environment.isExternalStorageManager()) {
         Uri uri = Uri.parse("package:" + BuildConfig.APPLICATION_ID);
         startActivity(new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, uri));
     }
    
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
shyguy
  • 301
  • 3
  • 8
  • 1
    Where to put this per condition? In this file at which line? – Atif Feb 19 '22 at 15:55
  • 3
    " Android-9 and less Android-10 Android-11 and above " is the most complicated way to say "all Android versions" I have ever seen. – The incredible Jan Jan 12 '23 at 12:15
  • "You should request the MANAGE_EXTERNAL_STORAGE permission only when your app cannot effectively make use of the more privacy-friendly APIs, such as Storage Access Framework or the Media Store API. Additionally, the app's usage of the permission must fall within permitted uses, and must be directly tied to the core functionality of the app." https://developer.android.com/training/data-storage/manage-all-files – The incredible Jan Jan 23 '23 at 10:50
15

As answered @PerracoLabs the warning is just informational. It will be better if at first you'll read his answer. https://stackoverflow.com/a/65477206/6055194

I use WRITE_EXTERNAL_STORAGE permission only for downloading of .pdf-files into Downloads and app "private" directories by using of DownloadManager (that's important and you can understand why if you will read discussion under @PerracoLabs answer). So I've just added max level of SDK for this permission and ignore tag for scoped storage.

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28"
    tools:ignore="ScopedStorage" />

P.S. if tools tag not recognized, than you must declare it by adding xmlns:tools="http://schemas.android.com/tools" to your AndroidManifest.xml like this.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.app.my">

But you must note that after it onRequestPermissionsResult function will not be called for Build.VERSION_CODES.Q and higher (API 29+). So you must call request permission WRITE_EXTERNAL_STORAGE only for API lower than 29.

So you can add check write external storage function like this (Kotlin).

   private fun hasWriteStoragePermission(): Boolean {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            return true
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ActivityCompat.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(
                        arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                        REQUEST_PERMISSIONS_CODE_WRITE_STORAGE
                )

                return false
            }
        }

        return true
    }
Eugene Babich
  • 1,271
  • 15
  • 25
1

Add the line:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:remove="android:maxSdkVersion"/>

and add the below code to to AndroidManifest.xml:

 android:requestLegacyExternalStorage="true"
10 Rep
  • 2,217
  • 7
  • 19
  • 33