<CarlosDev/>

Actual

Android 17: adaptable y con límite de memoria

API 37, junio de 2026. Las pantallas grandes perdieron la exclusión de orientación, el sistema mata apps que superan un límite de memoria según el RAM, y siguieron dos SDK menores.

Publicado
Actualizado
Lectura
45 min
Redacción
Estable
Serie
Parte 19 de 19 · Android, versión por versión
Banner de Android 17 Cinnamon Bun, con el logo de la versión.
En este registro14 secciones

Android 17 es la versión que volvió obligatorios los diseños adaptables y le puso un techo de memoria a cada app. Las apps que apuntan a API 37 ya no pueden mantener sus bloqueos de orientación o de relación de aspecto en tablets y plegables, y el sistema ahora mata un proceso que supera un límite fijado según el RAM del dispositivo. Para los desarrolladores, casi todo lo que se rompe espera a que targetSdk llegue a 37.

Esta es la vista general. La siguen seis partes, en orden: las cuatro betas, de la Beta 1 a la Beta 4, con la Beta 3 repasando cada API nueva en platform stability; qué rompe en targetSdk 37; y los límites de memoria.

De un vistazo

VersiónAndroid 17
Nivel de API37 (37.1 desde QPR1; 37.2 en la beta de QPR2)
NombreCinnamon Bun, interno; visible en la constante del SDK. Nombre público: Android 17
Build.VERSION_CODESCINNAMON_BUN (VERSION_CODES_FULL.CINNAMON_BUN_1 para QPR1)
LanzamientoEl despliegue en Pixel empezó el 16 de junio de 2026
Dispositivos de lanzamientoPixel 6 y posteriores por actualización; Samsung Galaxy Z Fold8 Ultra, Z Fold8 y Z Flip8, anunciados el 22 de julio de 2026
Fecha límite de target API en Google PlaySin anunciar a octubre de 2026

Google lanzó Android 17 el 16 de junio de 2026 para los Pixel 6 y posteriores, y publicó el código en AOSP el mismo día. El primer build de lanzamiento es CP2A.260605.016, tag android-17.0.0_r1, con el parche de seguridad de junio de 2026.

Esta vez Google no publicó developer previews: el canal Canary cumplió esa función, y Android 17 pasó directo a beta. La Beta 1 salió el 13 de febrero de 2026 y la Beta 2 el 26 de febrero. La superficie de API se bloqueó en la Beta 3, el 26 de marzo. La Beta 4, del 16 de abril, fue la última programada; el 1 de junio llegó una Beta 4.1 de correcciones.

Samsung anunció los Galaxy Z Fold8 Ultra, Z Fold8 y Z Flip8 el 22 de julio de 2026; sus fichas técnicas indican Android 17 con One UI 9.

El nombre Cinnamon Bun se filtró en agosto de 2025, cuando una fuente de Google le mostró a Android Authority el nombre en código del nivel de API 37.0. La constante del SDK lo confirma: CINNAMON_BUN = 37. Como Baklava en Android 16, es interno, pero los desarrolladores lo ven en el código.

Qué trajo

  • Adaptable primero. El post de lanzamiento de Google habla de “an adaptive-first development standard”. Cualquier app puede convertirse en una App Bubble flotante con una pulsación larga sobre su ícono, las pantallas grandes tienen una Bubble Bar en la barra de tareas, y el modo escritorio suma ventanas picture-in-picture interactivas.
  • Compose primero. Google declaró que todas las APIs de UI, librerías y guías nuevas se harán solo para Jetpack Compose. Las Views de android.widget y las librerías de Jetpack basadas en Views, como Fragments y RecyclerView, pasan a mantenimiento: solo correcciones críticas.
  • AppFunctions. Las apps pueden exponer sus acciones como herramientas que los agentes de IA, como Gemini, descubren y ejecutan en nombre del usuario. La librería de Jetpack está en alfa; la integración con Gemini estaba en vista previa privada al lanzamiento.
  • Menos reinicios de activity. Cambios como CONFIG_KEYBOARD o CONFIG_COLOR_MODE ya no recrean la activity por defecto; recibe onConfigurationChanged() en su lugar. android:recreateOnConfigChanges restaura el comportamiento anterior.
  • Continue On. setHandoffEnabled() permite que la barra de tareas de la tablet ofrezca la app que el usuario acaba de usar en su teléfono, con un deep link al punto donde la dejó.
  • Herramientas de rendimiento. ProfilingManager suma TRIGGER_TYPE_COLD_START, TRIGGER_TYPE_OOM, TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE y TRIGGER_TYPE_ANOMALY. Para las apps que apuntan a 37, MessageQueue no usa locks.
  • Multimedia y cámara. ImageFormat.RAW14, extensiones de cámara definidas por el fabricante, consulta del tipo de cámara, un codificador Extended HE-AAC, soporte de H.266 (VVC) para fabricantes y grabación de video a calidad constante.
  • Audífonos. AudioDeviceInfo.TYPE_BLE_HEARING_AID distingue los audífonos de los auriculares, y el usuario puede enviar notificaciones, tonos y alarmas al audífono o al altavoz del teléfono.
  • Selectores en vez de permisos. Un selector de contactos del sistema (ACTION_PICK_CONTACTS), una grilla vertical en el selector de fotos, un botón de ubicación dibujado por el sistema y un cuentagotas para toda la pantalla (ACTION_OPEN_EYE_DROPPER).

La lista completa de APIs en platform stability está en la parte de la Beta 3.

Seguridad y privacidad

  • Criptografía post-cuántica. Android Keystore puede generar claves ML-DSA en hardware seguro a través de las APIs estándar de JCA, y el APK Signature Scheme v3.2 combina una firma clásica con una ML-DSA.
  • Encrypted Client Hello. Para las apps que apuntan a 37, TLS usa ECH cuando la librería de red y el servidor lo soportan. <domainEncryption> en la configuración de seguridad de red lo activa o desactiva por dominio.
  • Certificate Transparency viene activado por defecto para las apps que apuntan a 37; en Android 16 era opt-in.
  • Carga de código nativo más segura. Para las apps que apuntan a 37, System.load() lanza UnsatisfiedLinkError si el archivo no es de solo lectura, lo que extiende la regla de Android 14 para archivos DEX y JAR.
  • OTP por SMS. Los mensajes WebOTP se retienen tres horas para toda app que no sea el destinatario verificado. Para las apps que apuntan a 37, la retención cubre también los OTP estándar.
  • Seguridad de activities. Para las apps que apuntan a 37, las restricciones de lanzamiento de activities en background se extienden a IntentSender, y MODE_BACKGROUND_ACTIVITY_START_ALLOWED cede su lugar a modos granulares como MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE.
  • Protección contra robo. “Mark as lost” de Find Hub puede exigir biometría además del PIN, así que un ladrón que vio el PIN no puede desactivar el rastreo. Remote Lock y Theft Detection Lock vienen activados por defecto en los dispositivos con Android 17 nuevos, restablecidos o actualizados.
  • Contraseñas con teclado físico. Para las apps que apuntan a 37, el último carácter escrito ya no se muestra cuando el usuario escribe con un teclado físico; la entrada táctil y la física tienen ajustes separados.
  • Keystore y perfiles. Las apps que no son del sistema y apuntan a 37 pueden tener como máximo 50.000 claves en Keystore, y el tráfico loopback entre perfiles queda bloqueado por defecto.

