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 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
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.
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.
| Android Version | Change in Permission Model |
|---|---|
| Android 1.0–5.x | All 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) |
Android defines four protection levels for permissions, each with its own grant rules. Let’s review each level in detail.
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 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.
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.
Since Android 6.0, all dangerous permissions require runtime requests. Let’s walk through the full runtime permission lifecycle in Kotlin.
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.
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
)
}
}
}
}
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.
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()
}
The AndroidManifest.xml file contains the
Each permission is declared with a separate
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.
<!-- 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>
The
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).
Proper permission handling is a key aspect of Android app quality. Let’s review the main recommendations and typical mistakes.
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.
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 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.
Frequently Asked Questions
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.
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.
In device settings: Settings → Apps → [your app] → Permissions. To reset all permissions, use the adb command: adb shell pm reset-permissions.
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.
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
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.
Read also