Cambios de comportamiento: todas las apps

La plataforma de Android 12 incluye cambios de comportamiento que podrían afectar a tu app. Los siguientes cambios se aplican a todas las apps cuando se ejecutan en Android 12, independientemente de la targetSdkVersion. Debes probar tu app y, luego, modificarla según corresponda para admitir estos cambios.

Asegúrate también de revisar la lista de cambios de comportamiento que solo afectan a las apps orientadas a Android 12.

Experiencia del usuario

Efecto de desplazamiento

El comportamiento de los eventos de desplazamiento cambia en Android 12. Consulta Efecto de desplazamiento para obtener más información.

Demora en la UX de las notificaciones del servicio en primer plano

A fin de proporcionar una experiencia optimizada para los servicios en primer plano de ejecución breve en Android 12, el sistema puede demorar 10 segundos la visualización de las notificaciones de determinados servicios en primer plano. Este cambio permite que se completen las tareas de corta duración antes de que aparezcan sus notificaciones.

Si un servicio en primer plano tiene al menos una de las siguientes características, el sistema muestra la notificación asociada inmediatamente después de que se inicia el servicio:

  • El servicio está asociado con una notificación que incluye botones de acción.
  • El servicio tiene un foregroundServiceType de mediaPlayback, mediaProjection o phoneCall.
  • El servicio proporciona un caso de uso relacionado con las llamadas telefónicas, la navegación o la reproducción multimedia, como se define en el atributo de categoría de la notificación.
  • Para inhabilitar el cambio de comportamiento, el servicio pasó FOREGROUND_SERVICE_IMMEDIATE a setForegroundServiceBehavior() cuando configuró la notificación.

Mejoras en el modo envolvente para la navegación por gestos

Android 12 simplifica el modo envolvente para facilitar la navegación por gestos y mantener la coherencia con el resto de la experiencia de actividades, como mirar un video y leer un libro. Para obtener más información, consulta la entrada correspondiente en la página de funciones.

Resolución de intents web

A fin de ofrecer una experiencia más optimizada cuando los usuarios seleccionan vínculos web, Android 12 abre un intent web determinado en el navegador predeterminado del usuario si ese intent contiene un dominio no aprobado. La app puede aprobarse para el dominio mediante uno de los siguientes enfoques:

  • Verifica el dominio con Android App Links.
  • Solicítale al usuario que asocie la app con un dominio.

Si la app invoca intents web, considera agregar un mensaje o un diálogo que le solicite al usuario que confirme la acción.

Obtén más información sobre los cambios en la resolución de intents web.

Intervalo restringido de App Standby

Los intervalos de App Standby ayudan al sistema a priorizar las solicitudes de recursos que hace la app según el momento y la frecuencia en que esta se usa.

Cada intervalo representa una prioridad. Una prioridad baja significa que el sistema impone más restricciones para ejecutar la app.

A partir de Android 12, se incluye un intervalo nuevo que se denomina restringido. El intervalo restringido tiene la prioridad más baja (y las restricciones más altas) de todos los intervalos. A continuación, se ordenan los intervalos de prioridad alta a baja:

  1. Activo: La app está en uso o se usó muy recientemente
  2. Conjunto de tareas: Se está haciendo un uso habitual de la app
  3. Frecuente: Se usa con frecuencia la app, pero no todos los días
  4. Poco frecuente: No se usa con frecuencia la app
  5. Restringido

El sistema tiene en cuenta el comportamiento de la app, además de los patrones de uso, para decidir si debe ubicarla en el intervalo restringido. Es menos probable que se coloque la app en este tipo de intervalo si esta usa recursos del sistema de manera más responsable.

El sistema coloca la app en un intervalo menos restringido si el usuario interactúa directamente con esta.

Restricciones para la administración de batería

Si el sistema coloca la app en el intervalo restringido, se aplican las siguientes restricciones:

  • Puedes ejecutar trabajos una vez al día, en una sesión por lotes de 10 minutos. Durante esta sesión, el sistema agrupa los trabajos de la app con los trabajos de otras.
  • La app puede ejecutar menos trabajos acelerados, en comparación con el momento en el que el sistema la coloca en un intervalo menos restringido.
  • Las alarmas inexactas de la app se entregan una vez al día. Creas una alarma inexacta cuando llamas a los métodos set(), setInexactRepeating(), setAndAllowWhileIdle() o setWindow().
  • Por día, la app puede recibir, de manera oportuna, cinco mensajes de Firebase Cloud Messaging (FCM) con prioridad alta. Todos los mensajes posteriores de FCM se entregan con prioridad normal; por lo tanto, quizá se demoren si el dispositivo está en modo de ahorro de energía.