Permisos

  • ACCESS_LOCAL_NETWORK. Las apps que apuntan a 37 pierden el acceso a la red local salvo que tengan este permiso de runtime o usen un selector de dispositivos del sistema. Pertenece al grupo NEARBY_DEVICES, así que a quien ya concedió otro permiso de dispositivos cercanos no se le vuelve a preguntar.
  • Contactos. El selector de contactos devuelve solo los campos que el usuario elige, sin READ_CONTACTS. Para las apps que apuntan a 37, Contacts Provider oculta ACCOUNT_NAME, ACCOUNT_TYPE y ACCOUNT_TYPE_AND_DATA_SET en la vista de datos, y las consultas sin READ_CONTACTS pasan por validación SQL estricta.
  • Botón de ubicación. USE_LOCATION_BUTTON permite insertar el botón dibujado por el sistema que concede ubicación precisa solo para la sesión, sin diálogo.
  • Ventanas fijadas. USE_PINNED_WINDOWING_LAYER permite pedir una ventana interactiva siempre visible en el modo escritorio.
  • Acceso a la NPU. Las apps que apuntan a 37 y usan directamente la unidad de procesamiento neuronal deben declarar FEATURE_NEURAL_PROCESSING_UNIT, o quedan bloqueadas.

Qué cambió para tu app

Todas las apps en Android 17

CambioQué hacer
Un proceso que supera su límite de memoria según el RAM muere sin stack traceBusca "MemoryLimiter:AnonSwap" en ApplicationExitInfo; ver la parte de memoria
Reproducir audio, pedir foco o cambiar el volumen exige una activity visible o un foreground service (que no sea SHORT_SERVICE)Arranca un servicio mediaPlayback mientras la app es visible; MediaSessionService de Media3 lo hace
Los SMS WebOTP se retienen tres horas para las apps que no son el destinatarioUsa SMS Retriever o SMS User Consent
El tráfico loopback entre perfiles está bloqueadoNo dependas de localhost entre el perfil de trabajo y el personal

Apps que apuntan a API 37

CambioQué hacer
En pantallas sw600dp, se ignoran los límites de orientación, redimensionamiento y relación de aspecto; ya no hay exclusiónDiseña para cualquier tamaño de ventana; los juegos que declaran android:appCategory="game" quedan exentos
Los campos static final no se pueden cambiar por reflection ni por JNIActualiza las librerías que los reescriben; la escritura por JNI cierra el proceso
MessageQueue no usa locks; sus campos privados no sirven para reflectionActualiza Espresso y Robolectric; usa TestLooperManager
El acceso a la red local está bloqueado por defectoUsa un selector de dispositivos, o pide ACCESS_LOCAL_NETWORK
El audio en background exige un foreground service con capacidad while-in-useArranca el servicio desde una acción del usuario
Los SMS con OTP estándar se retienen tres horasPasa a SMS Retriever o SMS User Consent
setContentCaptureEnabled(false) ya no desactiva Content CaptureUsa FLAG_SECURE
Los RemoteViews de widgets que superan el límite de memoria de bitmaps lanzan IllegalArgumentExceptionReduce bitmaps e íconos en los widgets

El porqué de cada cambio, y en qué orden atacarlos, está en Android 17: qué rompe en targetSdk 37.

Android 17 QPR1, QPR2 y los SDK menores

Android 16 tuvo un SDK menor, en su versión de diciembre. Android 17 lleva dos.

  • QPR1 (API 37.1). Sus notas de versión listan diffs de API de 37 a 37.1 y dicen que llegó a platform stability en la Beta 6. Llegó a los Pixel el 15 de septiembre de 2026, con el Feature Drop de septiembre. La referencia de API lista VERSION_CODES_FULL.CINNAMON_BUN_1, “Android 17, minor release 1”, agregada en la versión 37.1.
  • QPR2 (API 37.2, beta). Google dice que “includes a minor SDK release”, con “no planned behavior changes”. La Beta 7.1 salió el 16 de octubre de 2026, y la referencia ya lista CINNAMON_BUN_2. Dos cambios igual llegan a las apps: los códigos USSD de desvío de llamadas ya no funcionan solo con CALL_PHONE, y los self-broadcasts se entregan de forma más eficiente, para todas las apps.

La comprobación es la misma que en Android 16: comparar SDK_INT_FULL con la constante de VERSION_CODES_FULL.

Qué perduró

Es pronto para saber qué mantienen las versiones siguientes. Google anunció dos pasos: desde Android 18, el sistema deja de conceder permisos de URI de forma implícita para algunos intents, y usesCleartextTraffic quedará deprecado en una versión futura en favor de una configuración de seguridad de red. Google Play no fijó fecha para API 37; la regla vigente pide API 36 desde el 31 de agosto de 2026.

Para saber

  • El comentario de Build.java para la constante son dos palabras: “Cinnamon Bun.” En VERSION_CODES_FULL es solo “Android 17.” Nada parecido a la línea de Shakespeare que AOSP dejó en S_V2.
  • El nombre respeta el orden. El alfabeto de postres volvió a empezar en Baklava, una B, con Android 16; Cinnamon Bun es la C que sigue. Los IDs de build de Android 17 también empiezan con C: CP2A.260605.016.
  • El Easter egg abre con 17 puntos en círculo. Si los recorres todos de un solo trazo y vuelves al primero, aparece el logo; si levantas el dedo, el recorrido se reinicia. Con teclado se salta: el código dice “skip the cute star-drawing minigame”.
  • El campo de estrellas cambió de dirección. El comentario del código dice “New in C: we’re zooming out from the center this time. Classic.” El techo de warp sigue en 16f, con el comentario “must go faster”.
  • El recolector de basura generacional aparece tanto en el post de Android 16 QPR2 de Google como en el de lanzamiento de Android 17. AOSP lo resuelve: el flag use_generational_cmc se activó por primera vez en la configuración de release de Android 16 QPR2, y Android 17 la hereda.

Parte 1 de 6

3 min

Android 17 Beta 1: directo a beta

13 de febrero de 2026. La primera build de Android 17, sin Developer Preview antes, quitó la exclusión de orientación en pantallas grandes para apps con target API 37.

Leer por separado: Android 17 Beta 1: directo a beta

La Beta 1 fue la primera build de Android 17 que Google entregó a los desarrolladores, el 13 de febrero de 2026. No hubo Developer Preview antes: Google reemplazó esa fase con el canal Android Canary. Casi todo lo que luego se rompe con targetSdk 37 ya estaba en esta build.

Fecha13 de febrero de 2026
BuildCP21.260116.011 (.A1 para Pixel 6, 6 Pro, 6a, 7 y 7 Pro; .B1 para el resto)
Parche de seguridad2026-01-05
DispositivosTeléfonos y tablets Pixel compatibles por OTA; imágenes de 64 bits del Android Emulator
Estabilidad de plataformaTodavía no; Google apuntaba a marzo

Google la describe como “suitable for development, testing, and general use”, con la advertencia de que el sistema y las apps “might not always work as expected”. Llegó a los Pixel dos días más tarde de lo previsto: 9to5Google informó un retraso de dos días antes de que empezara el despliegue el día 13.

Por qué no hubo Developer Preview

El post de Google sobre la Beta 1 dice que Android “has replaced the traditional ‘Developer Preview’ with a continuous Canary channel”. Las funciones y APIs llegan a Canary en cuanto pasan las pruebas internas, Canary se actualiza por OTA y la beta arranca más cerca de la versión final. El mismo post fijó el plan del año: la versión de Q2 es la única con cambios de comportamiento que rompen apps, y en Q4 llega un SDK menor.

