Introduction

Before Android 6.0 (API level 23), requesting permissions was straightforward. You simply added the required permissions to the AndroidManifest.xml configuration file. For example, if you needed four permissions, your manifest would look like this:

<!--Make a phone call-->
<uses-permission android:name="android.permission.CALL_PHONE" />
<!--Use the camera-->
<uses-permission android:name="android.permission.CAMERA" />
<!--Write to external storage-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--Read from external storage-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

However, starting from Android 6.0 (API level 23), this approach is no longer sufficient. This is because requesting permissions this way gives apps full access to all requested permissions as soon as they are installed, which can lead to privacy concerns. Thus, Android 6.0 introduced the requirement for dangerous permissions to be requested dynamically at runtime. Below is a list of these dangerous permissions categorized by permission groups:

Permission Group Permissions
CALENDAR • READ_CALENDAR
• WRITE_CALENDAR
CAMERA • CAMERA
CONTACTS • READ_CONTACTS
• WRITE_CONTACTS
• GET_ACCOUNTS
LOCATION • ACCESS_FINE_LOCATION
• ACCESS_COARSE_LOCATION
MICROPHONE • RECORD_AUDIO
PHONE • READ_PHONE_STATE
• CALL_PHONE
• READ_CALL_LOG
• WRITE_CALL_LOG
• ADD_VOICEMAIL
• USE_SIP
• PROCESS_OUTGOING_CALLS
SENSORS • BODY_SENSORS
SMS • SEND_SMS
• RECEIVE_SMS
• READ_SMS
• RECEIVE_WAP_PUSH
• RECEIVE_MMS
STORAGE • READ_EXTERNAL_STORAGE
• WRITE_EXTERNAL_STORAGE

To use these dangerous permissions, you must:
1. Declare them in AndroidManifest.xml.
2. Request them dynamically in your Java code.

This guide will explain how to request single and multiple dangerous permissions.

Dynamic Request for a Single Permission

Suppose your app needs to make phone calls, which is a dangerous permission.

Step 1: Declare the Permission in AndroidManifest.xml

<!--Make a phone call-->
<uses-permission android:name="android.permission.CALL_PHONE" />

Step 2: Implement the Dynamic Permission Request in Java Code

// Request a single permission
private void requestPermission() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)
            != PackageManager.PERMISSION_GRANTED) {
        // Request the permission (request code 1001 will be used in the callback)
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.CALL_PHONE}, 1001);
    } else {
        Toast.makeText(this, "You already have the permission to make a call", Toast.LENGTH_LONG).show();
    }
}

Step 3: Handle the Permission Request Result

// Callback when permission request results are received
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case 1001:
            // Check if the permission was granted
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(MainActivity.this, "Permission granted! You can now make calls.", Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(MainActivity.this, "Permission denied! Call functionality will not be available.", Toast.LENGTH_LONG).show();
            }
            break;
    }
}

Step 4: Add a Button to Trigger the Permission Request

In your onCreate method:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button requestPermissionBtn = (Button) findViewById(R.id.request_permission_btn);

    // Set click listener to trigger permission request
    requestPermissionBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            requestPermission();
        }
    });
}

Step 5: Layout XML (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    tools:context="com.yeyupiaoling.testpermission.MainActivity">

    <Button
        android:id="@+id/request_permission_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Request Single Permission" />
</LinearLayout>

Testing

When you first run the app, the permission dialog will appear. If the user grants permission, you can proceed with the phone call functionality. If not, the functionality will be disabled. Subsequent requests will not show the dialog again if the permission was already granted.

Dynamic Request for Multiple Permissions

For multiple permissions, you need to follow similar steps but manage a list of permissions.

Step 1: Declare All Permissions in AndroidManifest.xml

<!--Use the camera-->
<uses-permission android:name="android.permission.CAMERA" />
<!--Write to external storage-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--Read from external storage-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Note: WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE are in the same STORAGE group. If one is granted, the other is automatically granted, so no additional dialog will be shown.

Step 2: Request Multiple Permissions in Java Code

// Request multiple permissions
private void requestMultiplePermissions() {
    List<String> permissionList = new ArrayList<>();

    // Check each permission and add to the list if not granted
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
            != PackageManager.PERMISSION_GRANTED) {
        permissionList.add(Manifest.permission.CAMERA);
    }

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {
        permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
    }

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {
        permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE);
    }

    // If permissions are missing, request them all at once
    if (!permissionList.isEmpty()) {
        ActivityCompat.requestPermissions(this,
                permissionList.toArray(new String[permissionList.size()]), 1002);
    } else {
        Toast.makeText(this, "All permissions are already granted.", Toast.LENGTH_LONG).show();
    }
}

Step 3: Handle Multiple Permission Results

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case 1002:
            // Check each permission's result
            if (grantResults.length > 0) {
                for (int i = 0; i < grantResults.length; i++) {
                    if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
                        Toast.makeText(MainActivity.this, permissions[i] + " permission is denied.", Toast.LENGTH_SHORT).show();
                    }
                }
            }
            break;
    }
}

Step 4: Add a Button to Trigger Multiple Permission Request

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button requestPermissionsBtn = (Button) findViewById(R.id.request_permissions_btn);

    requestPermissionsBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            requestMultiplePermissions();
        }
    });
}

Step 5: Update Layout XML (Add the New Button)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    tools:context="com.yeyupiaoling.testpermission.MainActivity">

    <Button
        android:id="@+id/request_permission_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Request Single Permission" />

    <Button
        android:id="@+id/request_permissions_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Request Multiple Permissions" />
</LinearLayout>

Testing

When you click the “Request Multiple Permissions” button, you’ll see a dialog requesting all the missing permissions. If any permission is denied, you’ll be notified, and the app should handle it appropriately.

Conclusion

This guide has shown you how to handle both single and multiple dangerous permissions in Android. The key steps are:
1. Declare permissions in AndroidManifest.xml.
2. Dynamically request permissions at runtime using ActivityCompat.requestPermissions().
3. Handle the results in onRequestPermissionsResult().

For more details, refer to the official Android documentation: Android Permissions Guide.

Xiaoye