Asignación de servicios en primer plano

Si el sistema coloca la app en el intervalo restringido, esta todavía puede ejecutar servicios en primer plano. Sin embargo, si la app se orienta a Android 12, las restricciones para el inicio del servicio en primer plano continúan aplicándose.

Cómo verificar si la app se encuentra en el intervalo restringido

Para comprobar si el sistema colocó la app en el intervalo restringido, llama a getAppStandbyBucket(). Si el valor que se muestra de este método es STANDBY_BUCKET_RESTRICTED, la app se encuentra en este tipo de intervalo.

Cómo probar el comportamiento del intervalo restringido

Para probar cómo se comporta la app cuando el sistema la coloca en el intervalo restringido, puedes moverla manualmente a ese intervalo. Para ello, ejecuta el siguiente comando en una ventana de la terminal:

adb shell am set-standby-bucket PACKAGE_NAME restricted

Display#getRealSize y getRealMetrics: baja y zona de pruebas

Los dispositivos Android están disponibles en muchos factores de forma diferentes, como pantallas grandes, tablets y dispositivos plegables. A fin de procesar el contenido de manera correcta para cada dispositivo, la app debe determinar el tamaño de pantalla o visualización. Con el tiempo, Android brindó diferentes API para recuperar esta información. En Android 11, presentamos la API de WindowMetrics y dimos de baja estos métodos:

En Android 12, continuamos recomendándote que uses la clase WindowMetrics y dimos de baja estos métodos:

Con el objeto de mitigar el comportamiento de las aplicaciones que usan las API de Display para recuperar los límites de la aplicación, Android 12 agrega un mecanismo nuevo de zona de pruebas a fin de corregir la información que muestran estas API. Este mecanismo podría afectar a las apps que usan esta información con MediaProjection.

Las apps deben usar las API de WindowMetrics para consultar los límites de su ventana y Configuration.densityDpi a fin de consultar la densidad actual.

Para obtener una compatibilidad más amplia con versiones anteriores de Android, puedes usar la biblioteca de WindowManager de Jetpack, que incluye una clase WindowMetrics compatible con Android 4.0 (nivel de API 14) y versiones posteriores.

Ejemplos de cómo usar WindowMetrics

Primero, asegúrate de que se pueda cambiar por completo el tamaño de las actividades de la app.

Una actividad debe depender de WindowMetrics desde el contexto de una actividad para cualquier trabajo relacionado con la IU, en especial WindowManager.getCurrentWindowMetrics().

Si la app crea un elemento MediaProjection, los límites deben tener el tamaño correcto, ya que la proyección captura la pantalla. Si se puede cambiar por completo el tamaño de la app, el contexto de la actividad mostrará los límites correctos de la siguiente manera:

WindowMetrics projectionMetrics = activityContext
        .getSystemService(WindowManager.class).getMaximumWindowMetrics();

Si no se puede cambiar por completo el tamaño, la app debe consultar los límites desde una instancia de WindowContext y recuperar la clase WindowMetrics del área de visualización máxima disponible para la aplicación mediante WindowManager.getMaximumWindowMetrics().

Context windowContext = mContext.createWindowContext(mContext.getDisplay(),
        TYPE_APPLICATION, null /* options */);
WindowMetrics projectionMetrics = windowContext.getWindowManager()
        .getMaximumWindowMetrics();

Imágenes y gráficos

Mejora del cambio de frecuencia de actualización

En Android 12, los cambios de frecuencia de actualización mediante setFrameRate() pueden ocurrir independientemente de que la pantalla admita una transición fluida a la frecuencia de actualización nueva; una transición fluida es aquella en la que no se producen interrupciones visuales, como una pantalla negra durante uno o dos segundos. Anteriormente, si la pantalla no admitía una transición fluida, por lo general, continuaba usando la misma frecuencia de actualización después de llamar a setFrameRate(). Puedes llamar a getAlternativeRefreshRates() para determinar con anticipación si es probable que la transición a la actualización nueva se realice de manera fluida. Por lo general, se llama a la devolución de llamada onDisplayChanged() después de que se completa el cambio de frecuencia de actualización. Sin embargo, para algunas pantallas conectadas de forma externa, se llama durante una transición poco fluida.

Este es un ejemplo de cómo puedes implementarlo:

Kotlin

// Determine whether the transition will be seamless.
// Non-seamless transitions may cause a 1-2 second black screen.
val refreshRates = this.display?.mode?.alternativeRefreshRates
        val willbeSeamless = Arrays.asList<FloatArray>(refreshRates).contains(newRefreshRate)