Cambios de comportamiento en la Beta 1

  • Pantallas grandes. Las apps con target API 37 en pantallas con un ancho mínimo de 600dp o más ya no pueden excluirse del cambio de tamaño ni de orientación. Ahí se ignoran screenOrientation, setRequestedOrientation(), resizeableActivity, minAspectRatio y maxAspectRatio. Las apps que declaran android:appCategory="game" quedan exentas.
  • Menos reinicios de actividad. CONFIG_KEYBOARD, CONFIG_KEYBOARD_HIDDEN, CONFIG_NAVIGATION, CONFIG_TOUCHSCREEN, CONFIG_COLOR_MODE y algunos cambios de CONFIG_UI_MODE ya no recrean la actividad; esta recibe onConfigurationChanged(). android:recreateOnConfigChanges recupera el comportamiento anterior.
  • MessageQueue sin locks para apps con target 37. El código que usa reflexión sobre sus campos privados se rompe.
  • static final es final. Cambiar uno por reflexión lanza IllegalAccessException; hacerlo por JNI cierra la app.
  • Vistas de notificación personalizadas con límites de tamaño estrictos para apps con target 37, que cierran el atajo de usar URIs para saltarse los anteriores.
  • Audio en segundo plano. La reproducción, las solicitudes de foco y los cambios de volumen fallan en silencio si la app no está en un estado de ciclo de vida válido. La Beta 4 suavizó luego esta regla.
  • Tráfico en texto plano. Google anunció que android:usesCleartextTraffic queda obsoleto; las notas de versión dicen que entra en vigor con un nivel de SDK futuro, y que las apps deben pasar a una network security config.

APIs nuevas en la Beta 1

  • Disparadores de ProfilingManager para arranque en frío, OOM y uso excesivo de CPU.
  • CameraCaptureSession.updateOutputConfigurations() cambia entre foto y video sin cerrar la sesión, y LOGICAL_MULTI_CAMERA_ADDITIONAL_RESULTS devuelve metadatos de cada cámara física activa.
  • Soporte de H.266 (VVC) en dispositivos con decodificador por hardware, y MediaRecorder.setVideoEncodingQuality() para grabar con calidad constante.
  • Una Service Provider Interface pública para cifrado híbrido HPKE.
  • Perfiles de dispositivo médico y de pulsera de actividad en CompanionDeviceManager, y setExtraPermissions() para juntar los permisos de dispositivos cercanos en el diálogo de vinculación.

El post de Google también presentó el recolector de basura generacional de ART como un cambio de Android 17. AOSP muestra que ya estaba activo en Android 16 QPR2; ver “Para saber” en la vista general de arriba.

Parte 2 de 6

3 min

Android 17 Beta 2: selectores y red local

26 de febrero de 2026. Burbujas para cualquier app, EyeDropper y Contacts Picker, handoff entre dispositivos, el permiso ACCESS_LOCAL_NETWORK y tres horas de espera para los OTP.

Leer por separado: Android 17 Beta 2: selectores y red local

La Beta 2 llegó el 26 de febrero de 2026, trece días después de la Beta 1. Trajo casi todos los selectores del sistema que Android 17 usa en lugar de permisos amplios, y los dos cambios de privacidad que tocan a las apps por la red y por la bandeja de SMS.

Fecha26 de febrero de 2026
BuildCP21.260206.011 (.A1 para Pixel 6, 6 Pro, 6a, 7 y 7 Pro)
Parche de seguridad2026-02-05
DispositivosTeléfonos y tablets Pixel compatibles por OTA; imágenes de 64 bits del Android Emulator
Estabilidad de plataformaTodavía no; Google seguía apuntando a marzo

APIs nuevas en la Beta 2

  • Burbujas de apps. El usuario puede convertir cualquier app en una burbuja flotante con una pulsación larga sobre su icono; las pantallas grandes tienen una barra de burbujas en la barra de tareas. El post de Google aclara que las burbujas “aren’t yet fully enabled in Beta 2”.
  • EyeDropper. Intent.ACTION_OPEN_EYE_DROPPER devuelve el color de cualquier píxel de la pantalla sin permiso de captura de pantalla.
  • Contacts Picker. ACTION_PICK_CONTACTS da acceso temporal solo a los campos que el usuario elige, del perfil personal o del de trabajo, sin READ_CONTACTS.
  • Captura del puntero en touchpads. Un touchpad capturado ahora reporta movimiento relativo, como un mouse; View.POINTER_CAPTURE_MODE_ABSOLUTE mantiene las posiciones crudas de los dedos.
  • Handoff entre dispositivos. La Handoff API retoma el estado de una app en un dispositivo cercano, como una tablet, mediante CompanionDeviceManager, con una alternativa web.
  • Ranging y planes de datos. UWB DL-TDOA (FiRa 4.0) para navegación en interiores, detección de proximidad de la Wi-Fi Alliance, y getStreamingAppMaxDownlinkKbps() / getStreamingAppMaxUplinkKbps() para las tasas de streaming que asigna el operador.
  • Hora y texto. ACTION_TIMEZONE_OFFSET_CHANGED se dispara cuando cambia el desfase horario, por ejemplo con el horario de verano, e ICU 78 trae Unicode 17.

Cambios de comportamiento en la Beta 2

  • Red local. Las apps con target API 37 pierden el acceso a la LAN salvo que usen un selector de dispositivos del sistema o tengan el nuevo permiso de runtime ACCESS_LOCAL_NETWORK. Pertenece al grupo NEARBY_DEVICES, así que a quien ya concedió un permiso de dispositivos cercanos no se le vuelve a preguntar.
  • OTP retenidos tres horas. Los mensajes WebOTP se retienen para toda app que no sea el destinatario verificado, sea cual sea su target. Para las apps con target 37, la retención cubre todo SMS con un OTP. La app de SMS predeterminada, el asistente y las apps de dispositivos conectados quedan exentas; Google recomienda al resto SMS Retriever o SMS User Consent.
  • Acceso a la NPU. Las apps con target 37 que usan la unidad de procesamiento neuronal directamente, también mediante el delegado NPU de LiteRT o la obsoleta NNAPI, deben declarar FEATURE_NEURAL_PROCESSING_UNIT o quedan bloqueadas.

Las notas de versión listan 16 problemas corregidos. El primero es una regresión de Android 16 que hacía que las apps activas se reiniciaran o se recargaran solas.

Parte 3 de 6

9 min

Android 17 Beta 3: estabilidad de plataforma

Análisis completo de API 37 en estabilidad de plataforma — cada API nueva, cambio de comportamiento y qué hacer ahora.

Leer por separado: Android 17 Beta 3: estabilidad de plataforma

Android 17 Beta 3 llegó el 26 de marzo de 2026 con una señal muy específica para desarrolladores: se alcanzó la estabilidad de plataforma. Las APIs están bloqueadas — sin más adiciones, sin más eliminaciones, sin más sorpresas. Si eres autor de librerías, vendor de SDKs o desarrollador de apps, el tiempo corre.

Build CP21.260306.017, API nivel 37, parche de seguridad 2026-03-05. Esto es en serio.

Qué significa “estabilidad de plataforma”

