AndroidManifest Permissions: Key Concepts, Declaration and Types of Permissions

Author: IT Sectr Published: 2026-05-21 Reading time: 10 min

AndroidManifest Permissions are declarations of permissions in the AndroidManifest.xml file that determine which system resources and data an application can access. Android requires each permission to be declared in the manifest before using the corresponding API: from camera and geolocation to sending SMS and accessing contacts. According to Android Developer Documentation, each permission falls into one of four protection levels: normal, dangerous, signature, and special.

Key Takeaways

  • AndroidManifest.xml — the manifest file declaring all application permissions
  • Protection levels — normal, dangerous, signature, special with different request mechanisms
  • Runtime Permission — dangerous permissions require runtime requests (Android 6+)
  • Declare vs Request — declaration in the manifest is mandatory, but dangerous ones need an additional runtime request
  • Groups — permissions are grouped; consent to one grants access to the entire group

What Are AndroidManifest Permissions?

AndroidManifest Permissions are Android’s security mechanism that controls application access to protected data and system functions. Every application must declare the required permissions in the AndroidManifest.xml file using the element. Without declaration, calling the corresponding API will result in a SecurityException.

The Android permission model has evolved through several stages. Before Android 6.0 (API 23), all permissions were granted at install time — users saw the full list and either agreed or declined the app installation. Starting with Android 6.0, dangerous-level permissions are requested at runtime (Runtime Permissions), giving users more flexible control.

Permissions are divided into four protection levels: normal (automatically granted at install time), dangerous (require runtime request), signature (only available to apps signed with the same certificate), and special (require separate activation in settings). Each level has its own grant and revocation mechanism.

According to Google I/O 2024, Android 15 plans to introduce more granular permissions — users will be able to grant access only to specific files in the media library rather than the entire collection. This continues Android’s trend toward minimizing the amount of data provided by default.

Difference from iOS Permission Model

Unlike iOS, where all permissions are requested at runtime, Android divides permissions into install-time and runtime types. The normal level is granted automatically at install time without user notification. The dangerous level requires an explicit dialog, similar to iOS.

Another difference: in Android, permissions are grouped into permission groups. If a user grants camera access, the app automatically gets microphone access — they are in the same MICROPHONE group. In iOS, each permission is requested independently regardless of groups.

Permission Evolution Across Android Versions

Android VersionChange in Permission Model
Android 1.0–5.xAll permissions granted at install time
Android 6.0 (API 23)Introduction of Runtime Permissions for dangerous level
Android 10 (API 29)Scoped Storage — limited file system access
Android 11 (API 30)Auto-reset permissions — unused permissions are reset
Android 14 (API 34)Runtime permissions for media access (photo, video, audio)

What Permission Types Exist

Android defines four protection levels for permissions, each with its own grant rules. Let’s review each level in detail.

Normal Permissions (install-time)

Normal permissions are granted automatically at app installation without user notification or request. They cover low-risk functions that do not threaten user privacy: INTERNET, ACCESS_NETWORK_STATE, VIBRATE, BLUETOOTH. Users do not see a consent dialog — the permission is considered granted upon installation.

Developers do not need to handle runtime requests for normal permissions — declaring them in the manifest is sufficient. However, on Android 12+, when installing from Google Play, users see a “Permissions” tab listing all normal permissions, increasing transparency. According to Statista (2024), over 90% of apps on Google Play use INTERNET as the most common normal permission.

Dangerous Permissions (runtime)

Dangerous permissions cover access to data and functions that may compromise privacy: camera, microphone, geolocation, contacts, SMS, phone, calendar, body sensors. These permissions require a two-step mechanism: declaration in the manifest + runtime request via ActivityCompat.requestPermissions().

Users may deny a dangerous permission, and the app must handle this scenario gracefully. On Android 11+, if the user denies twice, subsequent requests do not show the system dialog — the system automatically returns DENIED. In this case, the app should direct the user to settings.

Signature and Special Permissions

The signature level — the permission is granted automatically if the app is signed with the same certificate as the system or another app that defined the permission. Used for system and enterprise apps. Example: BIND_ACCESSIBILITY_SERVICE — only available to system apps.