// Set the frame rate even if the transition will not be seamless.
surface.setFrameRate(newRefreshRate, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS)

Java

// Determine whether the transition will be seamless.
// Non-seamless transitions may cause a 1-2 second black screen.
Display display = context.getDisplay(); // API 30+
Display.Mode mode = display.getMode();
float[] refreshRates = mode.getAlternativeRefreshRates();
boolean willbeSeamless = Arrays.asList(refreshRates).contains(newRefreshRate);

// Set the frame rate even if the transition will not be seamless.
surface.setFrameRate(newRefreshRate, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS);

Seguridad y privacidad

Botones de activación del micrófono y de la cámara

Los azulejos de Configuración rápida se etiquetan como &quot;Acceso a la cámara&quot; y &quot;Acceso al micrófono&quot;.
Figura 1: Botones de activación del micrófono y de la cámara en Configuración rápida.

En Android 12, los dispositivos compatibles permiten que los usuarios habiliten e inhabiliten el acceso a la cámara y al micrófono para todas las apps del dispositivo con solo presionar un botón de activación. Los usuarios pueden acceder a opciones que se pueden activar o desactivar desde Configuración rápida, como se muestra en la figura 1, o desde la pantalla de privacidad en la configuración del sistema.

Los botones de activación de la cámara y del micrófono afectan a todas las apps del dispositivo:

  • Cuando el usuario desactiva el acceso a la cámara, las apps reciben un feed de la cámara en blanco.
  • Cuando el usuario desactiva el acceso al micrófono, las apps reciben un audio vacío. Además, los sensores de movimiento tienen un límite de frecuencia, independientemente de si declaras el permiso HIGH_SAMPLING_RATE_SENSORS.

Cuando el usuario desactiva el acceso a la cámara o al micrófono, y luego inicia una app que necesita acceder a la información de estos, el sistema le recuerda al usuario que se desactivó el botón de activación para todo el dispositivo.

Cómo verificar si un dispositivo determinado admite los botones de activación del micrófono y de la cámara

Para comprobar si un dispositivo admite los botones de activación del micrófono y de la cámara, agrega la lógica que aparece en el siguiente fragmento de código:

Kotlin

val sensorPrivacyManager = applicationContext
        .getSystemService(SensorPrivacyManager::class.java)
        as SensorPrivacyManager
val supportsMicrophoneToggle = sensorPrivacyManager
        .supportsSensorToggle(Sensors.MICROPHONE)
val supportsCameraToggle = sensorPrivacyManager
        .supportsSensorToggle(Sensors.CAMERA)

Java

SensorPrivacyManager sensorPrivacyManager = getApplicationContext()
        .getSystemService(SensorPrivacyManager.class);
boolean supportsMicrophoneToggle = sensorPrivacyManager
        .supportsSensorToggle(Sensors.MICROPHONE);
boolean supportsCameraToggle = sensorPrivacyManager
        .supportsSensorToggle(Sensors.CAMERA);

Cómo verificar el comportamiento de la app en respuesta a los botones de activación del micrófono y de la cámara

Los botones de activación del micrófono y de la cámara no deben afectar la manera en que la app controla los permisos CAMERA y RECORD_AUDIO, siempre que sigas las prácticas recomendadas para los permisos de Android.

En particular, asegúrate de que la app haga lo siguiente:

  • Espera para acceder a la cámara del dispositivo hasta que el usuario otorgue el permiso CAMERA a la app.
  • Espera a acceder al micrófono del dispositivo hasta que el usuario otorgue el permiso RECORD_AUDIO a la app.

Indicadores de acceso al micrófono y a la cámara

Rectángulo redondeado en la esquina superior derecha, que incluye el ícono de una cámara y de un micrófono
Figura 2: Indicadores de acceso al micrófono y a la cámara, que muestran el acceso reciente a los datos.

En dispositivos que ejecutan Android 12, cuando una app accede al micrófono o a la cámara, aparece un ícono en la barra de estado. Si la app está en modo envolvente, el ícono aparece en la esquina superior derecha de la pantalla. Los usuarios pueden abrir Configuración rápida y seleccionar el ícono para ver qué apps están usando el micrófono o la cámara en ese momento. En la figura 2, se muestra un ejemplo de captura de pantalla que contiene los íconos.

Para ofrecer una mejor experiencia del usuario, no accedas al micrófono ni a la cámara hasta que el usuario otorgue de manera explícita permiso a la app.

Las apps no pueden cerrar los diálogos del sistema

