Managing Permissions
1. Declaring Permissions in Manifest
| Permission | Group | Description | Protection Level |
|---|---|---|---|
| android.permission.ACCESS_COARSE_LOCATION | Location | Allows an app to access approximate location | Normal |
| android.permission.ACCESS_FINE_LOCATION | Location | Allows an app to access precise location | Signature |
| android.permission.CAMERA | Camera | Allows an app to open camera | Normal |
| android.permission.READ_CONTACTS | Contacts | Allows an app to read the user's contacts | Normal |
| android.permission.RECORD_AUDIO | Microphone | Allows an app to record audio | Normal |
| android.permission.WRITE_EXTERNAL_STORAGE | Storage | Allows an app to write to external storage | Normal |
Example: Declaring Permissions in AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<application ... >
...
</application>
</manifest>
2. Requesting Runtime Permissions
| Permission | Type | Description | Group |
|---|---|---|---|
| android.permission.CAMERA | Normal | Allows access to camera hardware | Camera |
| android.permission.ACCESS_FINE_LOCATION | Signature | Allows access to precise location data | Location |
| android.permission.READ_CONTACTS | Normal | Allows reading contact data | Contacts |
| android.permission.RECORD_AUDIO | Normal | Allows recording audio | Microphone |
Example: Requesting Camera Permission
import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
class MainActivity : AppCompatActivity() {
private val CAMERA_PERMISSION_CODE = 100
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA),
CAMERA_PERMISSION_CODE
)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == CAMERA_PERMISSION_CODE) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted, proceed with camera usage
} else {
// Permission denied, handle accordingly
}
}
}
}
Note: Runtime permissions are only required for dangerous permissions. Normal permissions are automatically granted.
Warning: If a user denies a permission, you should provide clear feedback and explain why the permission is needed.
3. Checking Permission Status
| Permission | Status | Description |
|---|---|---|
| android.permission.CAMERA | PackageManager.PERMISSION_GRANTED |
User has granted camera access |
| android.permission.ACCESS_FINE_LOCATION | PackageManager.PERMISSION_GRANTED |
User has granted precise location access |
| android.permission.READ_CONTACTS | PackageManager.PERMISSION_GRANTED |
User has granted contact read access |
Example: Check Camera Permission
val cameraPermission = "android.permission.CAMERA"
val permissionStatus = ContextCompat.checkSelfPermission(this, cameraPermission)
if (permissionStatus == PackageManager.PERMISSION_GRANTED) {
// Proceed with camera usage
} else {
// Request permission or show rationale
}
Note: Runtime permissions must be checked before using sensitive APIs.
4. Handling Permission Results
| Permission | Status | Description |
|---|---|---|
| android.permission.CAMERA | NEW GRANTED | Allows access to camera hardware |
| android.permission.ACCESS_FINE_LOCATION | DENIED | User denied location access |
| android.permission.READ_CONTACTS | GRANTED | Read contact list from device |
Example: Requesting Camera Permission
class MainActivity : AppCompatActivity() {
private val CAMERA_PERMISSION = Manifest.permission.CAMERA
private val PERMISSION_REQUEST_CODE = 100
fun requestCameraPermission() {
if (ContextCompat.checkSelfPermission(this, CAMERA_PERMISSION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(CAMERA_PERMISSION), PERMISSION_REQUEST_CODE)
} else {
// Permission already granted
openCamera()
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
PERMISSION_REQUEST_CODE -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openCamera()
} else {
// Handle denied permission
showPermissionDeniedMessage()
}
}
}
}
private fun openCamera() {
// Launch camera intent
}
private fun showPermissionDeniedMessage() {
Toast.makeText(this, "Camera permission required", Toast.LENGTH_SHORT).show()
}
}
Note: Always check for permission status before using sensitive APIs. Handle both
GRANTED and DENIED cases.
Warning: If a user permanently denies a permission, they must manually enable it in Settings.
Permission Handling Workflow
- Check if permission is already granted
- If not, request permission using
ActivityCompat.requestPermissions() - Override
onRequestPermissionsResultto handle result - Act based on permission status (grant or deny)
5. Using Permission Request Contracts
| Permission | Contract Type | Description |
|---|---|---|
| android.permission.CAMERA | ActivityResultContract |
Requests camera access for capturing photos or videos. |
| android.permission.ACCESS_FINE_LOCATION | ActivityResultContract |
Requests permission to access precise location data. |
| android.permission.READ_CONTACTS | ActivityResultContract |
Requests permission to read user's contacts. |
Example: Requesting Camera Permission
class CameraPermissionContract : ActivityResultContract<Unit, Boolean>() {
override fun createIntent(context: Context, input: Unit): Intent {
return Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.parse("package:" + context.packageName)
}
}
override fun parseResult(resultCode: Int, intent: Intent?): Boolean {
return resultCode == Activity.RESULT_OK
}
}
val cameraPermissionLauncher = registerForActivityResult(
CameraPermissionContract()
) { granted ->
if (granted) {
// Proceed with camera usage
} else {
// Handle denied permission
}
}
// Launch the permission request
cameraPermissionLauncher.launch(Unit)
Note: The
ActivityResultContract approach is preferred for modern Android development as it simplifies handling permissions and results.
6. Understanding Permission Groups
| Permission Group | Description | Permissions Included | Protection Level |
|---|---|---|---|
| android.permission-group.CALENDAR | Access to calendar events and reminders | READ_CALENDAR, WRITE_CALENDAR |
NEW Dangerous |
| android.permission-group.CAMERA | Access to camera functionality | CAMERA |
Dangerous |
| android.permission-group.CONTACTS | Access to contacts and phone numbers | READ_CONTACTS, WRITE_CONTACTS, GET_ACCOUNTS |
Dangerous |
| android.permission-group.LOCATION | Access to device location | ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION |
Dangerous |
| android.permission-group.MICROPHONE | Access to microphone for audio recording | RECORD_AUDIO |
Dangerous |
| android.permission-group.PHONE | Access to phone functionality | READ_PHONE_STATE, CALL_PHONE, READ_CALL_LOG |
Dangerous |
| android.permission-group.SENSORS | Access to sensor data (e.g., heart rate) | BODY_SENSORS |
Dangerous |
| android.permission-group.STORAGE | Access to external storage | READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE |
Dangerous |
| android.permission-group.SYSTEM_ALERT_WINDOW | Draw over other apps | SYSTEM_ALERT_WINDOW |
DEPRECATED since API 23 |
| android.permission-group.UNDEFINED | Permissions not assigned to a group | ACCESS_NETWORK_STATE, INTERNET |
Normal |
Note: Beginning with Android 6.0 (API level 23), users grant permissions at runtime for dangerous permissions, including those in permission groups.
7. Handling Rationale UI
Example: Requesting Permission with Rationale UI
class MainActivity : AppCompatActivity() {
private val PERMISSION_REQUEST_CODE = 100
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
// Permission already granted
setupCamera()
} else {
// Request permission with rationale
requestPermissions(
arrayOf(Manifest.permission.CAMERA),
PERMISSION_REQUEST_CODE
)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
setupCamera()
} else {
// Show rationale if permission was denied
showRationaleDialog()
}
}
}
private fun showRationaleDialog() {
AlertDialog.Builder(this)
.setTitle("Camera Permission Required")
.setMessage("This app needs camera access to function properly. Please allow this permission in the settings.")
.setPositiveButton("Go to Settings") { _, _ ->
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", packageName, null)
intent.data = uri
startActivity(intent)
}
.setNegativeButton("Cancel") { dialog, _ ->
dialog.dismiss()
}
.show()
}
private fun setupCamera() {
// Camera setup logic here
}
}
Note: The rationale UI should explain why the permission is necessary and guide the user to enable it in settings if they previously denied it.
| Permission | Required | Description | Rationale UI |
|---|---|---|---|
| CAMERA | true |
Allows access to camera hardware | Yes, for user guidance |
| READ_CONTACTS | false |
Allows reading contact data | Yes, for user guidance |
| ACCESS_FINE_LOCATION | true |
Allows access to precise location | Yes, for user guidance |
Workflow: Handling Permission Requests with Rationale
- Check if permission is already granted using
checkSelfPermission - If not granted, request permission using
requestPermissions - Override
onRequestPermissionsResultto handle the result - If permission is denied, show rationale UI explaining why it's needed
- Provide option to navigate to app settings for manual permission enable
Comparison: Permission Request Methods
- requestPermissions: Direct permission request with rationale support
- shouldShowRequestPermissionRationale: Determines if rationale should be shown
- ActivityCompat.requestPermissions: Backward compatible method for older Android versions
8. Working with Special Permissions
| Permission | Description | Protection Level | Notes |
|---|---|---|---|
| android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS | Allows an app to bypass battery optimizations for itself. | NEW API 23 | Used for apps that need to run in background without battery restrictions. Requires user confirmation. |
| android.permission.ACCESS_NOTIFICATION_POLICY | Allows an app to access the notification policy settings. | NEW API 23 | Used to control notification behavior, such as muting or silencing. |
| android.permission.SYSTEM_ALERT_WINDOW | Allows an app to draw over other apps or system UI. | NEW API 23 | Used for overlay windows like floating action buttons or system overlays. Requires user permission. |
| android.permission.WRITE_SETTINGS | Allows an app to modify system settings. | DEPRECATED API 23 | Replaced by android.permission.WRITE_SECURE_SETTINGS for secure settings. Requires special permissions. |
Example: Requesting Special Permissions
val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS)
intent.data = Uri.parse("package:$packageName")
startActivity(intent)
Note: Special permissions often require the user to manually grant them through system settings or a special permission screen.
Warning: Apps requesting
SYSTEM_ALERT_WINDOW must comply with Google Play policies and may be rejected if misused.
9. Managing Location Permissions
| Permission | Level | Description | Usage |
|---|---|---|---|
| ACCESS_FINE_LOCATION | NEW | Allows app to access precise location | Required for GPS, Wi-Fi, and cell tower based location |
| ACCESS_COARSE_LOCATION | Standard | Allows app to access approximate location | Useful for less precise location services like city-level |
Example: Requesting Location Permissions
val LOCATION_PERMISSIONS = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
// Request permissions at runtime
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, LOCATION_PERMISSIONS, LOCATION_REQUEST_CODE)
}
Example: Handling Permission Results
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
when (requestCode) {
LOCATION_REQUEST_CODE -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted, proceed with location access
getCurrentLocation()
} else {
// Permission denied, show rationale or disable feature
Toast.makeText(this, "Location permission required", Toast.LENGTH_SHORT).show()
}
}
}
}
Note: Starting from Android 6.0 (API level 23), you must request permissions at runtime, even if they are declared in the manifest.
Warning: Users can deny permissions or revoke them later. Always check permission status before using location services.
10. Handling Permission Denials
| Permission | Status | Description | Handling Strategy |
|---|---|---|---|
| android.permission.CAMERA | DEPRECATED | Access to camera hardware | Use runtime checks and user education for re-requesting |
| android.permission.ACCESS_FINE_LOCATION | Required for accurate location | Access to precise GPS data | Implement rationale dialog before requesting again |
| android.permission.READ_CONTACTS | Read-only access to contacts | Access to user's contact list | Provide clear explanation of why contacts are needed |
Example: Requesting Permissions with Rationale
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
// Show an explanation to the user
AlertDialog.Builder(this)
.setTitle("Location Permission Required")
.setMessage("This app needs access to your location to function properly.")
.setPositiveButton("Allow") { _, _ ->
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), LOCATION_PERMISSION_REQUEST_CODE)
}
.setNegativeButton("Deny") { _, _ ->
// Handle denial
}
.show()
} else {
// No explanation needed, request the permission
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), LOCATION_PERMISSION_REQUEST_CODE)
}
}
Note: When a user denies a permission, it's important to provide context about why the permission is necessary for your app to function correctly.
Warning: If a user permanently denies a permission, you cannot request it again without directing them to the app settings.
11. Using Photo Picker
| Property | Description | Values |
|---|---|---|
| PhotoPicker | API for selecting photos from device | PhotoPicker |
| Intent | Used to launch the photo picker | Intent(Intent.ACTION_PICK) |
| ContentResolver | Used to resolve the selected image URI | contentResolver |
Example: Launching Photo Picker
val intent = Intent(Intent.ACTION_PICK).apply {
type = "image/*"
}
startActivityForResult(intent, PICK_IMAGE_REQUEST)
Example: Handling Selected Image
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK) {
val selectedImageUri = data?.data
// Use the URI to load or process image
}
}
Note: PhotoPicker is available from Android 13 (API level 33) and higher.