Android is a privilege separated operating system, i.e. each application in android is separated from another through a distinct id, and each application file / data is private to that application only. Each Android application is started in its own process thus are isolated from all other applications (even from system / default applications). As a result an Android application can’t access any file or data outside its scope until and unless the file or data is shared with the application.

So, the conclusion is that if an application needs anything outside its scope, then it should request for permission to the user. Android comes with a set of predefined permissions (System permissions) for certain tasks. Every application can request required permissions. For example, an application may declare that it requires network access. It can also define new permissions.

android-working-with-marshmallow-permissions

1. Permission Workflow before and from M (API 23)

Now the question arises how an application can request for or get permission? Or rather, what workflow the developer should follow to request and get permission for the app? Let us have a look. The permission model / workflow has been changed from API 23 – Android M.

> Permission Model before M (API 23): Before API 23, the permission model was simpler to the developer but offered less control and security to the user – requested permissions are presented to the user before installing the application. The user needs to decide whether to give these permissions to the application and install it or to deny as a whole and don’t install the application. Permissions cannot be denied or granted after the installation. Thus developers were required only to declare all needed permissions in the manifest and then just forget; if the app is running then it has all the requested permissions.

> Permission Model from M (API 23): With Android 6.0 Marshmallow (API 23), a new runtime permission model has been introduced. According to this model users are not prompted for permission at the time of installing the app, rather developers need to check for and request permission at runtime (i.e. before performing any action that may require the particular permission), users can then allow or deny the permission, users can also grant or revoke permission anytime from settings. Making the user experience very secure on an Android device. But inversely this feature imposes a great deal of effort on development. Therefore developers have to handle all the use cases. Thankfully, requesting Android runtime permissions, is not that difficult.

Note: According to Google, beginning with Android 6.0 (API level 23), users can revoke permissions from any app at any time, even if the app targets a lower API level. You should test your app to verify that it behaves properly when it’s missing a needed permission, regardless of what API level your app targets.

2. Permission levels

System permissions have different protection levels. The two most important protection levels are normal and dangerous permissions.

Normal Permissions: Those permissions which will have very little or zero effect on users privacy or security are categorized as normal permissions. The system itself grants normal permissions when requested instead of prompting to the user. Examples would be ACCESS_WIFI_STATE, WAKE_LOCK etc.

Dangerous Permissions: Those permissions which may have a greater effect on users privacy or security are categorized as dangerous permissions. Examples would be the ability to read the user’s contacts READ_CONTACTS. If an app requests a dangerous permission, the user has to explicitly grant the permission to the app.

Here is the detailed information about permissions and their groups.

So now let us start with creating a sample app.

3. Creating Android Project

1. Create a new project in Android Studio from File ⇒ New Project and fill the project details.

2. Open strings.xml and add the below string values.

<resources>
    <string name="app_name">M Permissions</string>
    <string name="action_settings">Settings</string>
    <string name="single_permission_text">(Click on FAB to check Single permission.\nTo test again, change the app permission in settings or reinstall the app)</string>
</resources>

3. Open build.gradle (app level) and make sure that you have set minSdkVersion and targetSdkVersion as we have to support the permissions model in lower versions also. I am keeping minSdkVersion to 17 and targetSdkVersion to 23.

apply plugin: 'com.android.application'

android {
    defaultConfig {
        ..
        minSdkVersion 17
        targetSdkVersion 23
        ..
    }
}

3.1 Requesting Single Permission

4. Though we will request the permissions at runtime, we should add it to the Manifest also, so that the user will be prompted first time when going to use the permission, and then the system will remember user’s decision until user updates from the settings. We will start with the WRITE_EXTERNAL_STORAGE_PERMISSION. Add the storage permission to your AndroidManifest.xml just before the application tag.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.mpermissions">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name="info.androidhive.mpermissions.MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

