Retrocompatibilidad para el entorno de ejecución de SDK

En este documento, se propone una nueva biblioteca de Jetpack para ayudar a los desarrolladores con la migración al entorno de ejecución de SDK. Además, se explica el modo en que el entorno de ejecución de SDK será compatible con versiones anteriores de la plataforma de Android (desde la compilación hasta la ejecución) y qué diferencias o limitaciones del entorno de ejecución pueden esperar los desarrolladores. Esta biblioteca permite a los desarrolladores crear una versión única de su app o SDK que incluya la capacidad de ejecutarse en dispositivos con o sin compatibilidad con el entorno de ejecución de SDK.

La retrocompatibilidad se logra a través de los siguientes componentes:

  • El complemento de Android para Gradle (AGP) junto con Bundletool compilan una variante de app para dispositivos que no son compatibles con el entorno de ejecución de SDK empaquetando ese entorno en el APK.

  • La biblioteca cliente del entorno de ejecución de SDK (androidx.privacysandbox.sdkruntime:sdkruntime-client) carga el SDK en forma de paquete desde los recursos de aplicación y emula el entorno de ejecución de SDK en dispositivos que no son compatibles con ese entorno.

  • La biblioteca del proveedor del entorno de ejecución de SDK (androidx.privacysandbox.sdkruntime:sdkruntime-provider) proporciona una API para que los SDKs permitan la carga desde la biblioteca cliente del entorno de ejecución de SDK.

Publicación del SDK con Bundletool

En los dispositivos compatibles con el entorno de ejecución de SDK, los SDKs se entregarán y se instalarán como paquetes separados.

Para admitir versiones de plataforma no compatibles con el entorno de ejecución de SDK, Bundletool compilará una o más variantes del conjunto de APK de la app que incluyan todos los SDKs de los que depende la app. Cada SDK se empaqueta como una división del APK independiente. Además, se realizan las siguientes transformaciones:

  1. Se copian los archivos del código de bytes del SDK (DEX) en el SDK divididos como recursos.
  2. Se copian los recursos de Java del SDK en el SDK divididos como recursos.
  3. Se reasignan los recursos del SDK y se combinan con los recursos de la app.
  4. Se generan archivos de configuración para la biblioteca cliente del entorno de ejecución de SDK.

Carga los SDKs con la biblioteca cliente del entorno de ejecución de SDK

La biblioteca cliente del entorno de ejecución de SDK proporciona APIs que son similares a las de la plataforma, pero admiten tanto los SDKs en el entorno del entorno de ejecución de SDK como los incluidos en la app de la variante.

Para usar la biblioteca cliente del entorno de ejecución de SDK, agrega la dependencia androidx.privacysandbox.sdkruntime:sdkruntime-client y usa SdkSandboxManagerCompat en lugar de SdkSandboxManager.

Cuando una app intenta cargar un SDK, la biblioteca primero comprueba si el SDK se empaqueta con la app durante la compilación. Si lo hace, la biblioteca extrae el SDK de la división y lo carga en el proceso de la app. Si el SDK no se empaqueta con la app, la biblioteca delega la API de la plataforma para cargar el SDK.

Extrae un SDK de los recursos

Cuando una app intenta cargar un SDK empaquetado, la biblioteca cliente del entorno de ejecución de SDK verifica si los archivos DEX del SDK ya se extrajeron al almacenamiento del dispositivo (code_cache) y, si no, los extrae de los recursos.

En general, la biblioteca extraerá archivos solo una vez después de instalar o actualizar una app.

Si el espacio de almacenamiento disponible es inferior al umbral permitido (actualmente, 100 MB) y no se extraen archivos DEX, la biblioteca intentará cargar el SDK directamente desde los recursos en dispositivos compatibles (nivel de API 27 o superior). De esta manera, se obtiene un mayor espacio en memoria.

Cargador de clases para clases de SDKs

Para evitar conflictos entre los SDKs y las clases de la app, todas las clases de SDKs se cargan con un cargador de clases independiente, lo que significa que se cargan de forma separada del cargador de clases principal de la app.

En el diseño actual del entorno de ejecución de SDK, todas las comunicaciones entre una app y los SDKs se realizan con las llamadas IPC de Binder. Los mismos objetos de Binder del SDK se usan para los SDKs agrupados, y la serialización de transacciones de Binder permite a los desarrolladores de apps transmitir objetos de Binder de SDK a interfaces de Binder de SDK en el lado de la app.

Para otras interacciones internas (como inicializar un SDK, proporcionar una API de controlador a un SDK, etc.), la biblioteca usa Proxies dinámicos y la reflexión para funcionar en diferentes cargadores de clases.

Entorno del SDK