Para mejorar el control del usuario cuando interactúa con apps y el sistema, la acción de intent ACTION_CLOSE_SYSTEM_DIALOGS deja de estar disponible a partir de Android 12. A excepción de algunos casos especiales, cuando tu app intenta invocar un intent que contiene esta acción, el sistema realiza una de las siguientes acciones según la versión del SDK de destino de la app:

  • Si la app se orienta a Android 12, se genera una SecurityException.
  • Si la app se orienta a Android 11 (nivel de API 30) o versiones anteriores, el intent no se ejecuta y aparece el siguiente mensaje en Logcat:

    E ActivityTaskManager Permission Denial: \
    android.intent.action.CLOSE_SYSTEM_DIALOGS broadcast from \
    com.package.name requires android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, \
    dropping broadcast.
    

Excepciones

En los siguientes casos, una app aún puede cerrar diálogos del sistema en Android 12:

  • La app ejecuta una prueba de instrumentación.
  • La app se orienta a Android 11 o versiones anteriores y muestra una ventana ubicada en el panel lateral de notificaciones.

  • La app se orienta a Android 11 o una versión anterior. Además, el usuario interactuó con una notificación, probablemente con los botones de acción de la notificación, y tu app procesa un servicio o receptor de emisión en respuesta a esa acción del usuario.

  • La app se orienta a Android 11 o versiones anteriores y tiene un servicio de accesibilidad activo. Si la app se orienta a Android 12 y desea cerrar la barra de notificaciones, usa la acción de accesibilidad GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE en su lugar.

Bloqueo de los eventos táctiles que no son de confianza

Con el objetivo de preservar la seguridad del sistema y una buena experiencia del usuario, Android 12 evita que las apps consuman eventos táctiles, en los que una superposición oculta la app de forma insegura. En otras palabras, el sistema bloquea los toques que pasan por determinadas ventanas, con algunas excepciones.

Apps afectadas

Este cambio afecta a las apps que permiten que los toques pasen por sus ventanas, por ejemplo, mediante la marca FLAG_NOT_TOUCHABLE. Entre los ejemplos, se incluyen los siguientes:

Excepciones

En los siguientes casos, se permiten los toques "de paso":

  • Interacciones dentro de tu app. La app muestra la superposición, y la superposición solo aparece cuando el usuario interactúa con la app.
  • Ventanas de confianza. Entre los ejemplos de estas ventanas, se incluyen los siguientes:

  • Ventanas invisibles. La vista raíz de la ventana es GONE o INVISIBLE.

  • Ventanas totalmente transparentes. La propiedad alpha es 0.0 para la ventana.

  • Ventanas de alerta de sistema lo suficientemente traslúcidas. El sistema considera que un conjunto de ventanas de alerta del sistema es lo suficientemente translúcido cuando la opacidad combinada es menor o igual que la opacidad máxima oculta del sistema para los toques. En la versión Beta de Android 12, esta opacidad máxima es de 0.8. Es posible que este valor cambie en una próxima versión Beta.

Detecta si se bloquea un toque no confiable

Si el sistema bloquea una acción táctil, Logcat registra el siguiente mensaje:

Untrusted touch due to occlusion by PACKAGE_NAME

Prueba el cambio

Los toques no confiables se bloquean de forma predeterminada en dispositivos que ejecutan la Vista previa para desarrolladores 3 de Android 12. Para permitir toques no confiables, ejecuta el siguiente comando de ADB en una ventana de terminal:

# A specific app
adb shell am compat disable BLOCK_UNTRUSTED_TOUCHES com.example.app

# All apps
# If you'd still like to see a Logcat message warning when a touch would be
# blocked, use 1 instead of 0.
adb shell settings put global block_untrusted_touches 0

Para revertir el comportamiento al valor predeterminado (en el que se bloquean los toques no confiables), ejecuta el siguiente comando:

# A specific app
adb shell am compat reset BLOCK_UNTRUSTED_TOUCHES com.example.app

# All apps
adb shell settings put global block_untrusted_touches 2

Visibilidad del paquete de permisos

En los dispositivos que ejecutan Android 12, las apps que se orientan a Android 11 (nivel de API 30) o versiones posteriores y que llaman a uno de los siguientes métodos reciben un conjunto filtrado de resultados, según la visibilidad del paquete de la app en otras:

Eliminación de la implementación de Bouncy Castle

En Android 12, se quitaron muchas implementaciones de Bouncy Castle de algoritmos criptográficos que ya habían dejado de estar disponibles, incluidos todos los algoritmos de AES. En cambio, el sistema usa las implementaciones de Conscrypt de estos algoritmos.