5. Open the layout file of main activity (activity_main.xml) and add the below xml. This layout contains few buttons to test various permission scenarios.

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="info.androidhive.mpermissions.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <RelativeLayout
        android:id="@+id/content_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context="info.androidhive.mpermissions.MainActivity"
        tools:showIn="@layout/activity_main">

        <Button
            android:id="@+id/btnLaunchMultiplePermission"
            android:layout_below="@+id/imageView"
            android:layout_marginTop="20dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Request Multiple Permissions"/>

        <Button
            android:id="@+id/btnLaunchPermissionFragment"
            android:layout_below="@+id/btnLaunchMultiplePermission"
            android:layout_marginTop="10dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Request Permission on Fragment"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/single_permission_text"
            android:textAlignment="center"
            android:layout_alignParentBottom="true"
            android:layout_alignParentStart="true"
            android:layout_marginBottom="123dp" />

    </RelativeLayout>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@drawable/ic_file_download_black_24dp" />

</android.support.design.widget.CoordinatorLayout>

The above layout generates a screen something like this.

android-marshmallow-permissions-model

6. Now open MainActivity.java and add the below code in FAB click event.

if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                //Show Information about why you need the permission
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                builder.setTitle("Need Storage Permission");
                builder.setMessage("This app needs storage permission.");
                builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                        ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_CONSTANT);
                    }
                });
                builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
                builder.show();
            } else if (permissionStatus.getBoolean(Manifest.permission.WRITE_EXTERNAL_STORAGE,false)) {
                //Previously Permission Request was cancelled with 'Dont Ask Again',
                // Redirect to Settings after showing Information about why you need the permission
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                builder.setTitle("Need Storage Permission");
                builder.setMessage("This app needs storage permission.");
                builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                        sentToSettings = true;
                        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                        Uri uri = Uri.fromParts("package", getPackageName(), null);
                        intent.setData(uri);
                        startActivityForResult(intent, REQUEST_PERMISSION_SETTING);
                        Toast.makeText(getBaseContext(), "Go to Permissions to Grant Storage", Toast.LENGTH_LONG).show();
                    }
                });
                builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
                builder.show();
            } else {
                //just request the permission
                ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_CONSTANT);
            }

            SharedPreferences.Editor editor = permissionStatus.edit();
            editor.putBoolean(Manifest.permission.WRITE_EXTERNAL_STORAGE,true);
            editor.commit();


        } else {
            //You already have the permission, just go ahead.
            proceedAfterPermission();
        }

Explanation:

> We checked for permission with checkSelfPermission() Method to check if the app already has permission to write on external storage. If it has then continue to else part, otherwise go to next step.

> Here we used shouldShowRequestPermissionRationale() method to check if we should show the explanation for the permission or not, if this returns true then we showed an alert dialog with explanation, and on the positive button click we requested the permission. If shouldShowRequestPermissionRationale returns false then we requested permission straightforward.

> We have successfully requested permission so far. We just need to override the method onRequestPermissionsResult to receive the result. In that method first we checked the requested code with our declared constant requestCode == EXTERNAL_STORAGE_PERMISSION_CONSTANT then checked if length of grantResult is greater than 0, so that it contains the user’s decision, then we cheked if the value of 0 index of the grant result, if the value is equal to PackageManager.PERMISSION_GRANTED then it means that the user has authorised the permission and we can continue our work, otherwise in the else part we can again request for permission with explanation.

> Add SharedPreferences, think of a situation where the user has denied permission by checking “Never Ask Again”. In that scenario the shouldShowRequestPermissionRationale method will return false and requestPermissions method will do just nothing. So we need to remember whether we ave priorly requested for permission or not, even after the app restarts. For that purpose we will use SharedPreferences, we will use the permission string as the key and boolean true or false as value.

Below is the complete code of MainActivity.java

package info.androidhive.mpermissions;

import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;


public class MainActivity extends AppCompatActivity {
   private static final int EXTERNAL_STORAGE_PERMISSION_CONSTANT = 100;
   private static final int REQUEST_PERMISSION_SETTING = 101;
   private boolean sentToSettings = false;
   private SharedPreferences permissionStatus;


   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
       setSupportActionBar(toolbar);


