Únete a ⁠ #Android11: The Beta Launch Show el 3 de junio.

Restricciones en interfaces que no pertenecen al SDK

A partir de Android 9 (API nivel 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 sobre 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 negras, grises y blancas

Con cada versión de Android, se restringirán 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 negra 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.
Lista gris Incluye interfaces que no pertenecen al SDK y que puedes usar siempre y cuando no estén restringidas para el nivel de API objetivo de tu app.

A partir de Android 9 (API nivel 28), cada nivel de la API tiene interfaces que no pertenecen al SDK y están restringidas en ese nivel. Tu app puede acceder a la API de una lista gris restringida cuando está orientada a un nivel anterior de la API. Sin embargo, si intenta acceder a una interfaz que no pertenece al SDK y está restringida para el nivel de la API al que está orientada la app, el sistema se comportará como si la API estuviera incluida en la lista negra.

Nota: En Android 9 (nivel de API 28), las interfaces que no pertenecen al SDK de la lista gris no restringida se denominaban lista gris claro y las interfaces que no pertenecen al SDK de la lista gris restringida se denominaban lista gris oscuro.

Lista blanca 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 y forman parte de la lista gris (según el nivel de la API al que está orientada 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 una alternativa 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 10

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

Android 9

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

La lista negra y la lista gris restringida 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 negra.

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 (API nivel 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 este sistema operativo 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 errores de compilación o advertencias, en función de si esas interfaces son parte de la lista negra o listas grises.

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 si esas interfaces son parte de la lista negra o listas grises.

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 (API nivel 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 apps que no pertenecen a Google Play
  • 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 (API nivel 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 negra o gris y están restringidas 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 la lista negra y las listas grises 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.

¿La lista negra y las listas grises 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 negra, pero no pueden quitar interfaces de las listas negras ni grises 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 tenemos una lista blanca de paquetes para algunas apps de la 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 se compilen con las API de plataformas privadas en lugar de las API de SDK (es decir, LOCAL_PRIVATE_PLATFORM_APIS := true).