Permisos delicados: ubicación, sensores y avisos
Hay un puñado de permisos que la plataforma trata de forma distinta a todos los demás porque su abuso histórico fue masivo y sus consecuencias, irreversibles. Esta lección los recorre con detalle: la escala de precisión de la ubicación y el diálogo de dos opciones, por qué el segundo plano exige una escalada separada y una justificación ante la tienda, cómo conviven la cámara y el micrófono con los indicadores de privacidad y los conmutadores globales del dispositivo, qué obligaciones impone la tipificación de los servicios en primer plano, y por qué el permiso de notificaciones llegó tarde y cambió el diseño de la incorporación de usuarios.
No todos los permisos peligrosos son igual de peligrosos. La plataforma mantiene, dentro de la categoría, un grupo reducido al que aplica reglas propias: solicitudes que no caben en un solo diálogo, concesiones que solo se otorgan desde Ajustes, indicadores permanentes en la barra de estado, conmutadores globales que anulan la concesión sin retirarla, revisiones manuales antes de publicar y tipificaciones obligatorias en el manifiesto. Ese tratamiento diferencial no responde a una jerarquía teórica de sensibilidad sino a una historia concreta de abuso documentado: la ubicación en segundo plano vendida a corredores de datos, los micrófonos activos sin indicación visible, las notificaciones convertidas en canal publicitario desde el primer arranque. Cada regla de este nivel es la cicatriz de un incidente real, y leerlas así —como jurisprudencia y no como capricho— es lo que permite anticipar cuál será la siguiente.
- Manejar los tres permisos de ubicación y el diálogo de precisión, incluida la concesión aproximada cuando se pidió exacta.
- Implementar la escalada al segundo plano como flujo separado y evaluar si tu caso de uso la justifica ante la tienda.
- Convivir con los indicadores de privacidad, los conmutadores globales de sensores y la tipificación de los servicios en primer plano.
- Integrar el permiso de notificaciones en el momento adecuado del recorrido del usuario y no en el arranque.
Ubicación: una escala de precisión, no un interruptor
La ubicación se expresa con dos permisos de primer plano y uno adicional para el segundo plano. El aproximado sitúa al usuario dentro de un área de varios kilómetros cuadrados, suficiente para el tiempo, para las noticias locales o para una lista de tiendas por ciudad. El exacto entrega la posición del sensor con la precisión que el hardware permita, y solo se justifica cuando la funcionalidad se rompe sin ella: navegación paso a paso, registro de rutas deportivas, emergencias.
Desde la API 31, la relación entre ambos dejó de ser jerárquica desde el punto de vista de la interfaz. El diálogo muestra dos opciones de precisión, y el usuario puede conceder el aproximado aunque tú hayas pedido el exacto. La consecuencia técnica es directa y sorprende a mucha gente la primera vez: la solicitud puede resolverse como concedida y dejarte sin la precisión que necesitabas.
Por eso ambos permisos deben pedirse juntos, en el mismo array, y el resultado debe inspeccionarse permiso a permiso en lugar de leerse como un booleano global. Pedir solo el exacto es un error de manual que produce un comportamiento indefinido según la versión.
private val pedirUbicacion = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions(),
) { resultado ->
when {
resultado[Manifest.permission.ACCESS_FINE_LOCATION] == true -> usarExacta()
resultado[Manifest.permission.ACCESS_COARSE_LOCATION] == true -> usarAproximada()
else -> ofrecerBusquedaManual()
}
}
pedirUbicacion.launch(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
),
)
La tasa de aceptación del permiso aproximado es sustancialmente mayor que la del exacto, y en la mayoría de las aplicaciones la diferencia funcional entre ambos es invisible para el usuario. Una estrategia que funciona muy bien consiste en declarar y pedir únicamente el aproximado, construir el producto sobre él, y escalar al exacto solo en la funcionalidad concreta que lo necesita y en el momento en que se abre. Quien ya está usando tu navegador entiende perfectamente por qué le pides precisión; quien acaba de instalar, no.
El segundo plano no se pide: se escala
Recoger ubicación cuando la aplicación no está a la vista es la capacidad que más abuso concentró históricamente, y la plataforma la trata en consecuencia. Desde la API 29 existe como permiso separado, y desde la API 30 ya no puede solicitarse en la misma petición que los de primer plano: hacerlo provoca que el sistema rechace la solicitud completa sin mostrar nada.
El flujo obligatorio es secuencial. Primero se obtiene el permiso de primer plano. Después, en un momento distinto y con una explicación propia, se solicita el de segundo plano, y el sistema no muestra un diálogo sino que conduce al usuario a la pantalla de ajustes de ubicación de la aplicación, donde debe elegir la opción de permitir siempre. El texto de esa opción es literalmente el que debes citar en tu explicación, porque de lo contrario la persona no sabrá qué buscar.
flowchart TD A[Funcion que necesita seguimiento continuo] --> B[Pedir permiso de primer plano] B -->|denegado| C[Ofrecer alternativa manual] B -->|concedido| D[Demostrar el valor con la funcion basica] D --> E[Explicar por que hace falta el uso continuo] E --> F[Abrir ajustes de ubicacion de la aplicacion] F --> G[Comprobar el estado al volver al primer plano] G -->|permitido siempre| H[Activar el seguimiento] G -->|sin cambios| I[Mantener el modo en primer plano]
A la barrera técnica se suma una barrera de distribución que conviene conocer antes de diseñar la funcionalidad. Google Play exige una declaración específica para el acceso a ubicación en segundo plano, con descripción del caso de uso, evidencia en vídeo del flujo dentro de la aplicación y una justificación de por qué el uso en primer plano no basta. Las aprobaciones son restrictivas y la sanción por incumplir es la retirada. Diseñar un producto cuyo modelo de negocio dependa de esa concesión sin haber comprobado antes que encaja en las categorías admitidas es un riesgo empresarial, no técnico.
Hay una familia de permisos que arrastró durante años el estigma de la ubicación sin necesitarla: los de proximidad. Explorar dispositivos por radio permite inferir posición, y por eso el sistema exigía el permiso de ubicación para escanear. Desde la API 31 existen permisos específicos de proximidad, y una bandera del manifiesto que declara formalmente que no derivarás posición del resultado, lo que libera a tu aplicación de pedir ubicación para conectar unos auriculares.
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="s" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
Esa bandera es un ejemplo excelente de una técnica que la plataforma está usando cada vez más: en lugar de restringir la capacidad, exigir una declaración de intención que el sistema puede hacer cumplir filtrando los resultados. Quien la declara recibe datos depurados de todo lo que permitiría triangular, y a cambio no molesta al usuario con una petición que no se corresponde con lo que hace.
Existe una alternativa que resuelve una fracción notable de los casos sin tocar el segundo plano: el servicio en primer plano con notificación persistente. El usuario ve que la aplicación está trabajando, la ubicación se recoge mientras el servicio vive, y no se necesita el permiso de segundo plano porque, formalmente, la aplicación está en uso. Para grabar una ruta deportiva o guiar un trayecto es exactamente el modelo correcto.
Cámara, micrófono y los límites que no controlas
Los sensores de captura tienen dos capas de control por encima del permiso, y ambas pueden dejarte sin datos con el permiso perfectamente concedido. La primera son los indicadores de privacidad introducidos en la API 31: un punto verde permanente en la barra de estado siempre que la cámara o el micrófono estén activos, con un panel que identifica a la aplicación responsable. No es configurable ni ocultable, y su efecto práctico es que cualquier uso desproporcionado se vuelve visible de inmediato.
La segunda son los conmutadores globales de sensor, accesibles desde los ajustes rápidos. Cuando el usuario desactiva el micrófono del dispositivo entero, las aplicaciones con permiso concedido siguen abriendo el flujo con normalidad y reciben silencio; con la cámara, reciben una imagen en negro. No hay excepción, no hay fallo y no hay señal obvia: la operación tiene éxito y los datos están vacíos.
val gestor = getSystemService(SensorPrivacyManager::class.java)
val micApagado = gestor?.supportsSensorToggle(SensorPrivacyManager.Sensors.MICROPHONE) == true
Una aplicación de grabación que no detecte esa situación mostrará al usuario un fichero de treinta minutos de silencio y un problema de soporte imposible de diagnosticar. La comprobación es barata y el aviso al usuario, junto con el atajo a los ajustes rápidos, convierte un fallo silencioso en un mensaje comprensible.
La tercera capa es la tipificación de los servicios en primer plano, obligatoria desde la API 34. Cualquier servicio que use cámara, micrófono o ubicación debe declarar su tipo en el manifiesto y poseer el permiso correspondiente al tipo, además del permiso del sensor. Y rige una restricción adicional que rompe mucho código antiguo: un servicio de estos tipos no puede arrancarse desde el segundo plano, porque el sistema exige que la aplicación esté en uso en el momento de iniciarlo.
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<service
android:name=".GrabadorService"
android:foregroundServiceType="microphone"
android:exported="false" />
Indicador siempre visible
El punto verde no se puede suprimir. Cualquier uso de cámara o micrófono queda expuesto en tiempo real, lo que convierte la captura silenciosa en una imposibilidad práctica y no solo en una infracción de política.
Conmutador global
El usuario puede apagar el sensor para todo el dispositivo. Tu permiso sigue concedido, tu código sigue funcionando y tus datos están vacíos. Detectarlo y explicarlo es responsabilidad tuya.
Notificaciones: el permiso que llegó tarde
Durante trece años, cualquier aplicación instalada podía notificar sin pedir nada. La API 33 introdujo el permiso de publicación de notificaciones y con ello alteró una premisa sobre la que descansaba el diseño de la incorporación de millones de aplicaciones: la de que el canal de reenganche estaba garantizado.
El comportamiento depende de la versión a la que apuntes. Si apuntas a la API 33 o superior, debes solicitarlo explícitamente como cualquier permiso peligroso. Si apuntas a una versión anterior, el sistema muestra el diálogo por su cuenta en el momento en que creas el primer canal de notificación, lo que casi siempre ocurre en el arranque y en el peor contexto posible. Esa diferencia hace que actualizar el objetivo del compilador sea, en este caso concreto, una mejora directa de la tasa de aceptación: te devuelve el control sobre el instante de la pregunta.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
!tieneAcceso(this, Manifest.permission.POST_NOTIFICATIONS)
) {
pedirNotificaciones.launch(Manifest.permission.POST_NOTIFICATIONS)
}
El momento correcto se deduce de la misma lógica que gobierna todo lo demás: pídelo cuando el usuario acaba de hacer algo cuyo resultado querrá saber. Después de activar una alerta de precio, después de enviar un mensaje que espera respuesta, después de configurar un recordatorio. Pedirlo en la pantalla de bienvenida es regalar el canal a cambio de nada, y ese canal no se recupera.
El permiso, además, es solo el primer filtro. Por debajo de él siguen operando los canales, que el usuario puede silenciar individualmente, y las políticas de agrupación e importancia que decide el propio sistema. Una aplicación que concentra todos sus avisos en un único canal obliga a la persona a elegir entre recibirlo todo o no recibir nada, y esa elección se resuelve casi siempre desactivándolo todo. Separar los canales por naturaleza del aviso —transaccional, social, promocional— es la mejor defensa que existe contra el silenciamiento total.
Conviene además asumir la consecuencia arquitectónica: una notificación es ahora una entrega no garantizada. Todo aviso importante necesita una representación equivalente dentro de la aplicación, visible cuando el usuario la abre, porque un porcentaje significativo de tu base de usuarios nunca verá la notificación y no lo sabrá.
Hay una manera de estudiar los permisos delicados que convierte una lista arbitraria de restricciones en un modelo predictivo, y consiste en preguntarse, ante cada regla, qué abuso concreto la produjo. El permiso de ubicación en segundo plano se separó del de primer plano porque una industria entera de corredores de datos se construyó comprando trazas de posición recogidas por aplicaciones de linterna y de meteorología, cuyos usuarios habían aceptado un único diálogo genérico años atrás. El indicador verde permanente apareció porque la única defensa efectiva contra la escucha oportunista resultó ser la visibilidad, después de que ninguna combinación de políticas y revisiones lograra detectarla a escala. La tipificación de los servicios en primer plano nació porque el servicio persistente se había convertido en el mecanismo universal para eludir todas las restricciones anteriores. Y el permiso de notificaciones se añadió trece años tarde porque el canal, concebido como servicio al usuario, había degenerado en un espacio publicitario que nadie había aceptado. Leídas así, las reglas dejan de ser un obstáculo y se vuelven un mapa: cada una señala el punto exacto donde la confianza se rompió, y la dirección del conjunto es inequívoca y monótona. Toda capacidad que pueda ejercerse sin que el usuario lo perciba acabará teniendo un indicador. Toda concesión amplia acabará fragmentándose en concesiones específicas. Todo permiso que pueda concederse una vez y explotarse indefinidamente acabará caducando. Todo lo que ocurra en segundo plano acabará exigiendo justificación ante la tienda. Quien diseña hoy una funcionalidad puede aplicar ese vector como test de longevidad, y el test es brutalmente simple: si el valor de tu producto depende de recoger algo que el usuario no advertiría, tu producto tiene fecha de caducidad, y la fecha la fija el calendario de versiones de Android y no tu hoja de ruta. La conclusión práctica no es defensiva sino de diseño: construye sobre lo que el usuario aceptaría si se lo explicaras en una frase, porque tarde o temprano el sistema se lo va a explicar por ti, y ese día conviene que la respuesta sea que sí.
- Comprueba si pides ubicación exacta y determina, funcionalidad por funcionalidad, cuáles seguirían funcionando con la aproximada.
- Simula la concesión aproximada cuando pediste exacta y verifica que tu código no la interpreta como una denegación.
- Apaga el micrófono con el conmutador global y ejecuta tu flujo de grabación: mide cuánto tarda tu aplicación en darse cuenta.
- Revisa cada servicio en primer plano, declara su tipo y localiza cualquier punto donde intentes arrancarlo desde el segundo plano.
- Mueve la solicitud de notificaciones desde el arranque hasta el primer momento en que el usuario configure algo que espera recibir, y compara la aceptación durante dos semanas.