La biblioteca del proveedor de SDKRuntime proporciona APIs a los desarrolladores de SDK. Estas APIs son similares a las de la plataforma, pero permiten que el entorno de ejecución de SDK y la biblioteca cliente de SDKRuntime carguen los SDKs.

Para poder usar el SDK de la biblioteca, debes agregar la dependencia androidx.privacysandbox.sdkruntime:sdkruntime-provider y extender SandboxedSdkProviderCompat en lugar de SandboxedSdkProvider.

También debes usar SandboxedSdkProviderAdapter como el proveedor del SDK para permitir que el proveedor de compatibilidad se cargue en el entorno de ejecución del SDK.

SdkSandboxControllerCompat delega a la API de la plataforma cuando el SDK se carga en su entorno de ejecución o delega a la biblioteca cliente de SDKRuntime cuando este se carga como un SDK empaquetado.

En el caso de los SDKs empaquetados, la biblioteca modifica el entorno de formas que emulan un comportamiento similar al entorno del entorno de ejecución de SDK.

En las siguientes secciones, se describen los comportamientos esperados cuando la biblioteca cliente de SDKRuntime carga el SDK.

Recursos del SDK

Los recursos del SDK (res/) son compatibles cuando el SDK se carga en el proceso de la app. Bundletool combina todos los recursos de los SDKs con los recursos de la app.

Para evitar conflictos, se reasignan los recursos del SDK cambiando el prefijo packageId en todos los IDs de recursos.

Cuando la biblioteca cliente SDKRuntime carga el SDK, se actualiza packageId en el entorno de ejecución para permitir la asignación de recursos reasignados con la clase R.

Recursos de Java

Los recursos Java son compatibles cuando el SDK se carga en el proceso de la app. Bundletool copia todos los recursos del SDK de Java en un directorio especial, en los recursos de aplicación. La biblioteca cliente SDKRuntime usa un cargador de clases intermedio para redireccionar todas las llamadas relacionadas con los recursos de Java al nuevo directorio raíz.

Elementos del SDK

Los elementos del SDK se combinan con los recursos de aplicación sin reasignarlos.

Almacenamiento del SDK

Para admitir el almacenamiento de SDK, la biblioteca cliente del entorno de ejecución de SDK crea un directorio raíz dedicado para cada SDK empaquetado en el almacenamiento de la app y proporciona un contexto especial que usa este directorio como raíz de almacenamiento.

Este contexto se puede recuperar desde SandboxedSdkProviderCompat#getContext.

Métodos compatibles relacionados con el almacenamiento:

  • getDataDir
  • getCacheDir
  • getCodeCacheDir
  • getNoBackupFilesDir
  • getDir
  • getFilesDir
  • openFileInput
  • openFileOutput
  • deleteFile
  • getFileStreamPath
  • fileList
  • getDatabasePath
  • openOrCreateDatabase
  • moveDatabaseFrom: Solo entre contextos de SDK
  • deleteDatabase
  • databaseList
  • getSharedPreferences
  • moveSharedPreferencesFrom: Solo entre contextos de SDK
  • deleteSharedPreferences

Se puede crear un contexto de almacenamiento protegido por el dispositivo llamando a createDeviceProtectedStorageContext() en ese contexto.

SdkSandboxControllerCompat

La biblioteca cliente de SDKRuntime proporciona la implementación de SdkSandboxControllerCompat para los SDKs agrupados que se cargan en el proceso de la app.

Si la biblioteca cliente no admite las APIs (por ejemplo, con un SDK compilado con una versión de la biblioteca más reciente que la versión de la app), se usará el resguardo más adecuado (no-ops o excepción).

Control de versiones

Cuando la biblioteca cliente de SDKRuntime carga un SDK empaquetado, realiza un protocolo de enlace con la biblioteca del proveedor de SDKRuntime dentro del SDK. Durante el protocolo de enlace, las bibliotecas intercambian sus versiones y ajustan el comportamiento para sustituir las APIs no disponibles con el resguardo más adecuado (no-ops o excepción).

Se recomienda usar la versión más reciente de la biblioteca tanto para los desarrolladores de apps como para los de SDK. De lo contrario, es posible que la funcionalidad que requiera compatibilidad en ambas partes no esté disponible.

Cualquier versión de la biblioteca cliente de SDKRuntime puede cargar un SDK con cualquier versión de la biblioteca del proveedor de SDKRuntime y viceversa.

En el futuro, se cambiará a la versión mínima de la biblioteca cliente necesaria para cargar el SDK con una versión específica de la biblioteca del proveedor.

Esto minimizará la fragmentación y ayudará a garantizar que la mayoría de las APIs sean compatibles si el SDK en paquete se cargó correctamente.