       permissionStatus = getSharedPreferences("permissionStatus",MODE_PRIVATE);


       FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
       fab.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                   if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                       //Show Information about why you need the permission
                       AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                       builder.setTitle("Need Storage Permission");
                       builder.setMessage("This app needs storage permission.");
                       builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                           @Override
                           public void onClick(DialogInterface dialog, int which) {
                               dialog.cancel();
                               ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_CONSTANT);
                           }
                       });
                       builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                           @Override
                           public void onClick(DialogInterface dialog, int which) {
                               dialog.cancel();
                           }
                       });
                       builder.show();
                   } else if (permissionStatus.getBoolean(Manifest.permission.WRITE_EXTERNAL_STORAGE,false)) {
                       //Previously Permission Request was cancelled with 'Dont Ask Again',
                       // Redirect to Settings after showing Information about why you need the permission
                       AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                       builder.setTitle("Need Storage Permission");
                       builder.setMessage("This app needs storage permission.");
                       builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                           @Override
                           public void onClick(DialogInterface dialog, int which) {
                               dialog.cancel();
                               sentToSettings = true;
                               Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                               Uri uri = Uri.fromParts("package", getPackageName(), null);
                               intent.setData(uri);
                               startActivityForResult(intent, REQUEST_PERMISSION_SETTING);
                               Toast.makeText(getBaseContext(), "Go to Permissions to Grant Storage", Toast.LENGTH_LONG).show();
                           }
                       });
                       builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                           @Override
                           public void onClick(DialogInterface dialog, int which) {
                               dialog.cancel();
                           }
                       });
                       builder.show();
                   } else {
                       //just request the permission
                       ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_CONSTANT);
                   }


                   SharedPreferences.Editor editor = permissionStatus.edit();
                   editor.putBoolean(Manifest.permission.WRITE_EXTERNAL_STORAGE,true);
                   editor.commit();




               } else {
                   //You already have the permission, just go ahead.
                   proceedAfterPermission();
               }
           }
       });


     }


   private void proceedAfterPermission() {
       //We've got the permission, now we can proceed further
       Toast.makeText(getBaseContext(), "We got the Storage Permission", Toast.LENGTH_LONG).show();
   }

   @Override
   public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
       super.onRequestPermissionsResult(requestCode, permissions, grantResults);
       if (requestCode == EXTERNAL_STORAGE_PERMISSION_CONSTANT) {
           if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
               //The External Storage Write Permission is granted to you... Continue your left job...
               proceedAfterPermission();
           } else {
               if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                   //Show Information about why you need the permission
                   AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                   builder.setTitle("Need Storage Permission");
                   builder.setMessage("This app needs storage permission");
                   builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                       @Override
                       public void onClick(DialogInterface dialog, int which) {
                           dialog.cancel();


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


                       }
                   });
                   builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                       @Override
                       public void onClick(DialogInterface dialog, int which) {
                           dialog.cancel();
                       }
                   });
                   builder.show();
               } else {
                   Toast.makeText(getBaseContext(),"Unable to get Permission",Toast.LENGTH_LONG).show();
               }
           }
       }
   }


   @Override
   protected void onActivityResult(int requestCode, int resultCode, Intent data) {
       super.onActivityResult(requestCode, resultCode, data);
       if (requestCode == REQUEST_PERMISSION_SETTING) {
           if (ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
               //Got Permission
               proceedAfterPermission();
           }
       }
   }


   @Override
   protected void onPostResume() {
       super.onPostResume();
       if (sentToSettings) {
           if (ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
               //Got Permission
               proceedAfterPermission();
           }
       }
   }
}
android-m-permissions
android-m-permissions-settings

3.2 Requesting Multiple Permissions

