Restricciones en interfaces que no pertenecen al SDK

A partir de Android 9 (nivel de API 28), la plataforma restringe las interfaces no pertenecientes al SDK que se pueden usar en tu app. Estas restricciones se aplican cada vez que una app hace referencia a una interfaz que no pertenece al SDK o intenta obtener su controlador mediante reflejo o JNI, y se implementaron para ayudar a mejorar la experiencia del usuario y el desarrollador, lo cual se traduce en menos riesgos de fallas para los usuarios y menos lanzamientos de emergencia para los desarrolladores. Si quieres obtener más información acerca de esta decisión, consulta el artículo sobre cómo reducir el uso de interfaces que no pertenecen al SDK para mejorar la estabilidad.

Cómo diferenciar entre las interfaces que pertenecen al SDK y las que no

En general, las interfaces públicas del SDK son aquellas que están documentadas en el Índice del paquete del marco de trabajo de Android. El control de las interfaces que no pertenecen al SDK es un detalle de la implementación que abstrae la API, de manera que están sujetas a cambios sin previo aviso.

Para evitar que se produzcan fallas y comportamientos inesperados, las apps deben utilizar únicamente las partes de las clases documentadas oficialmente en el SDK. Esta premisa también significa que no debes acceder a métodos o campos que no aparezcan en el SDK cuando interactúas con una clase por medio de mecanismos como el reflejo.

Listas de API que no pertenecen al SDK

Con cada versión de Android, se restringen interfaces adicionales que no pertenecen al SDK. Sabemos que estas restricciones pueden afectar el flujo de trabajo de tus actualizaciones y queremos asegurarnos de que poseas las herramientas necesarias para detectar el uso de interfaces que no pertenecen al SDK, tengas la oportunidad de brindarnos comentarios y cuentes con tiempo para planificar y adaptarte a las nuevas políticas.

Con el objetivo de minimizar el impacto de las restricciones para las interfaces que no pertenecen al SDK en el flujo de trabajo de desarrollo, estos tipos de vistas se dividen en listas que definen el grado de restricción de uso en función del nivel de la API al que está orientada tu app. En la siguiente tabla, se describen estas listas:

Lista Descripción
Lista de elementos bloqueados (blacklist) Incluye interfaces que no pertenecen al SDK y que no puedes usar, sin importar el nivel de API al que se orienta la app. Si tu app intenta acceder a una de estas interfaces, se generará un error en el sistema.
Elementos bloqueados de forma condicional (greylist-max-x)

A partir de Android 9 (nivel de API 28), cada nivel de API tiene interfaces que no pertenecen al SDK y se encuentran restringidas cuando una app se orienta a ese nivel.

Estas listas están etiquetadas según el nivel máximo de API (max-target-x) al que puede orientarse una app antes de que ya no pueda acceder a las interfaces de esa lista que no pertenecen al SDK. Por ejemplo, una interfaz que no pertenece al SDK y que no se bloqueó en Android Pie, pero que ahora está bloqueada en Android 10, es parte de la lista max-target-p (greylist-max-p), donde "p" significa Pie o Android 9 (nivel de API 28).

Si tu app intenta acceder a una interfaz restringida para el nivel de API al que se orienta, el sistema se comporta como si la API fuera parte de la lista de elementos bloqueados.

No compatible (greylist) Son interfaces que no pertenecen al SDK y que no tienen restricciones, por lo que la app puede usarlas. Sin embargo, ten en cuenta que estas interfaces no son compatibles y están sujetas a cambios sin previo aviso. Se espera que estas interfaces se bloqueen de forma condicional en versiones futuras de Android en una lista max-target-x.
SDK (whitelist) Incluye interfaces que se pueden usar sin restricciones y se admiten como parte del Índice del paquete del marco de trabajo de Android documentado oficialmente.