The special level (SYSTEM_ALERT_WINDOW, WRITE_SETTINGS, REQUEST_INSTALL_PACKAGES, MANAGE_EXTERNAL_STORAGE) — requires explicit user action through system settings. The app can open the settings page using Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION). Google Play restricts the use of special permissions and requires justification in the form during publishing.

Runtime Permission: Working with Dangerous Permissions

Since Android 6.0, all dangerous permissions require runtime requests. Let’s walk through the full runtime permission lifecycle in Kotlin.

Checking and Requesting Permissions

Before calling an API that requires a dangerous permission, always check the current status via ContextCompat.checkSelfPermission(). If the status is PERMISSION_GRANTED, you can call the API. If PERMISSION_DENIED, you need to request the permission via the ActivityResultContract RequestPermission (AndroidX) or the deprecated requestPermissions().

It is recommended to use ActivityResultContracts.RequestMultiplePermissions to request multiple permissions at once. Google recommends grouping related permissions (e.g., camera + microphone for video recording) into one dialog so the user sees the full request context.

kotlin
import android.Manifest
import android.content.pm.PackageManager
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat

class CameraActivity : AppCompatActivity() {
    private val requestPermissionLauncher =
        registerForActivityResult(
            ActivityResultContracts.RequestPermission()
        ) { isGranted: Boolean ->
            if (isGranted) {
                openCamera()
            } else {
                explainWhyPermissionNeeded()
            }
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        checkCameraPermission()
    }

    private fun checkCameraPermission() {
        when {
            ContextCompat.checkSelfPermission(this,
                Manifest.permission.CAMERA
            ) == PackageManager.PERMISSION_GRANTED -> {
                openCamera()
            }
            shouldShowRequestPermissionRationale(
                Manifest.permission.CAMERA
            ) -> {
                showRationale()
            }
            else -> {
                requestPermissionLauncher.launch(
                    Manifest.permission.CAMERA
                )
            }
        }
    }
}

Handling “Don’t Ask Again” Rejection

If a user denies twice, Android transitions the request to a “Never ask again” state. In this case, shouldShowRequestPermissionRationale() returns false, and the system dialog will not be shown. The app should direct the user to system settings via Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).

Important: do not show a dialog offering to open settings immediately after the first denial — this is perceived as aggressive behavior. Use shouldShowRequestPermissionRationale() to determine whether an explanation is needed. Material Design Guidelines recommend showing a screen explaining the value of access, not just an “Open settings” button.

kotlin
private fun openAppSettings() {
    Intent(
        Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
        Uri.fromParts("package", packageName, null)
    ).also { intent ->
        startActivity(intent)
    }
}

private fun showPermissionSettings() {
    AlertDialog.Builder(this)
        .setTitle("Camera access")
        .setMessage("Allow camera access in Settings, "
            + "to take profile photos")
        .setPositiveButton("Open settings") { _, _ ->
            openAppSettings()
        }
        .setNegativeButton("Cancel", null)
        .show()
}

Declaring Permissions in AndroidManifest.xml

The AndroidManifest.xml file contains the element for each permission the app uses. Permissions are declared at the level before the element.

Declaration Syntax

Each permission is declared with a separate element using the android:name attribute specifying the full permission name. For permissions introduced in specific Android versions, use the maxSdkVersion attribute to limit the declaration to only the needed versions — this improves compatibility.

For example, the WRITE_EXTERNAL_STORAGE permission is not needed on Android 10+ (Scoped Storage), so specify maxSdkVersion="28" (Android 9). This prevents unnecessary questions from users on newer versions. Android Studio warns about recommended maxSdkVersion via Lint.

xml
<!-- AndroidManifest.xml -->
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Normal permissions (install-time) -->
    <uses-permission
        android:name="android.permission.INTERNET" />
    <uses-permission
        android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!-- Dangerous permissions (runtime) -->
    <uses-permission
        android:name="android.permission.CAMERA" />
    <uses-permission
        android:name="android.permission.ACCESS_FINE_LOCATION" />

    <!-- Legacy storage permission limited to API 28 and below -->
    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="28" />

    <uses-feature
        android:name="android.hardware.camera"
        android:required="false" />

    <application>
        <!-- ... -->
    </application>