Think of a scenario where you might need to ask for multiple permissions. For this, instead of requesting multiple permissions in multiple dialogs, we can prompt all the permissions in a single dialog where permissions will be scrolled through one after another. Now we’ll test this case by creating a new activity.

7. Create a new Activity and name it as MultiplePermissionsActivity.java. Open the layout file this activity (activity_multiple_permissions.xml) and add the below layout code.

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="info.androidhive.mpermissions.FragmentPermissionActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>


    <LinearLayout
        android:id="@+id/activity_multiple_permissions"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context="info.androidhive.mpermissions.MultiplePermissionsActivity">

        <TextView
            android:id="@+id/txtPermissions"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/btnCheckPermissions"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Check Permissions" />

    </LinearLayout>
</android.support.design.widget.CoordinatorLayout>

8. Initialize btnLaunchMultiplePermission inside oncreate on MainActivity.java, and implement onClickListener to open MultiplePermissionsActivity on click.

Button btnLaunchMultiplePermission = (Button) findViewById(R.id.btnLaunchMultiplePermission);
btnLaunchMultiplePermission.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
       Intent intent = new Intent(MainActivity.this, MultiplePermissionsActivity.class);
       startActivity(intent);
   }
});

9. Add the below permissions to AndroidManifest.xml file.

<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" />

10. Open MultiplePermissionsActivity.java and modify the code as below.

package info.androidhive.mpermissions;