Si bien actualmente puedes usar algunas interfaces que no pertenecen al SDK (según el nivel de API al que se oriente la app), usar cualquier método o campo que no pertenece al SDK siempre implica un gran riesgo de error para tu app. Si la app depende de interfaces que no pertenecen al SDK, debes comenzar a planificar una migración hacia interfaces de SDK o cualquier otra alternativa. Si no encuentras otra opción para reemplazar el uso de una interfaz que no pertenece al SDK para una función de la app, debes solicitar una nueva API pública.

Determina a qué lista pertenece una interfaz

Las listas de interfaces que no pertenecen al SDK se compilan como parte de la plataforma. Consulta las siguientes secciones para obtener información sobre cada versión de Android.

Android 11

En Android 11 (nivel de API 30), el siguiente archivo describe todas las interfaces que no pertenecen al SDK y sus correspondientes listas: hiddenapi-flags.csv.

Para obtener más información sobre los cambios en la lista de API que no pertenecen al SDK en Android 11, incluidas las alternativas a la API pública sugeridas para las API que se bloquean de forma condicional en Android 11, consulta Cambios de lista para Android 11.

Android 10

En Android 10 (nivel de API 29), el siguiente archivo describe todas las interfaces que no pertenecen al SDK y sus correspondientes listas: hiddenapi-flags.csv.

Para obtener más información sobre los cambios en la lista de API que no pertenecen al SDK en Android 10, incluidas las alternativas a la API pública sugeridas para las API que se bloquean de forma condicional en Android 10, consulta Cambios de lista para Android 10.

Android 9

En el caso de Android 9 (nivel de API 28), el siguiente archivo de texto contiene la lista de API que no pertenecen al SDK y que no están restringidas (incluidas en la lista gris): hiddenapi-light-greylist.txt.

La lista de elementos bloqueados (blacklist) y la lista de API bloqueadas de forma condicional (lista gris oscuro) se derivan cuando se procesa la compilación.

Cómo generar listas de AOSP

Cuando trabajas con AOSP, puedes generar un archivo hiddenapi-flags.csv que contenga todas las interfaces que no pertenecen al SDK y sus listas correspondientes. Para ello, descarga la fuente de AOSP y, luego, ejecuta el siguiente comando:

m out/soong/hiddenapi/hiddenapi-flags.csv

Podrás encontrar el archivo en la siguiente ubicación:

out/soong/hiddenapi/hiddenapi-flags.csv

Comportamiento esperado cuando tu app accede a interfaces que no pertenecen al SDK y están restringidas

En la siguiente tabla, se describe el comportamiento que puedes esperar si tu app intenta acceder a una interfaz que no pertenece al SDK y forma parte de la lista de elementos bloqueados.

Método de acceso Resultado
Instrucción de Dalvik que hace referencia a un campo Se arroja NoSuchFieldError
Instrucción de Dalvik que hace referencia a un método Se arroja NoSuchMethodError
Reflexión a través de Class.getDeclaredField() o Class.getField() Se arroja NoSuchFieldException
Reflexión a través de Class.getDeclaredMethod(), Class.getMethod() Se arroja NoSuchMethodException
Reflexión a través de Class.getDeclaredFields(), Class.getFields() Los miembros que no pertenecen al SDK no aparecen en los resultados
Reflexión a través de Class.getDeclaredMethods(), Class.getMethods() Los miembros que no pertenecen al SDK no aparecen en los resultados
JNI a través de env->GetFieldID() Se muestra NULL, se arroja NoSuchFieldError
JNI a través de env->GetMethodID() Se muestra NULL, se arroja NoSuchMethodError

Prueba la app para interfaces que no pertenecen al SDK

Existen varios métodos que puedes usar para probar las interfaces de tu app que no pertenecen al SDK.

Realiza pruebas con una app depurable

Puedes realizar pruebas para las interfaces que no pertenecen al SDK. Para ello, debes compilar y ejecutar una app depurable en un dispositivo o emulador que use Android 9 (nivel de API 28) o versiones posteriores. Asegúrate de que coincidan el nivel del dispositivo o emulador y el nivel de la API objetivo al que se orienta la app.

