Download Manager Android Permissions error: write external Storage

Mark Witt picture Mark Witt · Nov 9, 2016 · Viewed 23.1k times · Source

I programmed an Android App that downloads a sample PDF file to the Download directory using DownloadManager. Here's the code:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_vertretungsplan);
    Button dlbutton = (Button) findViewById(R.id.buttondownload);
    final Context c = this;
    dlbutton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            DownloadManager.Request request = new DownloadManager.Request(Uri.parse(myurl));
            request.setTitle("Vertretungsplan");
            request.setDescription("wird heruntergeladen");
            request.allowScanningByMediaScanner();
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
            String filename = URLUtil.guessFileName(myurl, null, MimeTypeMap.getFileExtensionFromUrl(myurl));
            request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
            DownloadManager manager = (DownloadManager) c.getSystemService(Context.DOWNLOAD_SERVICE);
            manager.enqueue(request);
        }
    });
}

Now, I tried to debug-run it from Android Studio on my Xperia Z3 Android 6.0. As I clicked on the download button, this error occurs:

E/AndroidRuntime: FATAL EXCEPTION: main
              Process: com.example.markwitt.schul_app, PID: 29297
              java.lang.SecurityException: No permission to write to /storage/emulated/0/Download/hrpsampletable.pdf: Neither user 10047 nor current process has android.permission.WRITE_EXTERNAL_STORAGE.
                  at android.os.Parcel.readException(Parcel.java:1627)
                  at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
                  at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
                  at android.content.ContentProviderProxy.insert(ContentProviderNative.java:476)
                  at android.content.ContentResolver.insert(ContentResolver.java:1240)
                  at android.app.DownloadManager.enqueue(DownloadManager.java:946)
                  at com.example.markwitt.schul_app.Vertretungsplan$1.onClick(Vertretungsplan.java:59)
                  at android.view.View.performClick(View.java:5280)
                  at android.view.View$PerformClick.run(View.java:21239)
                  at android.os.Handler.handleCallback(Handler.java:739)
                  at android.os.Handler.dispatchMessage(Handler.java:95)
                  at android.os.Looper.loop(Looper.java:224)
                  at android.app.ActivityThread.main(ActivityThread.java:5526)
                  at java.lang.reflect.Method.invoke(Native Method)
                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

Disconnected from the target VM, address: 'localhost:8600', transport: 'socket'

(on the third line) it displays me that it has no write permission on external storage.

But my AndroidManifest is set up correctly:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.markwitt.schul_app">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
    android:launchMode="singleTop"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <activity android:name=".Stundenplan">


        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:name=".Vertretungsplan"></activity>
</application>

What do I need to do?

Answer

Tushar Monirul picture Tushar Monirul · Nov 9, 2016

You are getting this error because your app is running in Android 6.0(API level 23). From API level >= 23 you will need to check for the permission in run time. Your code is just fine for below level 23. So please check first if your user has given the permission to use the storage:

if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
    Log.e("Permission error","You have permission");
    return true;
}

If not then prompt the request:

ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);

Total things looks like this:

public  boolean haveStoragePermission() {
    if (Build.VERSION.SDK_INT >= 23) {
        if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                == PackageManager.PERMISSION_GRANTED) {
            Log.e("Permission error","You have permission");
            return true;
        } else {

            Log.e("Permission error","You have asked for permission");
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
            return false;
        }
    }
    else { //you dont need to worry about these stuff below api level 23
        Log.e("Permission error","You already have the permission");
        return true;
    }
}

And last thing :) receive the result by callback:

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(grantResults[0]== PackageManager.PERMISSION_GRANTED){
            //you have the permission now.
            DownloadManager.Request request = new DownloadManager.Request(Uri.parse(myurl));
            request.setTitle("Vertretungsplan");
            request.setDescription("wird heruntergeladen");
            request.allowScanningByMediaScanner();
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
            String filename = URLUtil.guessFileName(myurl, null, MimeTypeMap.getFileExtensionFromUrl(myurl));
            request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
            DownloadManager manager = (DownloadManager) c.getSystemService(Context.DOWNLOAD_SERVICE);
            manager.enqueue(request);
        }
    }



Or, you can just use my custom class PermissionCheck to handle all this implementation very easily. Here is the class:

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;

/**
 * Created by Tushar on 6/30/2017.
 */

public class PermissionCheck{
    public static boolean readAndWriteExternalStorage(Context context){
        if(ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
            return false;
        }else{
            return true;
        }

    }

    public static boolean audioRecord(Context context){
        if(ActivityCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED ){
            ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.RECORD_AUDIO}, 2);
            return false;
        }else{
            return true;
        }

    }

    public static boolean readAndWriteContacts(Context context){
        if(ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS}, 3);
            return false;
        }else{
            return true;
        }

    }

    public static boolean vibrate(Context context){
        if(ActivityCompat.checkSelfPermission(context, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.VIBRATE}, 4);
            return false;
        }else{
            return true;
        }

    }

    public static boolean sendSms(Context context){
        if(ActivityCompat.checkSelfPermission(context, Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.SEND_SMS}, 5);
            return false;
        }else{
            return true;
        }

    }

    //Just like this you can implement rest of the permissions. 
}


usage:

if(PermissionCheck.readAndWriteExternalStorage(context)){
    //Your read write code.
}

if(PermissionCheck.sendSms(context)){
    //Your sms sending code.
}

**Notice that, these methods call will ask for the permissions automatically and also your onRequestPermissionsResult will be fired. :)