import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MultiplePermissionsActivity extends AppCompatActivity {

    private static final int PERMISSION_CALLBACK_CONSTANT = 100;
    private static final int REQUEST_PERMISSION_SETTING = 101;
    String[] permissionsRequired = new String[]{Manifest.permission.CAMERA,
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_COARSE_LOCATION};
    private TextView txtPermissions;
    private Button btnCheckPermissions;
    private SharedPreferences permissionStatus;
    private boolean sentToSettings = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_multiple_permissions);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        permissionStatus = getSharedPreferences("permissionStatus",MODE_PRIVATE);

        txtPermissions = (TextView) findViewById(R.id.txtPermissions);
        btnCheckPermissions = (Button) findViewById(R.id.btnCheckPermissions);

        btnCheckPermissions.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(ActivityCompat.checkSelfPermission(MultiplePermissionsActivity.this, permissionsRequired[0]) != PackageManager.PERMISSION_GRANTED
                        || ActivityCompat.checkSelfPermission(MultiplePermissionsActivity.this, permissionsRequired[1]) != PackageManager.PERMISSION_GRANTED
                        || ActivityCompat.checkSelfPermission(MultiplePermissionsActivity.this, permissionsRequired[2]) != PackageManager.PERMISSION_GRANTED){
                    if(ActivityCompat.shouldShowRequestPermissionRationale(MultiplePermissionsActivity.this,permissionsRequired[0])
                            || ActivityCompat.shouldShowRequestPermissionRationale(MultiplePermissionsActivity.this,permissionsRequired[1])
                            || ActivityCompat.shouldShowRequestPermissionRationale(MultiplePermissionsActivity.this,permissionsRequired[2])){
                        //Show Information about why you need the permission
                        AlertDialog.Builder builder = new AlertDialog.Builder(MultiplePermissionsActivity.this);
                        builder.setTitle("Need Multiple Permissions");
                        builder.setMessage("This app needs Camera and Location permissions.");
                        builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.cancel();
                                ActivityCompat.requestPermissions(MultiplePermissionsActivity.this,permissionsRequired,PERMISSION_CALLBACK_CONSTANT);
                            }
                        });
                        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.cancel();
                            }
                        });
                        builder.show();
                    } else if (permissionStatus.getBoolean(permissionsRequired[0],false)) {
                        //Previously Permission Request was cancelled with 'Dont Ask Again',
                        // Redirect to Settings after showing Information about why you need the permission
                        AlertDialog.Builder builder = new AlertDialog.Builder(MultiplePermissionsActivity.this);
                        builder.setTitle("Need Multiple Permissions");
                        builder.setMessage("This app needs Camera and Location permissions.");
                        builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.cancel();
                                sentToSettings = true;
                                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                Uri uri = Uri.fromParts("package", getPackageName(), null);
                                intent.setData(uri);
                                startActivityForResult(intent, REQUEST_PERMISSION_SETTING);
                                Toast.makeText(getBaseContext(), "Go to Permissions to Grant  Camera and Location", Toast.LENGTH_LONG).show();
                            }
                        });
                        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.cancel();
                            }
                        });
                        builder.show();
                    }  else {
                        //just request the permission
                        ActivityCompat.requestPermissions(MultiplePermissionsActivity.this,permissionsRequired,PERMISSION_CALLBACK_CONSTANT);
                    }

                    txtPermissions.setText("Permissions Required");

                    SharedPreferences.Editor editor = permissionStatus.edit();
                    editor.putBoolean(permissionsRequired[0],true);
                    editor.commit();
                } else {
                    //You already have the permission, just go ahead.
                    proceedAfterPermission();
                }
            }
        });
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode == PERMISSION_CALLBACK_CONSTANT){
            //check if all permissions are granted
            boolean allgranted = false;
            for(int i=0;i<grantResults.length;i++){
                if(grantResults[i]==PackageManager.PERMISSION_GRANTED){
                    allgranted = true;
                } else {
                    allgranted = false;
                    break;
                }
            }

            if(allgranted){
                proceedAfterPermission();
            } else if(ActivityCompat.shouldShowRequestPermissionRationale(MultiplePermissionsActivity.this,permissionsRequired[0])
                    || ActivityCompat.shouldShowRequestPermissionRationale(MultiplePermissionsActivity.this,permissionsRequired[1])
                    || ActivityCompat.shouldShowRequestPermissionRationale(MultiplePermissionsActivity.this,permissionsRequired[2])){
                txtPermissions.setText("Permissions Required");
                AlertDialog.Builder builder = new AlertDialog.Builder(MultiplePermissionsActivity.this);
                builder.setTitle("Need Multiple Permissions");
                builder.setMessage("This app needs Camera and Location permissions.");
                builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                        ActivityCompat.requestPermissions(MultiplePermissionsActivity.this,permissionsRequired,PERMISSION_CALLBACK_CONSTANT);
                    }
                });
                builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
                builder.show();
            } else {
                Toast.makeText(getBaseContext(),"Unable to get Permission",Toast.LENGTH_LONG).show();
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_PERMISSION_SETTING) {
            if (ActivityCompat.checkSelfPermission(MultiplePermissionsActivity.this, permissionsRequired[0]) == PackageManager.PERMISSION_GRANTED) {
                //Got Permission
                proceedAfterPermission();
            }
        }
    }

    private void proceedAfterPermission() {
        txtPermissions.setText("We've got all permissions");
        Toast.makeText(getBaseContext(), "We got All Permissions", Toast.LENGTH_LONG).show();
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();
        if (sentToSettings) {
            if (ActivityCompat.checkSelfPermission(MultiplePermissionsActivity.this, permissionsRequired[0]) == PackageManager.PERMISSION_GRANTED) {
                //Got Permission
                proceedAfterPermission();
            }
        }
    }
}

What we’ve done here is that created and string array with required permissions. While checking for permissions, checked by all elements of the array. The only exception is in while checking with SharedPreferences, here we’ve checked with only the first string, the reason is that for storing shared preferences on string is enough and only one shared preference can tell us whether we’ve already requested for permissions or not (as we are requesting for all permissions in one go).

In the onRequestPermissionsResult() method we used a for loop to determine whether all permissions are granted or not. If a single permission is not granted then we will not proceed further.

android-m-requesting-multiple-permissions-1

3.3 Requesting Permission From Fragment

We will now move forward to requesting permission from Fragments. Now let us take a different approach. We will request for Phone State permission (we will actually do nothing with the permissions, we just ask for the permissions and will show the permission we’ve got).