El sistema imprime un mensaje de registro si tu app accede a ciertas interfaces que no pertenecen al SDK mientras se está realizando la prueba. Puedes analizar los mensajes de registro de tu app en busca de los siguientes detalles:

  • La clase, el nombre y el tipo de declaración (en el formato que se usa en el tiempo de ejecución de Android)
  • El método de acceso, que puede ser mediante vinculación, reflejo o JNI
  • La lista de la cual forma parte la interfaz que no pertenece al SDK

Puedes usar adb logcat para acceder a estos mensajes de registro, que aparecen debajo del PID de la app que está en ejecución. Por ejemplo, en el registro puede haber una entrada que diga lo siguiente:

Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI)

Realiza pruebas con la API de StrictMode

Otra forma de probar las interfaces que no pertenecen al SDK es mediante la API de StrictMode. Usa el método detectNonSdkApiUsage para habilitar esa opción. Una vez que hayas habilitado la API de StrictMode, podrás recibir una devolución de llamada para cada uso de una interfaz que no pertenece al SDK. Para ello, debes emplear un penaltyListener, donde puedes implementar el control personalizado. El objeto Violation que se proporciona en la devolución de llamada proviene de Throwable, y el seguimiento de pila delimitado brinda el contexto de uso.

Realiza pruebas con la herramienta veridex

También puedes ejecutar la herramienta de análisis estático veridex en el APK. Esta utilidad analiza toda la base de código del APK, incluidas las bibliotecas de terceros y, además, informa los usos de las interfaces que no pertenecen al SDK que encuentra.

Estas son algunas de las limitaciones de la herramienta veridex:

  • No detecta invocaciones mediante JNI.
  • Puede detectar únicamente un subconjunto de invocaciones mediante reflejo.
  • El análisis de rutas de acceso de códigos que se encuentren inactivas se limita a las verificaciones del nivel de la API.
  • Solo se puede ejecutar en máquinas compatibles con instrucciones SSE4.2 y POPCNT.

Windows

No se proporcionan objetos binarios nativos de Windows, pero puedes ejecutar la herramienta veridex en Windows mediante la ejecución de los objetos binarios de Linux con el Subsistema de Windows para Linux (WSL). Antes de seguir los pasos de esta sección, instala el WSL y elige Ubuntu como la distribución de Linux.

Después de instalar Ubuntu, inicia una terminal de Ubuntu y sigue estos pasos:

  1. Descarga la herramienta veridex del repositorio de compilaciones previas de tiempo de ejecución de Android.
  2. Extrae el contenido del archivo appcompat.tar.gz.
  3. En la carpeta extraída, busca el archivo veridex-linux.zip y descomprímelo.
  4. Navega a la carpeta descomprimida y ejecuta el siguiente comando, donde your-app.apk es el APK que quieres probar:

    ./appcompat.sh --dex-file=your-app.apk
    

macOS

Para ejecutar la herramienta veridex en macOS, sigue estos pasos:

  1. Descarga la herramienta veridex del repositorio de compilaciones previas de tiempo de ejecución de Android.
  2. Extrae el contenido del archivo appcompat.tar.gz.
  3. En la carpeta extraída, busca el archivo veridex-mac.zip y descomprímelo.
  4. Navega a la carpeta descomprimida y ejecuta el siguiente comando, donde /path-from-root/your-app.apk es la ruta de acceso al APK que quieres probar, comenzando por el directorio raíz de tu sistema:

    ./appcompat.sh --dex-file=/path-from-root/your-app.apk
    

Linux

Para ejecutar la herramienta veridex en Linux, sigue estos pasos:

  1. Descarga la herramienta veridex del repositorio de compilaciones previas de tiempo de ejecución de Android.
  2. Extrae el contenido del archivo appcompat.tar.gz.
  3. En la carpeta extraída, busca el archivo veridex-linux.zip y descomprímelo.
  4. Navega a la carpeta descomprimida y ejecuta el siguiente comando, donde your-app.apk es el APK que quieres probar:

    ./appcompat.sh --dex-file=your-app.apk
    