Beta 3 es el punto en que el conjunto final de APIs queda congelado. De aquí al lanzamiento estable:

  • No se agregarán ni eliminarán APIs públicas
  • Las apps enviadas a Google Play ya pueden apuntar a API 37
  • Los autores de SDKs y librerías deben publicar actualizaciones de compatibilidad de inmediato
  • Los motores de juego y herramientas de desarrollo necesitan validar contra este build

El lanzamiento estable se espera para más adelante en 2026. Aún no hay fecha exacta, pero el patrón es consistente con años anteriores: Beta 3 en marzo → estable en torno al Q3.

Actualización, 17 de octubre de 2026: esa estimación falló. Android 17 pasó a estable el 16 de junio de 2026, después de una beta más (Beta 4, 16 de abril). Ver Android 17: qué rompe en targetSdk 37.


Cámara y multimedia

Personalización de la photo picker

Una función muy pedida: el selector de fotos ahora soporta relación de aspecto 9:16 en retrato, además del 1:1 cuadrado de siempre. Se usa PhotoPickerUiCustomizationParams:

val params = PhotoPickerUiCustomizationParams.Builder()
    .setAspectRatio(PhotoPickerUiCustomizationParams.ASPECT_RATIO_PORTRAIT_9_16)
    .build()

Compatible con ACTION_PICK_IMAGES y con la variante de photo picker embebida. Muy útil para apps donde el video vertical o la fotografía en retrato es el caso de uso principal.

RAW14: formato RAW de 14 bits

ImageFormat.RAW14 es una nueva constante para imágenes RAW de 14 bits por píxel, empaquetadas de forma compacta (4 píxeles en 7 bytes). Las apps de cámara profesional con acceso a sensores compatibles pueden ahora capturar la máxima profundidad de color sin trucos con formatos personalizados.

Consulta del tipo de dispositivo de cámara

Ahora es posible consultar si una cámara es hardware integrado, una webcam USB externa, o una cámara virtual — sin recurrir a workarounds de CameraCharacteristics.

Extensiones de cámara definidas por el fabricante

Los fabricantes de hardware pueden definir modos de extensión propios — Super Resolución, mejoras nocturnas con IA, o mejoras exclusivas del fabricante. Se consulta el soporte con isExtensionSupported(int) en CameraExtensionCharacteristics.

Soporte para audífonos Bluetooth LE Audio

Nueva constante AudioDeviceInfo.TYPE_BLE_HEARING_AID para distinguir audífonos de prótesis auditivas en los dispositivos LE Audio genéricos:

val devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
val hayAudifonoConectado = devices.any {
    it.type == AudioDeviceInfo.TYPE_BLE_HEARING_AID
}

Esto también habilita el enrutamiento granular de audio hacia audífonos — los usuarios pueden enrutar notificaciones, tonos de llamada y alarmas de forma independiente hacia el audífono o el altavoz del dispositivo, sin cambios en la app.

Codificador de software HE-AAC extendido

Nuevo MediaCodec provisto por el sistema (c2.android.xheaac.encoder) para codificación eficiente de voz y audio. Unifica voz y audio en un solo codec, con metadatos de loudness obligatorios para volumen consistente en condiciones de bajo ancho de banda.

val encoder = MediaCodec.createByCodecName("c2.android.xheaac.encoder")
val format = MediaFormat.createAudioFormat(
    MediaFormat.MIMETYPE_AUDIO_AAC,
    48000,
    1
)
format.setInteger(
    MediaFormat.KEY_AAC_PROFILE,
    MediaCodecInfo.CodecProfileLevel.AACObjectXHE
)

Privacidad y seguridad

Botón de ubicación provisto por el sistema

Un nuevo botón renderizado por el sistema (entregado vía Jetpack) que otorga ubicación precisa de una sola vez para la sesión actual — sin diálogo de permisos del sistema. Requiere el nuevo permiso USE_LOCATION_BUTTON.

Una mejora UX significativa para apps donde el usuario quiere compartir su ubicación en una acción específica (pedir comida, compartir con un amigo) sin otorgar acceso permanente.

Visibilidad de contraseñas diferenciada

La opción “Mostrar contraseñas” ahora se divide en dos:

  • Teclado táctil/virtual: muestra brevemente el último carácter escrito (igual que antes)
  • Teclado físico: oculto inmediatamente por defecto

Si tu app usa campos de texto personalizados, usa la nueva API ShowSecretsSetting:

val esTecladoFisico =
    event.source and InputDevice.SOURCE_KEYBOARD == InputDevice.SOURCE_KEYBOARD
val mostrarContrasena =
    android.text.ShowSecretsSetting.shouldShowPassword(context, esTecladoFisico)

Firma de APK post-cuántica

Android 17 introduce el esquema de firma APK v3.2 con un enfoque híbrido: firmas RSA o Curva Elíptica clásicas combinadas con ML-DSA (Module Lattice Digital Signature Algorithm — un algoritmo post-cuántico estandarizado por NIST). Prepara el ecosistema ante amenazas de computación cuántica sin romper la compatibilidad actual.

Carga dinámica de código nativo — aplicación de solo lectura

Desde Android 14, los archivos Java/DEX cargados dinámicamente deben ser de solo lectura. Android 17 extiende esta restricción a las librerías nativas para las apps que apuntan a API 37. Si una de esas apps llama a System.load() sobre un archivo nativo que no está marcado como solo lectura, ahora lanza UnsatisfiedLinkError. Revisa tus rutas de carga de librerías nativas.

Certificate Transparency — activado por defecto

Lo que era una función opt-in en Android 16 ahora está activada por defecto para las apps que apuntan a API 37. No se requieren cambios en apps que usen HTTPS estándar — pero si haces validación de certificados manual o certificate pinning, revisa tu implementación.


Rendimiento y batería

AlarmManager: alarmas exactas con callback listener

Nueva sobrecarga de setExactAndAllowWhileIdle() que acepta un OnAlarmListener en lugar de un PendingIntent. Pensado para apps que actualmente usan wakelocks continuos para temporización precisa:

alarmManager.setExactAndAllowWhileIdle(
    AlarmManager.RTC_WAKEUP,
    triggerAtMillis,
    "com.example.MI_ALARMA",
    executor,
    new AlarmManager.OnAlarmListener() {
        @Override
        public void onAlarm() {
            // Manejar la alarma
        }
    }
);

Nota: CommonsWare señaló que la documentación de esta API es contradictoria — dice apuntar a apps “que dependen de wakelocks continuos” pero también promete “reducir wakelocks”. La intención parece ser una alternativa más precisa a los wakelocks de larga duración.


Experiencia de usuario y UI del sistema

Widgets en pantallas externas

RemoteViews.setViewPadding() ahora acepta unidades complejas (DP y SP, no solo píxeles). Los widgets pueden obtener el ID de la pantalla en la que se renderizan:

val displayId = appWidgetManager
    .getAppWidgetOptions(appWidgetId)
    .getInt(AppWidgetManager.OPTION_APPWIDGET_DISPLAY_ID)

Permite adaptar el layout del widget para monitores externos con distintas densidades de píxeles — crítico para el Modo Escritorio.

Picture-in-Picture interactivo en modo escritorio

Las apps pueden solicitar una capa de ventana anclada en modo escritorio. La ventana es siempre-en-frente y completamente interactiva. Requiere el nuevo permiso USE_PINNED_WINDOWING_LAYER:

appTask.requestWindowingLayer(
    ActivityManager.AppTask.WINDOWING_LAYER_PINNED,
    context.mainExecutor,
    object : OutcomeReceiver<Int, Exception> {
        override fun onResult(result: Int) { /* éxito */ }
        override fun onError(e: Exception) { /* manejar */ }
    }
)

Etiquetas de apps ocultas

Los usuarios pueden ocultar los nombres de las apps en la pantalla de inicio. Recomendación de Google: asegúrate de que el ícono de tu app sea reconocible sin su etiqueta.

Toolbar de grabación de pantalla rediseñado

Nueva barra de herramientas flotante para los controles de grabación. El toolbar se excluye del video final capturado.


Cambios de comportamiento (breaking changes)

Para todas las apps

Ninguno de los cambios que Google destacó en la Beta 3 aplica a todas las apps. Una versión anterior de esta parte listaba aquí el DCL nativo, Certificate Transparency y el acceso a red local; el post de la Beta 3 de Google limita los tres a las apps que apuntan a API 37, así que están en la tabla siguiente. La lista final de cambios para todas las apps está en la parte de la versión estable.

Para apps apuntando a API 37

CambioQué hacer
Redimensionamiento en pantallas grandes obligatorioProbar en plegables y tablets — no puedes optar por no cumplir las restricciones
DCL nativo aplicadoAsegúrate de que las librerías nativas en System.load() estén en rutas de solo lectura
Certificate Transparency por defectoRevisa validaciones de certificados personalizadas
Acceso a red local bloqueadoPide ACCESS_LOCAL_NETWORK, o usa un selector de dispositivos del sistema, para llegar a dispositivos LAN

Cambios de API en el SDK 37, que aparecen al compilar contra él, apuntes a lo que apuntes:

  • ACTION_TAG_DISCOVERED está deprecado. Pasa a ACTION_NDEF_DISCOVERED o ACTION_TECH_DISCOVERED.
  • DnsResolver.getInstance() está deprecado, no eliminado. El SDK 37 agrega un constructor DnsResolver(Context, Looper) para usar en su lugar.
  • String.getChars() sigue ahí. El diff de API lo lista entre las eliminaciones, pero el método está en la API pública en el tag android-17.0.0_r1. Lo que cambió es que CharSequence ganó un getChars() por defecto de OpenJDK 25. No hace falta migrar nada.

APIs poco documentadas

CommonsWare identificó varias APIs nuevas en el diff sin documentación clara:

  • SerialManager — acceso a puertos serie, alcance incierto
  • WebAppManager — relación con el navegador predeterminado desconocida
  • FileManager — I/O de disco en segundo plano para apps privilegiadas
  • Notificaciones bridgeadas — desde otros dispositivos conectados
  • Registro de advertencia ANR — callbacks de timeout antes de que ocurra un ANR

Por ahora, tratar estas como APIs internas o para socios.


Correcciones de estabilidad en Beta 3

Beta 3 incluye un número inusualmente grande de correcciones de estabilidad, lo que sugiere que las betas anteriores tenían problemas significativos de fiabilidad:

  • Regresión del ciclo de vida del proceso (Android 16): reinicios aleatorios de apps y parpadeo de pantalla
  • Cámara: fallos al cambiar al teleobjetivo 5x, tartamudeo en la transición de lentes
  • Android Auto: congelamiento de pantalla de bloqueo tras desconexión
  • Reinicios espontáneos: más de 40 problemas relacionados, cuelgues durante el reposo
  • Bluetooth: emparejamiento bloqueado durante 150 segundos
  • Artefactos de UI: íconos de la barra de estado desapareciendo, fallos al descartar notificaciones

Qué hacer ahora

  1. Autores de librerías/SDKs: publica tu actualización de compatibilidad con Android 17. Las APIs están congeladas.
  2. Desarrolladores de apps: prueba tu app en el emulador o imagen de sistema Beta 3. Presta atención a los cambios de DCL nativo y acceso a red local.
  3. Prueba en pantallas grandes: el cambio de redimensionamiento obligatorio es el más probable de generar problemas en plegables y tablets.
  4. Revisa tu código NFC: si usas ACTION_TAG_DISCOVERED, empieza a planificar la migración.
  5. Sube tu app a Google Play apuntando a API 37 — ya puedes hacerlo.

Recursos

Parte 4 de 6

2 min

Android 17 Beta 4: la última beta prevista

16 de abril de 2026. La última beta prevista trajo límites de memoria según el RAM, un disparador de perfilado de anomalías y claves ML-DSA, y suavizó las reglas de audio.

Leer por separado: Android 17 Beta 4: la última beta prevista

La Beta 4 salió el 16 de abril de 2026, tres semanas después de la Beta 3. Google la llamó “the last scheduled beta of this release cycle”. La superficie de APIs ya estaba cerrada, así que trajo pocas APIs nuevas y un cambio que nota toda app: los límites de memoria.

Fecha16 de abril de 2026; Beta 4.1 el 1 de junio de 2026
BuildCP21.260330.008; Beta 4.1 CP21.260330.011
Parche de seguridad2026-04-05; Beta 4.1 2026-05-05
DispositivosTeléfonos y tablets Pixel compatibles por OTA; imágenes de 64 bits del Android Emulator
Estabilidad de plataformaSí, desde la Beta 3

Qué agregó la Beta 4

  • Límites de memoria por app. El sistema mata la app cuyo uso de memoria supera un límite que depende del RAM total del dispositivo. Google fijó los límites de forma conservadora en Android 17, para atrapar fugas extremas y casos atípicos. Una app afectada encuentra "MemoryLimiter" en ApplicationExitInfo.getDescription(). La parte sobre límites de memoria, más abajo, lo cubre a fondo.
  • TRIGGER_TYPE_ANOMALY. Un nuevo disparador de ProfilingManager entrega a la app un heap dump cuando supera el límite de memoria, o una muestra de pilas de binder cuando hace demasiadas llamadas binder. El callback llega antes de que el sistema aplique cualquier medida.
  • ML-DSA en Android Keystore. En dispositivos compatibles, las apps pueden generar claves ML-DSA-65 y ML-DSA-87 en hardware seguro con KeyPairGenerator, KeyFactory y Signature.
  • LeakCanary en Android Studio. Android Studio Panda suma LeakCanary como una tarea del Profiler.

Qué cambió la Beta 4

  • Audio en segundo plano, más flexible. Tras los comentarios de los desarrolladores, Google limitó la exigencia de un servicio en primer plano con capacidad while-in-use a las apps con target 37 y eximió el audio de alarmas.
  • Loopback entre perfiles bloqueado. El tráfico loopback entre perfiles deja de estar permitido por defecto, para toda app en Android 17. El tráfico dentro de un mismo perfil no cambia.

La Beta 4.1, una actualización de correcciones, llegó el 1 de junio. Android 17 salió el 16 de junio.

Parte 5 de 6

12 min

Android 17: qué rompe en targetSdk 37

De la Beta 3 al release del 16 de junio — los cambios que quedan dormidos hasta que subes targetSdk a 37, y las dos reescrituras de runtime debajo.

Leer por separado: Android 17: qué rompe en targetSdk 37

Android 17 — API 37, nombre en código interno Cinnamon Bun — pasó a estable el 16 de junio de 2026, junto con el Pixel Drop de junio, en Pixel 6 y posteriores. El resto de los fabricantes se viene sumando al rollout desde entonces. La superficie de API se bloqueó en la Beta 3, el 26 de marzo, cuando Google declaró platform stability; la Beta 4, la última programada, salió el 16 de abril. Mi parte sobre la Beta 3 cubre API 37 en platform stability; esta cubre lo que pasó después, y lo que realmente te cuesta tiempo.