Este cambio afecta a la app si se cumple alguna de las siguientes condiciones:

  • La app usa tamaños de claves de 512 bits. Conscrypt no admite este tamaño. Si es necesario, actualiza la lógica criptográfica de la app para usar diferentes tamaños de claves.
  • La app usa tamaños de claves no válidos con KeyGenerator. La implementación de KeyGenerator de Conscrypt realiza una validación adicional de los parámetros clave, en comparación con Bouncy Castle. Por ejemplo, Conscrypt no permite que la app genere una clave AES de 64 bits, ya que AES solo es compatible con claves de 128, 192 y 256 bits.

    Bouncy Castle permite generar claves de tamaños no válidos, pero luego falla si estas se usan con Cipher. Conscrypt falla antes.

  • Inicializa los algoritmos de cifrado en modo Galois/Counter (GCM) con un tamaño diferente a 12 bytes. La implementación de GcmParameterSpec de Conscrypt requiere una inicialización de 12 bytes, que NIST recomienda.

Notificaciones del acceso al portapapeles

En Android 12, cuando una app llama a getPrimaryClip() para acceder a ClipData desde una app diferente por primera vez, un mensaje de aviso informa al usuario sobre el acceso a este portapapeles.

El texto dentro de este mensaje contiene el siguiente formato: APP pasted from your clipboard.

La notificación no aparece cuando se recupera la descripción del portapapeles

La app podría llamar a getPrimaryClipDescription() para recibir información sobre los datos actuales que se encuentran en el portapapeles. Cuando llama a este método, el sistema no muestra ningún mensaje de aviso.

Android 12 mejora este método para detectar los siguientes detalles adicionales:

Conectividad

Actualizaciones de Passpoint

En Android 12, se agregaron las siguientes API:

  • isPasspointTermsAndConditionsSupported():Términos y condiciones es una función de Passpoint que permite que las implementaciones de redes reemplacen portales cautivos no seguros, que usan redes abiertas, por una red segura de Passpoint. Se le muestra una notificación al usuario cuando debe aceptar términos y condiciones. Las apps que sugieren redes de Passpoint que están restringidas por términos y condiciones deben llamar primero a esta API para asegurarse de que el dispositivo admita la función. Si el dispositivo no la admite, no podrá conectarse a esta red, y se sugerirá una red alternativa o heredada.
  • isDecoratedIdentitySupported(): Cuando te autenticas en redes extendidas con un prefijo, el prefijo con identidad extendido permite que los operadores de red actualicen el identificador de acceso a la red (NAI) para realizar el enrutamiento explícito a través de varios proxies dentro de una red AAA (consulta RFC 7542 para obtener más información al respecto).

    Android 12 implementa esta función a fin de cumplir con la especificación de WBA para extensiones PPS-MO. Las apps que sugieren redes de Passpoint que requieren una identidad extendida deben llamar primero a esta API para asegurarse de que el dispositivo admita la función. Si el dispositivo no la admite, la identidad no se extenderá, y es posible que falle la autenticación en la red.

Para crear una sugerencia de Passpoint, las apps deben usar las clases PasspointConfiguration, Credential y HomeSp. Estas clases describen el perfil de Passpoint, que se define en la especificación de Passpoint de Wi-Fi Alliance.

Actualización de restricciones en interfaces que no pertenecen al SDK

Android 12 incluye listas actualizadas de este tipo de interfaces que están basadas en la colaboración con desarrolladores de Android y las pruebas internas más recientes. Siempre que sea posible, nos aseguramos de que las alternativas públicas estén disponibles antes de restringir las interfaces que no pertenecen al SDK.

Si tu app no está orientada a Android 12, es posible que algunos de estos cambios no te afecten de inmediato. Sin embargo, aunque actualmente puedes usar algunas interfaces que no pertenecen al SDK (según el nivel de API al que esté orientada la app), utilizar cualquier método o campo que no pertenezca al SDK siempre implica un gran riesgo de error para tu app.

En caso de no saber con certeza si tu app usa este tipo de interfaces, puedes probarla para verificarlo. Si tu app depende de interfaces que no pertenezcan al SDK, deberías planificar una migración hacia otras alternativas que sí lo hagan. Sin embargo, sabemos que algunas apps tienen casos prácticos válidos para usarlas. Si no encuentras una alternativa al uso de una interfaz que no pertenece al SDK para una función de tu app, deberías solicitar una nueva API pública.

Para obtener más información sobre los cambios implementados en esta versión de Android, consulta Actualizaciones a las restricciones de interfaces que no pertenecen al SDK en Android 12. Para obtener más información sobre interfaces que no pertenecen al SDK en general, consulta Restricciones en interfaces que no pertenecen al SDK.