En esta página, se describen los cambios que se agregaron a AOSP para reducir los cambios de archivos innecesarios entre compilaciones. Los implementadores de dispositivos que mantienen sus propios sistemas de compilación pueden usar esta información como guía para reducir el tamaño de sus actualizaciones inalámbricas (OTA).
En ocasiones, las actualizaciones OTA de Android contienen archivos modificados que no corresponden a cambios de código. En realidad, son artefactos del sistema de compilación. Esto puede ocurrir cuando el mismo código, compilado en diferentes momentos, desde diferentes directorios o en diferentes máquinas, produce una gran cantidad de archivos modificados. Estos archivos adicionales aumentan el tamaño de un parche OTA y dificultan la determinación de qué código cambió.
Para que el contenido de una actualización OTA sea más transparente, AOSP incluye cambios en el sistema de compilación diseñados para reducir el tamaño de los parches OTA. Se eliminaron los cambios de archivos innecesarios entre compilaciones, y solo los archivos relacionados con parches se incluyen en las actualizaciones OTA. AOSP también incluye una herramienta de comparación de compilaciones, que filtra los cambios comunes de archivos relacionados con la compilación para proporcionar una comparación de archivos de compilación más clara, y una herramienta de asignación de bloques, que te ayuda a mantener la asignación de bloques coherente.
Un sistema de compilación puede crear parches innecesariamente grandes de varias maneras. Para mitigar esto, en Android 8.0 y versiones posteriores, se implementaron nuevas funciones para reducir el tamaño del parche para cada diferencia de archivo. Entre las mejoras que redujeron el tamaño de los paquetes de actualización OTA, se incluyen las siguientes:
-
Uso de ZSTD, un algoritmo de compresión sin pérdidas de uso general para imágenes completas en actualizaciones de dispositivos que no son A/B. ZSTD se puede personalizar para obtener relaciones de compresión más altas aumentando el nivel de compresión. El nivel de compresión se establece durante el tiempo de generación de OTA y se puede configurar pasando la marca
--vabc_compression_param=zstd,$COMPRESSION_LEVEL
. -
Se aumentó el tamaño de la ventana de compresión que se usa durante la actualización OTA. Para establecer el tamaño máximo de la ventana de compresión, puedes personalizar el parámetro de compilación en el archivo
.mk
de un dispositivo. Esta variable se establece comoPRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 262144
. - Uso de la recompresión de Puffin, una herramienta de parcheo determinista para flujos de desinflado que controla las funciones de compresión y diferencia para la generación de actualizaciones OTA A/B.
-
Cambios en el uso de la herramienta de generación de deltas, como la forma en que se usa la biblioteca
bsdiff
para comprimir parches. En Android 9 y versiones posteriores, la herramientabsdiff
selecciona el algoritmo de compresión que brindaría los mejores resultados de compresión para un parche. -
Las mejoras en
update_engine
generaron menos consumo de memoria cuando se aplican parches para las actualizaciones de dispositivos A/B.
En las siguientes secciones, se analizan varios problemas que afectan los tamaños de las actualizaciones OTA, sus soluciones y ejemplos de implementación en AOSP.
Orden de archivos
Problema: Los sistemas de archivos no garantizan un orden de archivos cuando se solicita una lista de archivos en un directorio, aunque suele ser el mismo para la misma confirmación de la compra. Las herramientas como
ls
ordenan los resultados de forma predeterminada, pero la función de comodín que usan comandos como
find
y make
no los ordenan. Antes de usar estas herramientas, debes ordenar los resultados.
Solución: Cuando uses herramientas como find
y make
con la función de comodín, ordena el resultado de estos comandos antes de usarlos. Cuando uses $(wildcard)
o $(shell find)
en archivos Android.mk
, ordénalos también. Algunas herramientas, como Java, ordenan las entradas, por lo que, antes de ordenar los archivos, verifica que la herramienta que usas no lo haya hecho.
Ejemplos: Se corrigieron muchas instancias en el sistema de compilación principal con la macro all-*-files-under
integrada, que incluye all-cpp-files-under
(ya que varias definiciones se distribuyeron en otros archivos de configuración de make).
Para obtener más información, consulta los siguientes recursos:
- https://android.googlesource.com/platform/build/+/4d66adfd0e6d599d8502007e4ea9aaf82e95569f
- https://android.googlesource.com/platform/build/+/379f9f9cec4fe1c66b6d60a6c19fecb81b9eb410
- https://android.googlesource.com/platform/build/+/7c3e3f8314eec2c053012dd97d2ae649ebeb5653
- https://android.googlesource.com/platform/build/+/5c64b4e81c1331cab56d8a8c201f26bb263b630c
Directorio de compilación
Problema: Cambiar el directorio en el que se compilan los elementos puede hacer que los objetos binarios sean diferentes. La mayoría de las rutas de acceso en la compilación de Android son rutas de acceso relativas, por lo que __FILE__
en C/C++ no es un problema. Sin embargo, los símbolos de depuración codifican la ruta de acceso completa de forma predeterminada, y .note.gnu.build-id
se genera a partir del hash del binario despojado previamente, por lo que cambiará si cambian los símbolos de depuración.
Solución: AOSP ahora hace que las rutas de depuración sean relativas. Para obtener más información, consulta CL: https://android.googlesource.com/platform/build/+/6a66a887baadc9eb3d0d60e26f748b8453e27a02.
Marcas de tiempo
Problema: Las marcas de tiempo en el resultado de la compilación generan cambios innecesarios en los archivos. Es probable que esto suceda en las siguientes ubicaciones:
- Macros
__DATE__/__TIME__/__TIMESTAMP__
en código C o C++ - Marcas de tiempo incorporadas en archivos basados en ZIP
Soluciones o ejemplos: Para quitar las marcas de tiempo del resultado de la compilación, usa las instrucciones que se indican a continuación en __DATE__/__TIME__/__TIMESTAMP__ en C/C++. y Marcas de tiempo incorporadas en archivos.
__DATE__/__TIME__/__TIMESTAMP__ en C/C++
Estas macros siempre producen resultados diferentes para compilaciones diferentes, por lo que no debes usarlas. Estas son algunas opciones para eliminar estas macros:
- Quítalas. Para ver un ejemplo, consulta https://android.googlesource.com/platform/system/core/+/30622bbb209db187f6851e4cf0cdaa147c2fca9f.
- Para identificar de forma única el objeto binario en ejecución, lee el ID de compilación del encabezado ELF.
-
Para saber cuándo se compiló el SO, lee
ro.build.date
(esto funciona para todo, excepto para las compilaciones incrementales, que pueden no actualizar esta fecha). Para ver un ejemplo, consulta https://android.googlesource.com/platform/external/libchrome/+/8b7977eccc94f6b3a3896cd13b4aeacbfa1e0f84.
Marcas de tiempo incorporadas en archivos (zip, jar)
Android 7.0 solucionó el problema de las marcas de tiempo incorporadas en los archivos ZIP agregando -X
a todos los usos del comando zip
. Esto quitó el UID/GID del compilador y la marca de tiempo de Unix extendida del archivo ZIP.
Una nueva herramienta, ziptime
(ubicada en /platform/build/+/android16-release/tools/ziptime/
), restablece las marcas de tiempo normales en los encabezados de ZIP. Para obtener más información, consulta el
archivo README.
La herramienta signapk
establece marcas de tiempo para los archivos APK que pueden variar según la zona horaria del servidor. Para obtener más información, consulta el CL
https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028.
La herramienta signapk
establece marcas de tiempo para los archivos APK que pueden variar según la zona horaria del servidor. Para obtener más información, consulta el CL https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028.
Cadenas de versión
Problema: Las cadenas de versión del APK a menudo tenían BUILD_NUMBER
agregado a sus versiones codificadas. Incluso si no se cambiara nada más en un APK, el APK seguiría siendo diferente.
Solución: Quita el número de compilación de la cadena de versión del APK.
Ejemplos:
- https://android.googlesource.com/platform/packages/apps/Camera2/+/5e0f4cf699a4c7c95e2c38ae3babe6f20c258d27
- https://android.googlesource.com/platform/build/+/d75d893da8f97a5c7781142aaa7a16cf1dbb669c
Habilita el procesamiento de verificación integrado en el dispositivo
Si dm-verity está habilitado en tu dispositivo, las herramientas OTA detectan automáticamente tu configuración de Verity y habilitan el procesamiento de Verity en el dispositivo. Esto permite que los bloques de Verity se calculen en dispositivos Android, en lugar de almacenarse como bytes sin procesar en tu paquete OTA. Los bloques de Verity pueden usar aproximadamente 16 MB para una partición de 2 GB.
Sin embargo, el procesamiento de la verificación en el dispositivo puede llevar mucho tiempo. Específicamente, el código de corrección de errores de reenvío puede tardar mucho tiempo. En dispositivos Pixel, suele tardar hasta 10
minutos. En dispositivos de baja gama, podría tardar más. Si quieres inhabilitar el procesamiento de Verity integrado en el dispositivo, pero aún así habilitar dm-verity, puedes pasar --disable_fec_computation
a la herramienta ota_from_target_files
cuando generes una actualización OTA. Esta marca inhabilita el procesamiento de verificación en el dispositivo durante las actualizaciones OTA.
Disminuye el tiempo de instalación inalámbrica, pero aumenta el tamaño del paquete inalámbrico. Si tu dispositivo no tiene habilitado dm-verity, pasar esta marca no tiene efecto.
Herramientas de compilación coherentes
Problema: Las herramientas que generan archivos instalados deben ser coherentes (una entrada determinada siempre debe producir el mismo resultado).
Soluciones o ejemplos: Se requirieron cambios en las siguientes herramientas de compilación:
- Creador del archivo de AVISO. Se cambió el creador de archivos AVISO para crear colecciones de AVISO reproducibles. Consulta CL: https://android.googlesource.com/platform/build/+/8ae4984c2c8009e7a08e2a76b1762c2837ad4f64.
- Java Android Compiler Kit (Jack). La cadena de herramientas de Jack requirió una actualización para controlar los cambios ocasionales en el orden del constructor generado. Se agregaron a la cadena de herramientas los accesores deterministas para los constructores: https://android.googlesource.com/toolchain/jack/+/056a5425b3ef57935206c19ecb198a89221ca64b.
- Compilador de AOT de ART (dex2oat). El objeto binario del compilador de ART recibió una actualización que agregó una opción para crear una imagen determinista: https://android.googlesource.com/platform/art/+/ace0dc1dd5480ad458e622085e51583653853fb9.
-
El archivo libpac.so (V8) Cada compilación crea un archivo
/system/lib/libpac.so
diferente porque la instantánea de V8 cambia para cada compilación. La solución fue quitar la instantánea: https://android.googlesource.com/platform/external/v8/+/e537f38c36600fd0f3026adba6b3f4cbcee1fb29. - Archivos de pre-dexopt (.odex) de la aplicación Los archivos pre-dexopt (.odex) contenían relleno no inicializado en sistemas de 64 bits. Se corrigió lo siguiente: https://android.googlesource.com/platform/art/+/34ed3afc41820c72a3c0ab9770be66b6668aa029.
Usa la herramienta de diferencias de compilación
En los casos en que no es posible eliminar los cambios de archivos relacionados con la compilación, AOSP incluye una herramienta de comparación de compilaciones, target_files_diff.py
, para comparar dos paquetes de archivos. Esta herramienta realiza una diferencia recursiva entre dos compilaciones, sin incluir los cambios comunes de archivos relacionados con la compilación, como los siguientes:
- Cambios esperados en el resultado de la compilación (por ejemplo, debido a un cambio en el número de compilación)
- Cambios debido a problemas conocidos en el sistema de compilación actual.
Para usar la herramienta de diferencias de compilación, ejecuta el siguiente comando:
target_files_diff.py dir1 dir2
dir1
y dir2
son directorios base que contienen los archivos de destino extraídos para cada compilación.
Mantén la coherencia de la asignación de bloques
En un archivo determinado, aunque su contenido permanezca igual entre dos compilaciones, es posible que los bloques reales que contienen los datos hayan cambiado. Como resultado, el actualizador debe realizar E/S innecesarias para mover los bloques para una actualización OTA.
En una actualización OTA A/B virtual, la E/S innecesaria puede aumentar en gran medida el espacio de almacenamiento necesario para almacenar la instantánea de copia en escritura. En una actualización OTA que no es A/B, mover los bloques para una actualización OTA contribuye al tiempo de actualización, ya que hay más E/S debido a los movimientos de bloques.
Para solucionar este problema, en Android 7.0, Google amplió la herramienta make_ext4fs
para mantener la asignación de bloques coherente en todas las compilaciones. La herramienta make_ext4fs
acepta una marca -d base_fs
opcional que intenta asignar archivos a los mismos bloques cuando se genera una imagen ext4
. Puedes extraer los archivos de asignación de bloques (como los archivos de mapa base_fs
) del archivo ZIP de los archivos de destino de una compilación anterior. Para cada partición ext4
, hay un archivo .map
en el directorio IMAGES
(por ejemplo, IMAGES/system.map
corresponde a la partición system
). Luego, estos archivos base_fs
se pueden registrar y especificar a través de PRODUCT_<partition>_BASE_FS_PATH
, como en este ejemplo:
PRODUCT_SYSTEM_BASE_FS_PATH := path/to/base_fs_files/base_system.map PRODUCT_SYSTEM_EXT_BASE_FS_PATH := path/to/base_fs_files/base_system_ext.map PRODUCT_VENDOR_BASE_FS_PATH := path/to/base_fs_files/base_vendor.map PRODUCT_PRODUCT_BASE_FS_PATH := path/to/base_fs_files/base_product.map PRODUCT_ODM_BASE_FS_PATH := path/to/base_fs_files/base_odm.map
Si bien esto no ayuda a reducir el tamaño general del paquete OTA, mejora el rendimiento de la actualización OTA, ya que reduce la cantidad de E/S. En el caso de las actualizaciones A/B virtuales, reduce de forma drástica la cantidad de espacio de almacenamiento necesario para aplicar la actualización inalámbrica.
Evita actualizar las apps
Además de minimizar las diferencias de compilación, puedes reducir los tamaños de las actualizaciones OTA si excluyes las actualizaciones para las apps que reciben actualizaciones a través de tiendas de aplicaciones. Los APKs suelen representar una parte significativa de varias particiones en un dispositivo. Incluir las versiones más recientes de las apps que se actualizan en las tiendas de aplicaciones en una actualización OTA puede tener un gran impacto en el tamaño de los paquetes OTA y proporcionar pocos beneficios para los usuarios. Cuando los usuarios reciben un paquete OTA, es posible que ya tengan la app actualizada o una versión aún más reciente, que recibieron directamente de las tiendas de aplicaciones.