Seis semanas después, el encuadre útil no es la lista de features. Es este: casi todo lo que va a romper tu app está dormido ahora mismo. Se dispara cuando subes targetSdk a 37, no cuando un usuario actualiza el teléfono. O sea que tú eliges el día, y eso conviene usarlo a propósito.

El proceso cambió, y esa es la noticia real

Por primera vez en años, Google no publicó ningún Developer Preview. Android 17 fue directo a Beta.

No es un atajo. Es el canal Canary haciendo el trabajo que hacían los Developer Preview: las APIs y features se prueban continuamente ahí, así que un build de preview aparte dejó de ganarse su lugar en el calendario. La consecuencia para planificar es concreta — la ventana entre “primer release compilable” y “platform stability” es más corta, y si tu proceso asumía una fase de DP para absorber sorpresas, esa fase ya no existe.

Qué rompe en targetSdk 37

La redimensionabilidad en pantallas grandes ya no es opcional

Las apps que apunten a Android 17 ya no pueden optar por salirse de las restricciones de orientación, redimensionamiento y aspect ratio en pantallas cuyo ancho mínimo supera los 600dp. Los flags del manifest que fijaban una app en vertical dejan de respetarse, la app ocupa toda la ventana del display, y ya no hay pillarboxing donde esconderse.

Este es el que hay que planificar, no parchear. Si tu layout asumía una orientación fija, va a ser redimensionado en una tablet o un plegable y vas a descubrir dónde viven los supuestos. Google plantea Android 17 como el paso a un estándar “adaptive-first”, y esta es la cláusula que lo hace cumplir.

La exención es más angosta que “los juegos zafan”: aplica a las apps que declaran android:appCategory="game". Es una declaración en el manifest, no una interpretación. Google todavía no anunció la fecha límite de Play para API 37 (a octubre de 2026); la regla vigente pide API 36 desde el 31 de agosto de 2026, y cada año hasta ahora subió la vara un nivel. Planifica como si API 37 siguiera el mismo camino — la exención te compra tiempo de layout, no una salida.

System.load() exige librerías nativas de solo lectura

Todos los archivos nativos cargados con System.load() deben estar marcados como solo lectura. Si no, el sistema lanza UnsatisfiedLinkError.

Si distribuyes código nativo que se extrae, se copia o se descarga en runtime y después se carga desde una ubicación escribible, ese camino ahora lanza excepción. Es una restricción de Dynamic Code Loading: una librería que todavía puedes escribir es una librería que un atacante todavía puede reescribir entre el chequeo y la carga.

