Oreo (ЗАПИСЬ ВНЕШНЕГО ХРАНЕНИЯ) Разрешение

По словам "developer.android.com"

If the app targets Android 8.0 (API level 26), the system grants only 
READ_EXTERNAL_STORAGE at that time; however, if the app later requests 
WRITE_EXTERNAL_STORAGE, the system immediately grants that privilege 
without prompting the user.

Теперь у меня есть READ_EXTERNAL_STORAGE, и я прошу DownloadManager загрузить файл в папке Public Download

downloadRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, mAttachmentItem.getFilename());

К сожалению, я получил

E/UncaughtException: java.lang.IllegalStateException: Unable to create directory: /storage/emulated/0/data/user/0/com.abc.cba/files
                                                                           at android.app.DownloadManager$Request.setDestinationInExternalPublicDir(DownloadManager.java:699)

Я объявил обе разрешения в манифесте

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

Разрешение на чтение уже предоставлено и в Runtime Я запрашиваю разрешение на запись, система не запрашивает никаких сообщений или диалоговых окон, она всегда отрицает (не предоставляется) в "onRequestPermissionsResult"

public static boolean isPermissionsToAccessStorageAreGranted(Context context, Activity activity) {
    ArrayList<String> permissions = new ArrayList<>();

    if (!PermissionUtil.checkPermissions(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
        permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
    }

    if (!permissions.isEmpty()) {
        String[] permissionsList = permissions.toArray(new String[permissions.size()]);
        PermissionUtil.requestPermissions(activity, permissionsList,
                PermissionUtil.REQUESTCODE_ACCESS_STORAGE);
        return false;
    } else {
        return true;
    }
}


@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if(requestCode==REQUESTCODE_ACCESS_STORAGE && grantResults[0]== PackageManager.PERMISSION_GRANTED){
 /* downloadFile(); */       
    }
}

Я пытаюсь предоставить эти два разрешения через ADB, это показывает мне ошибку:

adb shell pm grant com.abc.cba 
android.permission.WRITE_EXTERNAL_STORAGE
Operation not allowed: java.lang.SecurityException: Package com.abc.cbs 
has not requested permission android.permission.WRITE_EXTERNAL_STORAGE

adb shell pm grant com.abc.cba 
android.permission.READ_EXTERNAL_STORAGE
Operation not allowed: java.lang.SecurityException: Can't change 
android.permission.READ_EXTERNAL_STORAGE. It is required by the 
application

Ответ 1

На самом деле, это была внешняя проблема. Один из приложений App я использую запрос WRITE_EXTERNAL_PERMISSION с Android: maxSdkVersion. Так что при слиянии с моим манифестом это приведет к удалению разрешения для всего приложения.

Решение состоит в том, чтобы добавить:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace"/> 

Ответ 2

Вы правильно добавляете разрешения для манифеста:

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

Для версий Lollipop и выше вам также необходимо запросить разрешения во время выполнения. Чтобы решить эту проблему, я создал новый метод requestAppPermission который я вызываю при создании основного действия. Этот метод работает только для Lollipop и выше и возвращается раньше:

private void requestAppPermissions() {
    if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        return;
    }

    if (hasReadPermissions() && hasWritePermissions()) {
        return;
    }

    ActivityCompat.requestPermissions(this,
            new String[] {
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            }, REQUEST_WRITE_STORAGE_REQUEST_CODE); // your request code
}

private boolean hasReadPermissions() {
    return (ContextCompat.checkSelfPermission(getBaseContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
}

private boolean hasWritePermissions() {
    return (ContextCompat.checkSelfPermission(getBaseContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
}

Я называю этот метод в onCreate:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Other things
    requestAppPermissions();
}

При первой загрузке пользователь будет запрашивать разрешения.

Затем перед запуском любого кода, который необходимо прочитать и записать в хранилище, вы можете проверить эти разрешения либо путем сохранения значений, либо повторения этих проверок с помощью методов hasReadPermissions() и hasWritePermissions() определенных выше.

Ответ 3

Для Oreo,

вам нужно явно выполнить запрос READ_EXTERNAL_STORAGE (по коду), даже если вы уже запросили и получили разрешение WRITE_EXTERNAL_STORAGE, и наоборот.

до этого READ_EXTERNAL_STORAGE автоматически предоставляется, когда предоставляется WRITE_EXTERNAL_STORAGE.

так что вам нужно сделать, это

1) попросите разрешение WRITE в ваших кодах.

2) когда пользователь предоставляет разрешение WRITE, снова запросите разрешение READ - это будет автоматически предоставлено (вы получите исключение, если вы не запрашиваете READ явно)

Изменения для Oreo не имеют для меня большого смысла (не знаю, почему нам нужно запрашивать разрешение, которое автоматически предоставляется), но это то, что есть.

https://developer.android.com/about/versions/oreo/android-8.0-changes.html#rmp

Ответ 4

Вы должны предоставить разрешения во время выполнения на Зефир или выше. Образец фрагмента:

private static final int REQUEST_WRITE_STORAGE = 112;

if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            boolean hasPermission = (ContextCompat.checkSelfPermission(getBaseContext(),
                    Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
            if (!hasPermission) {
                ActivityCompat.requestPermissions(SplashScreen.this,
                        new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,
                                Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.RECORD_AUDIO, Manifest.permission.MODIFY_AUDIO_SETTINGS, Manifest.permission.INTERNET
                        },
                        REQUEST_WRITE_STORAGE);
            }else{ startMainActivity(); }
        }

Надеюсь, поможет.

Ответ 5

Просто уведомление: на Android PI пришлось показать диалоговое окно завершения для пользователя, требующего перезапустить приложение, потому что даже после того, как все написанное выше приложение не сможет прочитать внешнее хранилище, только после перезагрузки. Серьезно, эти ребята в google делают Android лучше каждый раз. 6, 8, и теперь столько проблем с 9, еще одно такое глупое обновление, и я поеду в iOS :)

Ответ 6

Если вы получаете ошибку " Отказано в разрешении", даже если разрешения предоставлены и вы уже осуществили проверку разрешений,

убедитесь, что вы не нацелены на уровень API 29:

Измените targetSdkVersion и compilesdkversion с 29 на 28 или любой другой более низкий уровень.


Надеюсь, это спасет кого-то от психического здоровья и волос. Я потерял часть своей борьбы с этой ерундой.