11. Create a new Activity and name it as FragmentPermissionActivity.java. Fill the required details and check Use a Fragment.

12. Initialize btnLaunchPermissionFragment inside oncreate on MainActivity.java, and implement onClickListener to open FragmentPermissionActivity on click.

Button btnLaunchPermissionFragment = (Button) findViewById(R.id.btnLaunchPermissionFragment);
btnLaunchPermissionFragment.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
       Intent intent = new Intent(MainActivity.this, FragmentPermissionActivity.class);
       startActivity(intent);
   }
});

13. Add below permission to AndroidManifest.xml

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

14. Open PermissionsFragment.java and modify the code as below.

package info.androidhive.mpermissions;

import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;


import org.jetbrains.annotations.Nullable;


/**
* A placeholder fragment containing a simple view.
*/
public class PermissionsFragment extends Fragment {


   private static final int PERMISSION_CALLBACK_CONSTANT = 101;
   private static final int REQUEST_PERMISSION_SETTING = 102;
   private View view;
   private TextView txtPermissions;
   private Button btnCheckPermissions;


  
   private SharedPreferences permissionStatus;
   private boolean sentToSettings = false;


   public PermissionsFragment() {
   }


   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container,
                            Bundle savedInstanceState) {
       return view = inflater.inflate(R.layout.fragment_permission, container, false);
   }


   @Override
   public void onActivityCreated(@Nullable Bundle savedInstanceState) {
       super.onActivityCreated(savedInstanceState);


       permissionStatus = getActivity().getSharedPreferences("permissionStatus",getActivity().MODE_PRIVATE);


       if(null != view){
           txtPermissions = (TextView) view.findViewById(R.id.txtPermissions);
           btnCheckPermissions = (Button) view.findViewById(R.id.btnCheckPermissions);


           btnCheckPermissions.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                   if(ActivityCompat.checkSelfPermission(getActivity(),Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED){
                       if(ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),Manifest.permission.READ_PHONE_STATE)){
                           //Show Information about why you need the permission
                           AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
                           builder.setTitle("Need Permission");
                           builder.setMessage("This app needs phone permission.");
                           builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                               @Override
                               public void onClick(DialogInterface dialog, int which) {
                                   dialog.cancel();
                                   requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE},PERMISSION_CALLBACK_CONSTANT);
                               }
                           });
                           builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                               @Override
                               public void onClick(DialogInterface dialog, int which) {
                                   dialog.cancel();
                               }
                           });
                           builder.show();
                       } else if (permissionStatus.getBoolean(Manifest.permission.READ_PHONE_STATE,false)) {
                           //Previously Permission Request was cancelled with 'Dont Ask Again',
                           // Redirect to Settings after showing Information about why you need the permission
                           AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
                           builder.setTitle("Need Permission");
                           builder.setMessage("This app needs storage permission.");
                           builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                               @Override
                               public void onClick(DialogInterface dialog, int which) {
                                   dialog.cancel();
                                   sentToSettings = true;
                                   Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                   Uri uri = Uri.fromParts("package", getActivity().getPackageName(), null);
                                   intent.setData(uri);
                                   startActivityForResult(intent, REQUEST_PERMISSION_SETTING);
                                   Toast.makeText(getActivity(), "Go to Permissions to Grant Phone", Toast.LENGTH_LONG).show();
                               }
                           });
                           builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                               @Override
                               public void onClick(DialogInterface dialog, int which) {
                                   dialog.cancel();
                               }
                           });
                           builder.show();
                       }  else {
                           //just request the permission
                           requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE},PERMISSION_CALLBACK_CONSTANT);
                       }
                       txtPermissions.setText("Permissions Required");


                       SharedPreferences.Editor editor = permissionStatus.edit();
                       editor.putBoolean(Manifest.permission.READ_PHONE_STATE,true);
                       editor.commit();
                   } else {
                       //You already have the permission, just go ahead.
                       proceedAfterPermission();
                   }
               }
           });
       }
   }


   private void proceedAfterPermission() {
       txtPermissions.setText("We've got all permissions");
       Toast.makeText(getActivity(), "We got All Permissions", Toast.LENGTH_LONG).show();
   }


   @Override
   public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
       super.onRequestPermissionsResult(requestCode, permissions, grantResults);
       if(requestCode == PERMISSION_CALLBACK_CONSTANT){
           //check if all permissions are granted
           boolean allgranted = false;
           for(int i=0;i<grantResults.length;i++){
               if(grantResults[i]==PackageManager.PERMISSION_GRANTED){
                   allgranted = true;
               } else {
                   allgranted = false;
                   break;
               }
           }


           if(allgranted){
               proceedAfterPermission();
           } else if(ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),Manifest.permission.READ_PHONE_STATE)){
               txtPermissions.setText("Permissions Required");
               AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
               builder.setTitle("Need Storage Permission");
               builder.setMessage("This app needs phone permission.");
               builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                   @Override
                   public void onClick(DialogInterface dialog, int which) {
                       dialog.cancel();
                       requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE},PERMISSION_CALLBACK_CONSTANT);
                   }
               });
               builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                   @Override
                   public void onClick(DialogInterface dialog, int which) {
                       dialog.cancel();
                   }
               });
               builder.show();
           } else {
               Toast.makeText(getActivity(),"Unable to get Permission",Toast.LENGTH_LONG).show();
           }
       }
   }


   @Override
   public void onActivityResult(int requestCode, int resultCode, Intent data) {
       super.onActivityResult(requestCode, resultCode, data);
       if (requestCode == REQUEST_PERMISSION_SETTING) {
           if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
               //Got Permission
               proceedAfterPermission();
           }
       }
   }


   @Override
   public void onResume() {
       super.onResume();


       if (sentToSettings) {
           if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
               //Got Permission
               proceedAfterPermission();
           }
       }
   }
}
android-m-permissions-requesting-in-fragment