Realiza pruebas con la herramienta lint de Android Studio

Cada vez que compilas una app en Android Studio, la herramienta lint inspecciona el código para detectar posibles problemas. Si la app usa interfaces que no pertenecen al SDK, es posible que veas advertencias o errores de compilación, según la lista en la que estén incluidas esas interfaces.

También puedes ejecutar la herramienta lint desde la línea de comandos o ejecutar inspecciones de forma manual en un proyecto, carpeta o archivo específicos.

Realiza pruebas con Play Console

Cuando subes la app en un segmento de pruebas en Play Console, se realizan pruebas automáticamente para detectar posibles problemas y se genera un informe previo al lanzamiento. Si la app usa interfaces que no pertenecen al SDK, se muestra un error o una advertencia en el informe previo al lanzamiento, en función de la lista en la que estén incluidas esas interfaces.

Para obtener más información, consulta la sección "Compatibilidad con Android" en Cómo usar los informes previos al lanzamiento para identificar problemas.

Solicita una nueva API pública

Si no puedes encontrar una alternativa a reemplazar el uso de una interfaz que no pertenece al SDK para una función de tu app, puedes crear una solicitud de función en nuestra Herramienta de seguimiento de errores a fin de que se agregue una nueva API pública al SDK.

Cuando creas una solicitud de función, debes proporcionar la siguiente información:

  • Las API que forman parte de la lista gris que usas en este momento, incluido el descriptor completo que aparece en el mensaje de logcat Accessing hidden ...
  • El motivo por el cual necesitas usar estas API y detalles sobre la función de nivel superior para la cual necesitas la API, no solo los detalles de bajo nivel
  • El motivo por el cual las API públicas relacionadas del SDK no satisfacen tu propósito
  • Otras alternativas que hayas intentado y los motivos por los cuales no dieron buenos resultados

Si incluyes esta información en la solicitud de función, aumentarás las probabilidades de que se otorgue una nueva API pública.

Otras preguntas

En esta sección, se incluyen respuestas a otras preguntas frecuentes de los desarrolladores:

Preguntas generales

¿De qué manera puede asegurarse Google de que captará las necesidades de todas las apps a través del seguimiento de problemas?

Creamos listas iniciales para Android 9 (nivel de API 28) a través del análisis estático de las apps, el cual se complementó con los siguientes métodos:

  • prueba manual de apps de Google Play y ajenas a esta plataforma
  • informes internos
  • recopilación automática de datos de los usuarios internos
  • informes de vista previa para desarrolladores
  • análisis estático adicional que se diseñó para incluir más falsos positivos de manera conservadora

Cuando evaluamos las listas para cada actualización nueva, tenemos en cuenta el uso de la API, así como los comentarios de los desarrolladores a través del seguimiento de problemas.

¿Cómo puedo habilitar el acceso a las interfaces que no pertenecen al SDK?

Puedes habilitar el acceso a estas interfaces en los dispositivos de desarrollo mediante el uso de comandos adb para cambiar la política de aplicación de la API. Los comandos que uses variarán según el nivel de API. Estos comandos no requieren un dispositivo con derechos de administrador.

Android 10 (API nivel 29)

Para habilitar el acceso, usa el siguiente comando adb:

adb shell settings put global hidden_api_policy  1

Para restablecer la política de aplicación de la API a la configuración original, usa el siguiente comando:

adb shell settings delete global hidden_api_policy
Android 9 (nivel de API 28)

Para habilitar el acceso, usa los siguientes comandos adb:

adb shell settings put global hidden_api_policy_pre_p_apps  1
adb shell settings put global hidden_api_policy_p_apps 1

Para restablecer la configuración predeterminada de la política de aplicación de la API, usa los siguientes comandos:

adb shell settings delete global hidden_api_policy_pre_p_apps
adb shell settings delete global hidden_api_policy_p_apps

