AndroidManifest Permissions son declaraciones de permisos en el archivo AndroidManifest.xml que determinan a qué recursos del sistema y datos tiene acceso una aplicación. Android requiere que cada permiso se declare en el manifiesto antes de usar la API correspondiente: desde la cámara y la geolocalización hasta el envío de SMS y el acceso a contactos. Según la Documentación para desarrolladores de Android, cada permiso pertenece a uno de cuatro niveles de protección: normal, dangerous, signature y special.
Puntos clave
AndroidManifest Permissions es el mecanismo de seguridad de Android que controla el acceso de las aplicaciones a datos protegidos y funciones del sistema. Cada aplicación debe declarar los permisos necesarios en el archivo AndroidManifest.xml mediante el elemento
El modelo de permisos de Android ha pasado por varias etapas de evolución. Antes de Android 6.0 (API 23), todos los permisos se concedían en la instalación: el usuario veía la lista completa y aceptaba o rechazaba la instalación de la aplicación. A partir de Android 6.0, los permisos de nivel dangerous se solicitan en tiempo de ejecución (Runtime Permissions), lo que da al usuario un control más flexible.
Los permisos se dividen en cuatro niveles de protección: normal (se conceden automáticamente al instalar), dangerous (requieren solicitud en tiempo de ejecución), signature (solo disponibles para aplicaciones firmadas con el mismo certificado) y special (requieren activación por separado en los ajustes). Cada nivel tiene su propio mecanismo de concesión y revocación.
Según Google I/O 2024, Android 15 planea introducir permisos más granulares: el usuario podrá conceder acceso solo a archivos específicos en la biblioteca multimedia, no a toda la colección. Esto continúa la tendencia de Android a minimizar la cantidad de datos proporcionados por defecto.
A diferencia de iOS, donde todos los permisos se solicitan en tiempo de ejecución, Android divide los permisos en tipos de instalación (install-time) y ejecución (runtime). El nivel normal se concede automáticamente al instalar sin notificación al usuario. El nivel dangerous requiere un diálogo explícito, similar a iOS.
Otra diferencia: en Android, los permisos se agrupan en grupos de permisos. Si el usuario acepta el acceso a la cámara, la aplicación obtiene automáticamente acceso al micrófono, ya que están en el mismo grupo MICROPHONE. En iOS, cada permiso se solicita de forma independiente, sin importar los grupos.
| Versión de Android | Cambio en el modelo de permisos |
|---|---|
| Android 1.0–5.x | Todos los permisos se conceden en la instalación |
| Android 6.0 (API 23) | Introducción de Runtime Permissions para nivel dangerous |
| Android 10 (API 29) | Scoped Storage: acceso limitado al sistema de archivos |
| Android 11 (API 30) | Auto-reset de permisos: los permisos no utilizados se restablecen |
| Android 14 (API 34) | Permisos en tiempo de ejecución para acceso multimedia (foto, video, audio) |
Android define cuatro niveles de protección para los permisos, cada uno con sus propias reglas de concesión. Analicemos cada nivel en detalle.
Los permisos normal se conceden automáticamente al instalar la aplicación sin notificación ni solicitud al usuario. Cubren funciones de bajo riesgo que no amenazan la privacidad del usuario: INTERNET, ACCESS_NETWORK_STATE, VIBRATE, BLUETOOTH. El usuario no ve ningún diálogo de consentimiento: el permiso se considera concedido desde la instalación.
El desarrollador no necesita gestionar solicitudes en tiempo de ejecución para los permisos normal — basta con declararlos en el manifiesto. Sin embargo, en Android 12+, al instalar desde Google Play, el usuario ve una pestaña “Permisos” que enumera todos los permisos normal, lo que aumenta la transparencia. Según Statista (2024), más del 90% de las aplicaciones en Google Play usan INTERNET como el permiso normal más común.
Los permisos dangerous cubren el acceso a datos y funciones que pueden comprometer la privacidad: cámara, micrófono, geolocalización, contactos, SMS, teléfono, calendario, sensores corporales. Estos permisos requieren un mecanismo de dos pasos: declaración en el manifiesto + solicitud en tiempo de ejecución mediante ActivityCompat.requestPermissions().
El usuario puede denegar un permiso dangerous, y la aplicación debe manejar este escenario correctamente. En Android 11+, si el usuario deniega dos veces, las solicitudes posteriores no muestran el diálogo del sistema: el sistema devuelve automáticamente DENIED. En este caso, la aplicación debe dirigir al usuario a los ajustes.
El nivel signature: el permiso se concede automáticamente si la aplicación está firmada con el mismo certificado que el sistema u otra aplicación que definió el permiso. Se usa para aplicaciones de sistema y empresariales. Ejemplo: BIND_ACCESSIBILITY_SERVICE, solo disponible para aplicaciones del sistema.
El nivel special (SYSTEM_ALERT_WINDOW, WRITE_SETTINGS, REQUEST_INSTALL_PACKAGES, MANAGE_EXTERNAL_STORAGE) requiere una acción explícita del usuario a través de los ajustes del sistema. La aplicación puede abrir la página de ajustes con Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION). Google Play restringe el uso de permisos special y requiere una justificación en el formulario de publicación.
Desde Android 6.0, todos los permisos dangerous requieren solicitud en tiempo de ejecución. Veamos el ciclo completo de trabajo con runtime permissions en Kotlin.
Antes de llamar a una API que requiera un permiso dangerous, verifica siempre el estado actual mediante ContextCompat.checkSelfPermission(). Si el estado es PERMISSION_GRANTED, puedes llamar a la API. Si es PERMISSION_DENIED, debes solicitar el permiso mediante ActivityResultContract RequestPermission (AndroidX) o el obsoleto requestPermissions().
Se recomienda usar ActivityResultContracts.RequestMultiplePermissions para solicitar varios permisos a la vez. Google recomienda agrupar permisos relacionados (por ejemplo, cámara + micrófono para grabación de video) en un solo diálogo, para que el usuario vea el contexto completo de la solicitud.
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
)
}
}
}
}
Si el usuario deniega dos veces, Android pasa la solicitud al estado “Never ask again”. En este caso, shouldShowRequestPermissionRationale() devuelve false y el diálogo del sistema no se mostrará. La aplicación debe dirigir al usuario a los ajustes del sistema mediante Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).
Importante: no muestres un diálogo ofreciendo abrir ajustes inmediatamente después de la primera denegación, ya que se percibe como un comportamiento agresivo. Usa shouldShowRequestPermissionRationale() para determinar si es necesario mostrar una explicación. Material Design Guidelines recomiendan mostrar una pantalla explicando el valor del acceso, no solo un botón de “Abrir ajustes”.
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("Acceso a la cámara")
.setMessage("Permite el acceso a la cámara en Ajustes, "
+ "para tomar fotos de perfil")
.setPositiveButton("Abrir ajustes") { _, _ ->
openAppSettings()
}
.setNegativeButton("Cancelar", null)
.show()
}
El archivo AndroidManifest.xml contiene el elemento
Cada permiso se declara con un elemento
Por ejemplo, el permiso WRITE_EXTERNAL_STORAGE no es necesario en Android 10+ (Scoped Storage), por lo que debes especificar maxSdkVersion="28" (Android 9). Esto evita preguntas innecesarias de los usuarios en versiones más recientes. Android Studio advierte sobre los maxSdkVersion recomendados mediante 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>
El elemento
Se recomienda establecer required="false" para todas las funciones de hardware y comprobar la disponibilidad mediante PackageManager.hasSystemFeature(). Esto amplía la audiencia de tu aplicación. La única excepción es si la función es crítica para el funcionamiento de la aplicación (una app de taxi sin GPS no tiene sentido).
El manejo correcto de permisos es un aspecto clave de la calidad de una aplicación Android. Revisemos las principales recomendaciones y errores típicos.
Solicita solo los permisos que realmente sean necesarios para el funcionamiento de la aplicación. Cada permiso adicional reduce la tasa de conversión de instalaciones y aumenta la cantidad de denegaciones. Google Play Console muestra cuántos usuarios rechazaron la instalación debido al conjunto de permisos. Según AppBrain (2024), las aplicaciones con 10+ permisos dangerous tienen un 35% menos de instalaciones.
Revisa periódicamente la lista de permisos. Elimina los no utilizados, especialmente al migrar a versiones más recientes de Android donde algunos permisos se vuelven opcionales. Por ejemplo, con el selector de fotos (ActivityResultContracts.PickVisualMedia) en Android 13+, se puede acceder a la biblioteca multimedia sin el permiso dangerous READ_MEDIA_IMAGES.
Antes de solicitar un permiso dangerous, muestra al usuario una pantalla explicando por qué se necesita ese permiso y qué valor aporta. Material Design recomienda usar una hoja inferior (bottom sheet) o un diálogo con un icono, texto breve y un botón de “Continuar”. La explicación aumenta el consentimiento entre un 20% y un 30% en comparación con una solicitud directa.
Verifica shouldShowRequestPermissionRationale() antes de llamar a launch(). Si es true, muestra la explicación. Si es false, el permiso ya está concedido o el usuario lo ha denegado permanentemente (never ask again). En este último caso, muestra un botón de “Abrir ajustes” en lugar de repetir la solicitud.
Prueba todos los escenarios posibles: concesión del permiso, denegación, denegación permanente, revocación del permiso en ajustes, restablecimiento de permisos (Android 11+ auto-reset). Cada escenario debe manejarse sin fallos ni pérdida de datos. Android Testing Guide recomienda usar la biblioteca TestPermission para automatizar las pruebas.
Presta especial atención al escenario en el que el usuario revoca un permiso mientras la aplicación está en ejecución (aplicación minimizada → Ajustes → revocación). Al volver a la aplicación, verifica todos los permisos en onResume(). No confíes en el almacenamiento en caché del estado de los permisos: el usuario puede cambiarlos en cualquier momento.
Preguntas frecuentes
Sí, si un SDK incluye un permiso en su manifiesto, se fusiona con el manifiesto de la aplicación durante la compilación. Puedes eliminar un permiso innecesario del SDK usando tools:node="remove" en AndroidManifest.xml.
Llamar a una API sin permiso generará una SecurityException, lo que provocará un cierre inesperado de la aplicación. Verifica siempre el estado del permiso antes de usar la API correspondiente y maneja la denegación correctamente.
En los ajustes del dispositivo: Ajustes → Aplicaciones → [tu aplicación] → Permisos. Para restablecer todos los permisos, usa el comando adb: adb shell pm reset-permissions.
Sí, mediante ActivityResultLauncher en un Fragment o Service. Sin embargo, el diálogo de solicitud siempre requiere un contexto de UI de Activity. Para un Service, puedes mostrar una Notification con un Intent que abra la Activity de solicitud.
Por ejemplo, WRITE_EXTERNAL_STORAGE no es necesario en Android 10+ (Scoped Storage). Al especificar android:maxSdkVersion="28", excluyes la declaración del permiso en versiones nuevas, lo que mejora la compatibilidad y reduce la lista de permisos solicitados.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.