</manifest>

Using for Filtering

The element indicates that the app requires specific hardware (camera, GPS, NFC). The android:required="false" attribute allows the app to be installed on devices without that hardware — availability is checked in code. If required="true", Google Play filters the app, making it unavailable for unsuitable devices.

It is recommended to set required="false" for all hardware features and check availability programmatically via PackageManager.hasSystemFeature(). This expands your app’s audience. The only exception is if the feature is critical for the app’s functionality (a taxi app without GPS makes no sense).

Best Practices and Common Mistakes

Proper permission handling is a key aspect of Android app quality. Let’s review the main recommendations and typical mistakes.

Minimize Requested Permissions

Request only the permissions truly necessary for the app to function. Every extra permission reduces install conversion and increases denial rates. Google Play Console shows how many users declined installation due to the permission set. According to AppBrain (2024), apps with 10+ dangerous permissions have 35% fewer installs.

Regularly review your permission list. Remove unused ones, especially when migrating to newer Android versions where some permissions become optional. For example, with the photo picker (ActivityResultContracts.PickVisualMedia) in Android 13+, media library access can be obtained without the dangerous READ_MEDIA_IMAGES permission.

Show Rationale Before Requesting

Before requesting a dangerous permission, show the user a screen explaining why the permission is needed and what value it provides. Material Design recommends using a bottom sheet or dialog with an icon, brief text, and a “Continue” button. Rationale increases consent by 20–30% compared to a direct request.

Check shouldShowRequestPermissionRationale() before calling launch(). If true, show the rationale. If false, either the permission is already granted or the user has permanently denied it (never ask again). In the latter case, show an “Open settings” button rather than repeating the request.

Test All Permission Scenarios

Test all possible scenarios: granting permission, denial, permanent denial, revoking permission in settings, permission reset (Android 11+ auto-reset). Every scenario should be handled without crashes or data loss. Android Testing Guide recommends using the TestPermission library to automate testing.

Pay special attention to the scenario where the user revokes a permission while the app is running (app minimized → Settings → revoke). Upon returning to the app, recheck all permissions in onResume(). Do not rely on caching permission status — users can change it at any time.

Common Mistakes

  • Requesting a permission without first checking checkSelfPermission — triggers an unnecessary dialog
  • Ignoring shouldShowRequestPermissionRationale — worsens UX after the first denial
  • Requesting permission without context (just “Allow access?”) — reduces consent
  • Using WRITE_EXTERNAL_STORAGE on Android 10+ without maxSdkVersion — unnecessary request
  • Missing permission checks in onResume — misses permission revocation in settings

Frequently Asked Questions

Do I need to declare a permission if an SDK requests it?

Yes, if an SDK includes a permission in its manifest, it merges with the app manifest at build time. You can remove an unnecessary SDK permission using tools:node="remove" in AndroidManifest.xml.

What happens if I don’t handle permission denial?

Calling an API without permission throws a SecurityException, causing the app to crash. Always check permission status before using the corresponding API and handle denial gracefully.

How do I reset permissions during development?

In device settings: Settings → Apps → [your app] → Permissions. To reset all permissions, use the adb command: adb shell pm reset-permissions.

Can I request a permission without an Activity?

Yes, using ActivityResultLauncher in a Fragment or Service. However, the request dialog always requires an Activity UI context. For a Service, you can show a Notification with an Intent opening the request Activity.

Why is maxSdkVersion needed for permissions?

For example, WRITE_EXTERNAL_STORAGE is not needed on Android 10+ (Scoped Storage). By specifying android:maxSdkVersion="28", you exclude the permission declaration on newer versions, improving compatibility and reducing the requested permission list.

Summary

  • AndroidManifest Permissions — mandatory declarations for accessing system resources in Android
  • 4 protection levels — normal, dangerous, signature, special with different grant mechanisms
  • Runtime Permissions — dangerous permissions require runtime requests (Android 6+)
  • Permission groups — consent to one permission in a group grants access to all in the group
  • Rationale — showing an explanation before requesting increases consent by 20–30%
  • Minimization — request only necessary permissions and use maxSdkVersion
  • Always check permission status before calling an API and handle all denial scenarios

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also