So everything here is almost same when you request for permission from fragment or activity, except for that you should not use ActivityCompat.requestPermissions when requesting from Fragment instead use the inbuilt method of fragment.

I hope this article gave you very good overview of Marshmallow permission model. Feel free to ask any queries / doubts in comments section below.

Rivu Chakraborty is a Sr. Tech. Member of Institution of Engineers(India), Diploma holder Engineer in Computer Science. He is presently at MaxMobility Pvt. Ltd. as a Sr. Software Engineer (Android)
  • Hai Rivu Chakraborty,
    Thanks for detailed guide.

    But I can see, In some apps like Snapchat, TubeMate all permissions including dangerous permissions are enabled by default even in Android M. How they are doing it ?

    • Just change your target SDK to below 23 (say 22). As runtime permissions was introduced in Android M which has SDK int 23, any app which is targeting below this will automatically grant all the permissions. Google play will show you the dialog with all the requested permissions.

      But this method is not safe now because user can revoke permissions anytime from device Settings on Android M or above devices which can make those apps unstable. So, it’s better to check for permissions every time.

      • Is there any other down sides on setting target SDK less than 23 ?
        At the least it will prevent annoying the users after installing the app.

        Anyhow, we’ll check for permission status in the code so, it will ask user to enable the permission only if user disables them manually.

        • The main reason of runtime permissions was to grant only those permissions which users are actually using and when required. If all the permissions are necessary for your app, you can ask them in one go by using multiple permissions as explained above.

          Still, you can do experiments with your idea if it is working fine for you.

    • If the device is running Android 5.1 Lollipop (API level 22) or lower, or the app’s targetSdkVersion is 22 or lower, the system asks the user to grant the permissions in a group which the app needs at install time, not the individual permissions. http://gadgetcreek.com/android-working-runtime-permissions-request/

  • AbelardoLG

    Hi Rivu,

    Which software did you use to make the video? Thanks in advance.
    Best regards.

  • Anonymous

    Hey this is the code someone gave you, right?
    As far as I can recollect! :”)
    The code is way too familiar.

    Naaice usage of other’s code! Keep up the good work! :’)

  • Neha Sharma

    While I am trying to download your source code…Login with google isn’t working…why?

    • I have to check. Have you tried other logins?

      • Neha Sharma

        yeah…Login with linkedin Its working

      • waxi

        only linkedin working

  • Hi, it’s a good tutorial, but i need help, when i try onRequestPermissionsResult in my listview adapter, it is not being called. it is given me this error ” method does not override method from its superclass “.
    Please help me.

    • Implement this code in activity or fragment instead of doing it in adapter.

      • Thanx for your reply, but i have buttons in my listview which need permission for write_external_storage, how is it possible in its parent activity ?

        • Write an interface in adapter class and pass the click to parent activity. Here is a simple example.


          // In adapter class add the interface
          private MyAdapterClassListener listener;

          public MyAdapterClass(MyAdapterClassListener listener){
          this.listener = listener;
          }

          /// in button click event
          if(listener != null){
          listener.onButtonClicked();
          }

          public interface MyAdapterClassListener {
          void onButtonClicked();
          }

          // In you activity implement the class from the interface and override the onButtonClick method.

          http://stackoverflow.com/questions/15444375/how-to-create-interface-between-fragment-and-adapter

          • thank you…i will test it.

          • Hi,
            I tried it, everything in adapter looks fine but when i implement it in my activity it says it Class must either be declared abstract or implement abstract method.

  • Everything is clean and clear, but I’m getting some errors have to fix it. Thanks for the tutorial

  • Jayanth

    Why you used onPostResume() here ?

  • Neha Sharma
    • As of now no direct way.

      • Neha Sharma

        May Be.. I have searched a lot but found no solution

        • Adam Gardner

          Could make a PreferenceFragment or PreferenceActivity to create that screen inside the app, then make that your Main and Launcher inside the Manifest.

    • Sumit Patel

      Intent intent = new Intent();
      intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
      Uri uri = Uri.fromParts(“package”, getPackageName(), null);
      intent.setData(uri);
      startActivity(intent);

  • Am asking, do I keep checking for permission in each and every activity or just in main activity. Okay, I understand if user gives permission, that will be enough through out the app. My worry is, if user rejects permission, that means I have to keep checking for permission every time user need’s operation associated with that particular permission. How do I do this, or I have to keep repeating the whole process of checking for permission in all activities?? Am confused here

    • You can keep the permissions related code in a class and use it in other places. Yes, you have to check for the permissions in each and every activity.

  • Nirjan Munshi

    Suppose in my project I am using Welcome Screen with animation. When animation ends then it will proceed to next activity. If I implement this permission code inside onStart method and animation codes inside “proceedAfterPermission()” method then application is working fine but animation never ends for the first time. i.e. next activity is not opening. I have to restart my application to open the next activity.

    Again If I check the permission inside onAnimationEnd() method, same problem again. My question is how to execute an event just after granting the permission…

  • syed shahid

    I want to add run time permission for the following code which give me option to add permission check plz help me

    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 0, this);

  • Ken Choong

    This help me..It works..thanks

  • Ajay K

    Can you provide the link to download the sample you have created? Thanks

    • The download button is there already at the beginning of the article.

  • can tell me how to hide btn after permissions is granted?

  • mani

    waste Still, you can do experiments with your idea if it is working fine for you.

  • mani

    pls doo some useful works

  • @rpit Patel

    Nice artical cover all scenario I like your tutorial but I am wondering why you use onPostResume method? Can you explain me bit??

  • Neha Agarwal

    Hi

    Everything is working fine for me but alert dialog is not dismissing by itself even after granting permissions?

  • development techlab

    Screen overlay Detected in Android M.
    how can i bypass it?

  • test

    alert(“dadaddadada”);

    • Use <pre> tag when you are posting code 🙂

      alert(“dadaddadada”);