Restricciones en interfaces que no pertenecen al SDK

A partir de Android 9 (nivel de API 28), la plataforma restringe las interfaces que no pertenecen 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 Interfaces que no pertenecen al SDK y que no puedes usar sin importar el nivel de la API al que está orientada tu app. Si tu app intenta acceder a una de estas interfaces, se generará un error en el sistema.
Lista gris Interfaces que no pertenecen al SDK y que puedes usar siempre y cuando no estén restringidas para el nivel de la API al que está orientada tu app.

A partir de Android 9 (nivel de API 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 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 tu 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 SDK, o bien otras alternativas. Si no encuentras una alternativa para reemplazar el uso de una interfaz que no pertenece al SDK para una función de tu app, debes solicitar una nueva API pública.

Cómo determinar a qué lista pertenece una interfaz

Las listas de interfaces que no pertenecen al SDK se compilan como parte de la plataforma.

En el caso de Android 9 (nivel de API 28), este archivo de texto contiene las API incluidas en la lista gris que no están restringidas. La lista negra y la lista gris restringida se derivan cuando se procesa la compilación. Existe una regla de compilación que genera las listas en AOSP. Si bien las listas para AOSP y Android 9 no son similares, el porcentaje de superposición es bastante alto. También puedes generar la lista negra por tu cuenta. Para ello, debes descargar AOSP y ejecutar el siguiente comando:

make hiddenapi-aosp-blacklist
    

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

out/target/common/obj/PACKAGING/hiddenapi-aosp-blacklist.txt
    

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 genera el error NoSuchFieldError.
Instrucción de Dalvik que hace referencia a un método Se genera el error NoSuchMethodError.
Reflejo mediante Class.getDeclaredField() o Class.getField() Se genera la excepción NoSuchFieldException.
Reflejo mediante Class.getDeclaredMethod(), Class.getMethod() Se genera la excepción NoSuchMethodException.
Reflejo mediante Class.getDeclaredFields(), Class.getFields() Los miembros que no pertenecen al SDK no aparecen en los resultados.
Reflejo mediante Class.getDeclaredMethods(), Class.getMethods() Los miembros que no pertenecen al SDK no aparecen en los resultados.
JNI mediante env->GetFieldID() Se muestra NULL y se genera el error NoSuchFieldError.
JNI mediante env->GetMethodID() Se muestra NULL y se genera el error NoSuchMethodError.

Cómo probar tu 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.

Cómo realizar 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 al que está orientada tu 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)
    

Cómo realizar 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 habilitarla. 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.

Cómo realizar 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.

Cómo solicitar una nueva API pública

Si no puedes encontrar una alternativa para 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 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 alto nivel 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 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. Para ello, debes cambiar la política de aplicación de la API con 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
    

Estos comandos no requieren un dispositivo con derechos de administrador.

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 que no pertenecen 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 que no pertenecen al SDK, pero imprime mensajes de registro con advertencias sobre el uso esas interfaces. Esta 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 está orientada la app.
  • 3: Inhabilita el uso de interfaces que no pertenecen al SDK y forman parte de la lista negra, pero habilita el uso de interfaces que forman parte de la lista gris y están restringidas para el nivel de la API al que está orientada 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 eliminar interfaces de las listas negras o 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 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 únicamente a las apps que se compilaron según las API de la plataforma privada en lugar de las API del SDK (en las que LOCAL_PRIVATE_PLATFORM_APIS := true).