En esta página, se describen los cambios que se agregaron al AOSP para reducir los cambios innecesarios en los archivos 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 en el 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 de OTA y dificultan la determinación de qué código cambió.
Para que el contenido de las OTA sea más transparente, el AOSP incluye cambios en el sistema de compilación diseñados para reducir el tamaño de los parches de OTA. Se eliminaron los cambios innecesarios en los archivos entre compilaciones, y solo los archivos relacionados con parches se incluyen en las actualizaciones OTA. El AOSP también incluye una herramienta de comparación de compilaciones, que filtra los cambios comunes en los archivos relacionados con la compilación para proporcionar una comparación más clara de los archivos de compilación, y una herramienta de asignación de bloques, que te ayuda a mantener la asignación de bloques de manera coherente.
Un sistema de compilación puede crear parches innecesariamente grandes de varias maneras. Para mitigar este problema, en Android 8.0 y versiones posteriores, se implementaron nuevas funciones para reducir el tamaño del parche de 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érdida de propósito genérico para imágenes completas en actualizaciones de dispositivos que no son A/B ZSTD se puede personalizar para obtener índices de compresión más altos si se aumenta el nivel de compresión. El nivel de compresión se establece durante el tiempo de generación de la OTA y se puede configurar pasando la marca
--vabc_compression_param=zstd,$COMPRESSION_LEVEL
. -
Aumentar el tamaño de la ventana de compresión que se usa durante la OTA El tamaño máximo de la ventana de compresión se puede establecer personalizando 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 aplicación de parches determinística para flujos de deflate, que controla las funciones de compresión y de diferenciación para la generación de actualizaciones OTA de pruebas 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 proporcionaría los mejores resultados de compresión para un parche. -
Las mejoras en
update_engine
dieron como resultado un menor 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 los 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. 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 ordena. 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 ya.
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 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 la compilación de Android son 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 el .note.gnu.build-id
se genera a partir del hash del archivo binario previo a la eliminación, 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 detalles, 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 en Marcas de tiempo incorporadas en archivos.
__DATE__/__TIME__/__TIMESTAMP__ en C/C++
Estas macros siempre producen resultados diferentes para diferentes compilaciones, por lo que no debes usarlas. Estas son algunas opciones para eliminar estas macros:
- Quítalos. Para ver un ejemplo, consulta https://android.googlesource.com/platform/system/core/+/30622bbb209db187f6851e4cf0cdaa147c2fca9f.
- Para identificar de forma única el archivo 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 es posible que no actualicen 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
. De esta manera, se quitaron el UID/GID del compilador y la marca de tiempo extendida de Unix del archivo ZIP.
Una nueva herramienta, ziptime
(ubicada en /platform/build/+/android16-release/tools/ziptime/
), restablece las marcas de tiempo normales en los encabezados 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 detalles, 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 el valor BUILD_NUMBER
agregado a sus versiones codificadas. Incluso si no se modificara nada más en un APK, este 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 cálculo de veracidad en el dispositivo
Si dm-verity está habilitado en tu dispositivo, las herramientas de OTA seleccionan automáticamente tu configuración de verity y habilitan el cálculo de verity en el dispositivo. Esto permite que los bloques de veracidad se calculen en dispositivos Android, en lugar de almacenarse como bytes sin procesar en tu paquete OTA. Los bloques de verificación pueden usar aproximadamente 16 MB para una partición de 2 GB.
Sin embargo, calcular la veracidad en el dispositivo puede llevar mucho tiempo. Específicamente, el código de corrección de errores hacia adelante puede tardar mucho tiempo. En los dispositivos Pixel, suele tardar hasta 10 minutos. En dispositivos de gama baja, podría tardar más. Si deseas inhabilitar el cálculo de verity integrado en el dispositivo, pero 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 cálculo de la integridad en el dispositivo durante las actualizaciones inalámbricas.
Disminuye el tiempo de instalación de la OTA, pero aumenta el tamaño del paquete de la OTA. Si tu dispositivo no tiene habilitado dm-verity, pasar esta marca no tendrá ningún efecto.
Herramientas de compilación coherentes
Problema: Las herramientas que generan archivos instalados deben ser coherentes (una entrada determinada siempre debe producir la misma salida).
Soluciones o ejemplos: Se requirieron cambios en las siguientes herramientas de compilación:
- Creador de archivos de NOTICE. Se cambió el creador del archivo NOTICE para crear colecciones de NOTICE reproducibles. Consulta el 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 de los constructores generados. Se agregaron accesores determinísticos para los constructores a la cadena de herramientas: https://android.googlesource.com/toolchain/jack/+/056a5425b3ef57935206c19ecb198a89221ca64b.
- Compilador AOT de ART (dex2oat). El compilador binario de ART recibió una actualización que agregó una opción para crear una imagen determinística: 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 .odex de optimización previa de la app Los archivos pre-dexopt (.odex) contenían padding sin inicializar en sistemas de 64 bits. Se corrigió en https://android.googlesource.com/platform/art/+/34ed3afc41820c72a3c0ab9770be66b6668aa029.
Cómo usar la herramienta de comparación de compilaciones
En los casos en los que no es posible eliminar los cambios en los archivos relacionados con la compilación, el AOSP incluye una
herramienta de comparación de compilaciones,
target_files_diff.py
para comparar dos paquetes de archivos. Esta herramienta realiza una comparación recursiva entre dos compilaciones, excluyendo los cambios comunes 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 comparación de compilaciones, 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 asignación de bloques de forma coherente
En el caso de un archivo determinado, aunque su contenido siga siendo el mismo entre dos compilaciones, es posible que hayan cambiado los bloques reales que contienen los datos. Como resultado, el actualizador debe realizar E/S innecesarias para mover los bloques en una actualización OTA.
En una actualización OTA de A/B virtual, las E/S innecesarias pueden aumentar considerablemente el espacio de almacenamiento necesario para guardar 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 extendió 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 de actualización inalámbrica, sí mejora el rendimiento de la actualización inalámbrica, ya que reduce la cantidad de E/S. En el caso de las actualizaciones A/B virtuales, se reduce drásticamente la cantidad de espacio de almacenamiento necesario para aplicar la OTA.
Evita actualizar apps
Además de minimizar las diferencias de compilación, puedes reducir el tamaño de las actualizaciones OTA si excluyes las actualizaciones de las apps que se actualizan a través de las tiendas de aplicaciones. Los APKs suelen comprender 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 el usuario. 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.