Puedes elegir uno de los siguientes valores de número entero para la política de aplicación de la API:

  • 0: Inhabilita toda la detección de interfaces ajenas al SDK. Si usas esta configuración, se inhabilitarán todos los mensajes de registro para el uso de interfaces que no pertenecen al SDK y no podrás probar tu app con la API de StrictMode. Esta configuración no es recomendable.
  • 1: Habilita el acceso a todas las interfaces ajenas al SDK, pero imprime mensajes de registro con advertencias para el uso de esas interfaces. El uso de este valor de configuración también te permite probar tu app con la API de StrictMode.
  • 2: Inhabilita el uso de interfaces que no pertenecen al SDK, forman parte de la lista de elementos bloqueados o están bloqueadas de forma condicional para el nivel de API al que se orienta la app.

Preguntas sobre las listas de interfaces que no pertenecen al SDK

¿Dónde puedo encontrar las listas de las API que no pertenecen al SDK en la imagen del sistema?

Están codificadas en el campo y en los bits de marcado de acceso al método en los archivos dex de la plataforma. No hay ningún archivo separado en la imagen del sistema que contenga estas listas.

¿Las listas de las API que no pertenecen al SDK son iguales en dispositivos de diferentes OEM que tienen la misma versión de Android?

Los OEM pueden agregar sus propias interfaces a la lista de elementos bloqueados (lista negra), pero no pueden quitar interfaces de las listas de API que no pertenecen al SDK de AOSP. El CDD evita esos cambios y las pruebas del CTS garantizan que el tiempo de ejecución de Android aplique la lista.

¿Existen restricciones para las interfaces que no pertenecen al SDK en el código nativo?

El SDK de Android incluye interfaces de Java. La plataforma comenzó a restringir el acceso a las interfaces que no pertenecen al SDK para el código nativo C/C++ en Android 7 (nivel de API 26). Si quieres obtener más información, consulta el artículo sobre cómo mejorar la estabilidad con restricciones de símbolos C/C++ privados en Android N.

¿Existe algún plan para restringir la manipulación de archivos dex2oat o DEX?

No contamos con planes activos para restringir el acceso al objeto binario dex2oat, pero no es nuestra intención que el formato de archivo DEX sea estable o una interfaz pública más allá de las partes especificadas públicamente en el formato ejecutable Dalvik. Nos reservamos el derecho a modificar o eliminar dex2oat y las partes no especificadas del formato DEX en cualquier momento. Además, ten en cuenta que los archivos derivados producidos por dex2oat, como ODEX (también conocido como OAT), VDEX y CDEX son formatos no especificados.

¿Qué ocurre si un SDK importante de terceros (por ejemplo, un ofuscador) no puede evitar el uso de interfaces que no pertenecen al SDK, pero se compromete a mantener la compatibilidad con versiones futuras de Android? ¿Android puede hacer una excepción en cuanto a los requisitos de compatibilidad en esos casos?

No está en nuestros planes hacer excepciones relacionadas con los requisitos de compatibilidad por SDK. Si la compatibilidad de la app de un desarrollador de SDK depende únicamente de las interfaces que forman parte de las listas grises, este deberá pensar en migrar a interfaces de SDK o alguna otra opción y solicitar una nueva API pública en caso de que no encuentre una alternativa al uso de este tipo de interfaz.

¿Las restricciones de las interfaces que no pertenecen al SDK se aplican a todas las apps (incluidas las de sistema y origen) y no solo a las apps de terceros?

Sí. Sin embargo, excluimos las apps firmadas con la clave de la plataforma y algunas apps de imagen del sistema. Ten en cuenta que estas exenciones solo se aplican a las apps que forman parte de la imagen del sistema (o a las apps de la imagen del sistema actualizadas). La lista está destinada solo para apps que que se compilen con las API de plataformas privadas en lugar de las API de SDK (es decir, LOCAL_PRIVATE_PLATFORM_APIS := true).