Vale hacer un grep de System.load( — no de loadLibrary, que resuelve desde el APK y no está afectado.

El acceso a la red local está bloqueado por defecto

Las apps que apunten a 17 tienen el acceso a la red local bloqueado por defecto. mDNS, sockets, descubrimiento de dispositivos — cualquier cosa que llegue a una impresora, un Chromecast, una lámpara inteligente, un servidor de desarrollo local u otro teléfono en la misma Wi-Fi.

Hay dos caminos de vuelta, y el orden importa:

  1. Un selector de dispositivos mediado por el sistema. El usuario elige el dispositivo, el sistema otorga el alcance, y tú no muestras ningún prompt de permiso.
  2. El nuevo permiso runtime ACCESS_LOCAL_NETWORK, pedido explícitamente, para comunicación persistente con la LAN.

Ir al permiso primero es el reflejo, y es la peor opción: pagas un prompt, una tasa de rechazo y una justificación que tienes que escribir. Prueba el selector antes de decidir que necesitas acceso amplio.

El audio en background exige un foreground service

En Android 17, toda app — apunte a lo que apunte — necesita una activity visible o un foreground service corriendo (que no sea de tipo SHORT_SERVICE) para reproducir audio, pedir foco de audio o cambiar el volumen. Una app que apunta a 37 y lo hace desde background además tiene que cumplir una de dos condiciones:

  • el foreground service tiene capacidades while-in-use (WIU), o
  • la app tiene el permiso de alarma exacta y está manejando streams USAGE_ALARM.

Ojo con lo que exige la plataforma: un foreground service con la capacidad correcta. MediaSessionService no está en la regla; es la forma práctica de cumplirla. Media3 configura, arranca y para ese servicio por ti y publica la notificación de media, así que una app construida sobre MediaSessionService ya está mayormente en regla.

Lo que convierte esto en la fecha límite de ExoPlayer 2. No tiene mantenimiento, no gestiona el ciclo de vida del foreground service, y las reglas nuevas están modeladas alrededor del manejo de sesión y foco de Media3. Nada bloquea de golpe a ExoPlayer 2 — simplemente vas a tener reproducción interrumpida, foco perdido y notificaciones rotas, que es una falla peor que un crash porque nadie reporta un bug por eso.

Apunte a lo que apunte, la falla es la misma: las APIs de audio llamadas fuera de un ciclo de vida válido fallan en silencio, y el foco de audio devuelve AUDIOFOCUS_REQUEST_FAILED.

La reflection sobre la plataforma deja de funcionar

Dos cambios, una misma causa, y caen sobre tus dependencias más que sobre tu código.

Los campos static final ya no se pueden cambiar. Una app corriendo en 17 y apuntando a 37 que reescriba uno por reflection recibe un IllegalAccessException; hacerlo por JNI (SetStaticLongField() y compañía) crashea el proceso. Los dobles de prueba, los shims de feature flags y los SDK de analytics hacen esto más de lo que uno supone.

MessageQueue es una implementación nueva sin locks. La vieja usaba un único lock para la cola del hilo principal, así que un hilo de fondo podía bloquear al principal — contención que aparecía como frames perdidos. La reescritura lo elimina. El costo es que todo lo que reflexione sobre los internos de MessageQueue se rompe: mMessages sigue existiendo por compatibilidad binaria, pero siempre es null, haya mensajes pendientes o no. Espresso y Robolectric son los primeros que vas a tocar, así que actualiza esas librerías antes de mover targetSdk.

Audita el árbol de dependencias antes del bump, no después. Un null silencioso en un campo que antes traía datos es el tipo de falla más caro de rastrear.

Los SMS con OTP llegan tres horas tarde

Para la mayoría de las apps que apuntan a 17, los SMS con código de un solo uso no se pueden leer hasta tres horas después de llegar. Durante esa ventana el broadcast SMS_RECEIVED_ACTION se retiene y las consultas al proveedor de SMS se filtran. La app de SMS por defecto, la app de asistente y las apps de dispositivos vinculados están exentas.

La misma retención de tres horas aplica a los mensajes en formato WebOTP sin importar el targetSdk, cuando la app no es el destinatario verificado por dominio.

Tres horas no es una demora con la que se diseña: es un mensaje de que leer la bandeja se terminó. Si todavía parseas SMS directamente, pásate a SMS Retriever o SMS User Consent — los dos te entregan el código sin que la app lea los mensajes del usuario, que es justamente el punto.

Certificate Transparency viene activado

CT era opt-in en Android 16. Para las apps que apuntan a 17 está activado por defecto.

Para casi todos esto es invisible y bueno. Muerde en un solo lugar: si pineas o inyectas un certificado que no está en un log público de CT — un proxy corporativo, un arnés de pruebas, una CA interna — conexiones que antes funcionaban van a fallar. Prueba tu entorno de staging antes de asumir que producción está bien.

Los contactos se angostan

Se movieron dos cosas, y solo una rompe.

La que rompe: para las apps que apuntan a 37, Contacts Provider 2 esconde las columnas con PII de la vista de datos — ACCOUNT_NAME, ACCOUNT_TYPE, ACCOUNT_TYPE_AND_DATA_SET. Si las consultas, cuenta con que no van a estar.

La otra es una oportunidad. Android 17 trae un Contact Picker estandarizado: una UI del sistema navegable donde declaras los campos que necesitas — teléfonos, mails — y el usuario te entrega contactos puntuales. READ_CONTACTS no está deprecado, pero ahora es la opción bruta. Si pedías la agenda entera porque no había alternativa, ahora la hay.

El recolector de basura se volvió generacional

El Concurrent Mark-Compact de ART ahora es generacional. En vez de tratar a todos los objetos igual y barrer el heap completo, corre recolecciones de generación joven, frecuentes y baratas, y reserva las pasadas de heap completo para cuando hacen falta de verdad.

No es nuevo en 17, aunque el post de lanzamiento de Google lo presente así. Google ya incluyó el recolector CMC generacional en el post del lanzamiento de Android 16 QPR2 en diciembre de 2025, y AOSP coincide: el flag use_generational_cmc aparece ENABLED por primera vez en la configuración de release bp4a, la del build de Android 16 QPR2 (BP4A.251205.006). La configuración de Android 17, cp1a, hereda de bp4a, así que 17 lo mantiene activado.

La premisa es la observación más vieja de la recolección de basura: la mayoría de los objetos muere joven. Actuar sobre eso baja el costo de CPU y la duración de la recolección, lo que se ve como menos frames perdidos y menos batería quemada haciendo contabilidad.

Esto lo tienes gratis — sin targetSdk, sin cambiar código. Y como ART se distribuye por Google Play system updates, las mejoras de runtime llegan a dispositivos hasta API 31, no solo a los que se actualizaron de OS.

La firma post-cuántica ya está en Keystore

Android Keystore ahora soporta ML-DSA — Module-Lattice-Based Digital Signature Algorithm, el estándar post-cuántico del NIST — en dos variantes, ML-DSA-65 y ML-DSA-87.

La parte que importa para la adopción: está expuesto a través de las APIs estándar de Java Cryptographic Architecture. KeyPairGenerator, KeyFactory, Signature. Ninguna superficie nueva que aprender, ningún SDK de proveedor. Si hoy generas claves de firma en hardware, la forma del código no cambia — cambia el string del algoritmo.

Si ya lo necesitas es otra pregunta. Las firmas post-cuánticas importan donde una firma tiene que seguir siendo verificable durante años y un adversario puede guardar el tráfico de hoy para atacarlo después. Para un token de sesión que expira en una hora, no. Está bien que la plataforma esté lista antes de que la necesidad sea urgente: ese es el orden correcto en las migraciones criptográficas.

Límites de memoria, en breve

Android 17 aplica límites de memoria por app derivados del RAM del dispositivo, y un proceso que los excede muere sin stack trace. ApplicationExitInfo es donde te enteras de que pasó.

Este merece más que un párrafo, y tiene su propia parte: Android 17 mata tu proceso sin stack trace.

Qué recibieron los usuarios

Dos cosas que vale conocer porque llegan preguntas de soporte por ellas:

App Bubbles para cualquier app. Presión larga sobre una app en la pantalla de inicio y se convierte en una burbuja flotante estilo chat head. Antes las burbujas eran una función de mensajería; ahora cualquier app puede serlo, lo que significa que a tu UI le pueden pedir que se dibuje en una ventana flotante chiquita para la que nunca fue diseñada.

“Marcar como perdido” de Find Hub puede exigir biometría. Bloquear un dispositivo perdido ahora puede pedir autenticación biométrica además del código, así que alguien que te vio escribir el PIN igual no puede desactivar el rastreo. Un cambio chico con un modelo de amenaza claro detrás.

Qué haría esta semana

  1. Compila contra SDK 37 en CI, sin cambiar targetSdk. Obtienes señal de deprecaciones y de superficie de API con riesgo de comportamiento cero.
  2. Audita tus dependencias por reflection. Las librerías de testing, feature flags y analytics son las que reescriben campos static final y leen los internos de MessageQueue. Actualiza Espresso y Robolectric primero.
  3. Grep de System.load(. El chequeo más rápido posible para el breaking change que más fácil se pasa por alto.
  4. Prueba en un plegable o un emulador de tablet en horizontal. No porque vaya a verse mal — porque ahora va a pasar, hayas optado o no.
  5. Inventaría tus permisos. Todo lo que toque la LAN, la agenda o la bandeja de SMS ahora tiene un camino más angosto y mediado por el sistema que le gana a pedir.
  6. Corre tu camino de TLS en staging. CT por defecto falla ruidosamente, pero solo donde tengas un certificado no público, y eso normalmente no es producción.
  7. Después sube targetSdk, a propósito, con tiempo para reaccionar.

Si manejas un equipo, no esperes a que el plazo de Play te fuerce el número. La auditoría es la parte lenta, no el bump.

El patrón de este release es consistente: la plataforma está cerrando caminos que antes eran opcionales — salirse de la orientación, librerías nativas escribibles, acceso irrestricto a la LAN, lecturas amplias de contactos, scraping de SMS, reflection sobre los internos, memoria sin techo. Cada uno era un lugar donde una app podía ser descuidada y el costo caía en otra parte. Es una dirección defendible. También significa que “sigue funcionando” deja de ser evidencia de nada el día que cambias un número.

Parte 6 de 6

7 min

Android 17 mata tu proceso sin stack trace

Los límites de memoria por app son límites de cgroup atados al RAM del dispositivo, no se pueden consultar en runtime, y la muerte no deja crash log.

Leer por separado: Android 17 mata tu proceso sin stack trace

Android 17 aplica límites de memoria por app. Cuando tu proceso cruza uno, el sistema lo mata — y no hay stack trace, no hay OutOfMemoryError, no hay nada en tu reportador de crashes que parezca un crash. Desde adentro es indistinguible de que el proceso simplemente deje de existir.

Ese es todo el problema. No el límite. El silencio.

Qué se está limitando realmente

El blog de desarrolladores de Google dice “límites de memoria basados en el RAM del dispositivo”. La documentación de AOSP dice qué significa eso, y es más específico de lo que el blog sugiere.

El Memory Limiter fija dos valores de cgroup por proceso:

ValorEfecto
memory.highLímite blando. El kernel estrangula el proceso y reclama memoria
memory.swap.maxLimita cuánto swap puede tomar el proceso

La métrica es memoria anónima — heap más asignaciones nativas que no están respaldadas por un archivo. Las páginas respaldadas por archivo se desalojan; las anónimas van a swap. Por eso la descripción de salida dice AnonSwap.

Fíjate qué es memory.high: un límite blando. El kernel no te mata inmediatamente por cruzarlo. Te estrangula e intenta reclamar. La muerte llega después, cuando el reclamo no alcanza y una asignación falla. Por eso no hay un OutOfMemoryError limpio que atrapar — a esa altura te están deteniendo, no preguntando.

Tres hechos que cambian cómo planificas

1. No puedes consultar el límite. A partir de Android 17 — SDK 37 — no hay API en runtime para preguntar cuál es tu límite. Tu app no puede leerlo, loguearlo, ni adaptarse a él. Cualquier estrategia que implique “consultar el presupuesto y quedarse abajo” no está disponible. En un dispositivo de pruebas sí puedes verlo: adb shell am memory-limiter status muestra los límites para procesos visibles y no visibles, y am memory-limiter manual <pid> <MB> impone uno más estricto para que veas a tu app chocar contra él.

2. Quién fija los números depende de qué fuente leas. Los límites son bloques <limitSet> en un memory-limiter-config.xml, que declaran minimumRequiredMemTotal, maximumAllowedMemTotal, memVisible, memNotVisible, swapVisible y swapNotVisible en MiB. Las dos fuentes primarias no coinciden en dónde vive ese archivo:

  • El código publicado (MemoryLimiter.java en android-17.0.0_r1) lee /vendor/etc/memory-limiter-config.xml, la partición que controla el fabricante. La documentación de AOSP decía lo mismo en julio de 2026.
  • La documentación actual (actualizada desde agosto de 2026) dice /system/etc/memory-limiter-config.xml y define límites estándar de plataforma por nivel de RAM. Nombra la “consistencia del ecosistema” como principio y no admite listas de excepciones por app.

Las dos ponen a los procesos visibles en aproximadamente 1/2 a 2/3 del RAM físico y a los no visibles en 1/4 a 1/3. Los niveles estándar, según el MemTotal del kernel:

RAM del dispositivoRango de MemTotal (MiB)VisibleNo visible
16 GB14336 a 1843110 GiB5 GiB
12 GB9216 a 143358 GiB4 GiB
8 GB6800 a 92155 GiB3 GiB
6 GB4800 a 67994 GiB2 GiB
4 GB3200 a 47992 GiB1 GiB

Los límites de swap coinciden con el límite de memoria no visible en cada nivel.

3. Algunos dispositivos no aplican nada. En el código publicado, si el archivo de configuración falta o es inválido, el limiter queda desactivado. Con los niveles estándar, los dispositivos con 18.432 MiB de MemTotal o más, o con menos de 3.200, no tienen límites. “Funciona en mi teléfono” nunca fue evidencia más débil que aquí.

Qué procesos están exentos

Solo PERSISTENT y PERSISTENT_UI — procesos del sistema. Todo lo que una app puede ser está monitoreado: TOP, FOREGROUND_SERVICE, IMPORTANT_FOREGROUND, TRANSIENT_BACKGROUND, BACKUP, SERVICE, RECEIVER, CACHED.

Los procesos cacheados reciben el trato más duro: reclamados al máximo y congelados. Que es el punto de la función, dicho sin vueltas en el propio planteo de Google:

Cuando una app se infla o pierde memoria mientras mantiene un estado privilegiado, el LMK se ve forzado a compensar matando decenas de apps cacheadas más chicas y bien comportadas.

Una app codiciosa le costaba a todas las demás su estado tibio. El limiter hace que pague la codiciosa. Difícil discutir la dirección.

Cómo detectarlo

Esta es la parte accionable. La muerte es silenciosa, pero queda registrada.

val activityManager = getSystemService(ActivityManager::class.java)

activityManager.getHistoricalProcessExitReasons(packageName, 0, 10)
    .filter { it.reason == ApplicationExitInfo.REASON_OTHER }
    .filter { it.description?.contains("MemoryLimiter") == true }
    .forEach { exit ->
        // la descripción contiene "MemoryLimiter:AnonSwap"
        report("memory-limit-kill", exit.description, exit.timestamp)
    }

Dos detalles que importan:

  • La razón es REASON_OTHER, no una constante dedicada. Tienes que matchear contra el string de descripción, lo que significa que un cambio de redacción en la plataforma puede romper tu detección en silencio. Matchea contra "MemoryLimiter", no contra el "MemoryLimiter:AnonSwap" completo.
  • Lee esto en el siguiente arranque, no en el momento de la muerte — no hay momento de la muerte que puedas observar. getHistoricalProcessExitReasons es la única ventana.

Si no agregas algo así, estas muertes llegan como una caída inexplicable en la duración de sesión y una suba de cold starts, sin nada en Crashlytics que las conecte.

Atraparlo antes que la plataforma

ProfilingManager — introducido en Android 15, extendido en 17 — te da dos disparadores que vale cablear:

  • TRIGGER_TYPE_OOM recolecta un heap dump de Java en el momento de un OutOfMemoryError. El dump que siempre quisiste y nunca tuviste.
  • TRIGGER_TYPE_ANOMALY se dispara ante problemas severos de rendimiento, incluyendo umbrales de memoria que se están acercando — antes de que el sistema aplique el límite.
val profilingManager = getSystemService(ProfilingManager::class.java)
profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback)
profilingManager.addProfilingTriggers(triggers)

TRIGGER_TYPE_ANOMALY es el que cambia el juego: es lo más cercano a la API de consulta que no existe. No puedes preguntar cuál es tu límite, pero sí te pueden avisar cuando te estás acercando.

Qué reduce el número de verdad

Nada de esto es consejo nuevo. Es consejo recién obligatorio, que es distinto.

Activa R8 bien. isMinifyEnabled = true, isShrinkResources = true, y proguard-android-optimize.txt en lugar de la variante que no optimiza. Una cantidad sorprendente de proyectos tiene los dos primeros y no el tercero.

Baja la resolución de las imágenes al decodificar. inSampleSize cuando conoces el tamaño destino, y RGB_565 cuando no necesitas transparencia — eso es la mitad de bytes por pixel que ARGB_8888. Un bitmap a resolución completa para un thumbnail es la forma más común de desperdiciar decenas de megabytes.

Responde a onTrimMemory. Específicamente TRIM_MEMORY_UI_HIDDEN — el usuario se fue, tus vistas no están visibles, y lo que cacheaste para dibujar es costo puro — y TRIM_MEMORY_BACKGROUND. Casi todas las apps sobreescriben este método y no liberan nada significativo dentro.

Encuentra los leaks. LeakCanary ahora se integra al profiler de Android Studio directamente en Panda 3. Un leak bajo un límite de memoria ya no es una degradación lenta; es una muerte segura con temporizador.

Qué haría esta semana con la memoria

  1. Envía primero el chequeo de ApplicationExitInfo. Son una docena de líneas y sin eso estás ciego. No puedes arreglar una tasa que no puedes ver.
  2. Cablea TRIGGER_TYPE_ANOMALY. Es la única señal que mira para adelante.
  3. Revisa el tercer flag de R8. Dos minutos, y a menudo una reducción real.
  4. Audita los caminos de decodificación de bitmaps. Donde suelen estar los megabytes.
  5. No ajustes contra un número. Tu app no puede leerlo, depende del nivel de RAM del dispositivo, y en algunos dispositivos no existe. Construye margen, no un objetivo.

El resumen honesto: es un buen cambio con un mal modo de falla. Hacer que una app hambrienta de memoria cargue con su propio costo — en vez de que lo paguen todas las demás apps del teléfono — es la decisión correcta. Hacerlo con una muerte que no deja rastro significa que las apps más afectadas son las que menos probablemente se enteren. Por eso la detección, y no la optimización, es lo primero que hay que publicar.

Clasificado en

Citas

42

Referencias externas