Esta página describe los cambios agregados a AOSP para reducir los cambios de archivo innecesarios entre compilaciones. Los implementadores de dispositivos que mantienen sus propios sistemas de compilación pueden utilizar esta información como guía para reducir el tamaño de sus actualizaciones inalámbricas (OTA).
Las actualizaciones de Android OTA contienen ocasionalmente archivos modificados que no corresponden a cambios de código. En realidad, son artefactos del sistema de construcción. Esto puede ocurrir cuando el mismo código, creado en diferentes momentos, desde diferentes directorios o en diferentes máquinas produce una gran cantidad de archivos modificados. Estos archivos en exceso aumentan el tamaño de un parche OTA y dificultan determinar qué código cambió.
Para hacer que el contenido de una 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 archivo innecesarios entre compilaciones, y las actualizaciones de OTA solo contienen archivos relacionados con parches. AOSP también incluye una herramienta de diferenciación de compilación , que filtra los cambios comunes de archivos relacionados con la compilación para proporcionar una diferencia de archivo de compilación más limpia, y una herramienta de mapeo de bloques , que le ayuda a mantener la asignación de bloques consistente.
Un sistema de compilación puede crear parches innecesariamente grandes de varias formas. 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. Las mejoras que redujeron el tamaño de los paquetes de actualización OTA incluyen las siguientes:
- Uso de Brotli , un algoritmo de compresión sin pérdidas de propósito genérico para imágenes completas en actualizaciones de dispositivos que no son A / B. Brotli se puede personalizar para optimizar la compresión. En actualizaciones más grandes compuestas por dos o más bloques en el sistema de archivos (por ejemplo,
system.img
), los fabricantes de dispositivos o socios pueden agregar sus propios algoritmos de compresión y pueden usar diferentes algoritmos de compresión en diferentes bloques de la misma actualización. - Uso de la recompresión Puffin , una herramienta de parcheo determinista para desinflar flujos, que maneja las funciones de compresión y diferenciación para la generación de actualizaciones A / B OTA.
- Cambios en el uso de la herramienta de generación delta, como la
bsdiff
que se usa la bibliotecabsdiff
para comprimir parches. En Android 9 ybsdiff
posteriores, la herramientabsdiff
selecciona el algoritmo de compresión que daría los mejores resultados de compresión para un parche. - Las mejoras en
update_engine
dieronupdate_engine
resultado un menor consumo de memoria cuando se aplican parches para las actualizaciones de dispositivos A / B. - Mejoras en la división de archivos zip grandes para actualizaciones OTA basadas en bloques. Un modo en
imgdiff
divide los archivos APK de gran tamaño, según los nombres de las entradas. Esto produce un parche más pequeño en comparación con dividir archivos linealmente y usar la herramientabsdiff
para comprimirlos.
Las siguientes secciones discuten varios problemas que afectan los tamaños de actualización de OTA, sus soluciones y ejemplos de implementación en AOSP.
Orden de archivo
Problema : los sistemas de archivos no garantizan un orden de archivos cuando se les solicita una lista de archivos en un directorio, aunque suele ser lo mismo para la misma comprobación. Herramientas como ls
ordenan los resultados de forma predeterminada, pero la función comodín que utilizan comandos como find
y make
no clasifica. Antes de utilizar estas herramientas, debe ordenar las salidas.
Solución : cuando utilice herramientas como find
y make
con la función comodín, ordene la salida de estos comandos antes de usarlos. Cuando use $(wildcard)
o $(shell find)
en archivos Android.mk
, Android.mk
también. Algunas herramientas, como Java, clasifican las entradas, por lo que antes de ordenar los archivos, verifique que la herramienta que está utilizando no lo haya hecho todavía.
Ejemplos: Se corrigieron muchas instancias en el sistema de compilación central utilizando la macro incorporada all-*-files-under
, que incluye all-cpp-files-under
(ya que varias definiciones se distribuyeron en otros all-cpp-files-under
MAKE). Para obtener más información, consulte lo siguiente:
- 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 las cosas puede hacer que los binarios sean diferentes. La mayoría de las rutas en la compilación de Android son rutas relativas, por lo que __FILE__
en C / C ++ no es un problema. Sin embargo, los símbolos de depuración codifican el nombre de ruta completo de forma predeterminada, y el .note.gnu.build-id
se genera a partir del hash del binario previamente eliminado, 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, consulte CL: https://android.googlesource.com/platform/build/+/6a66a887baadc9eb3d0d60e26f748b8453e27a02 .
Marcas de tiempo
Problema: las marcas de tiempo en la salida de la compilación dan como resultado cambios de archivo innecesarios. Es probable que esto suceda en las siguientes ubicaciones:
-
__DATE__/__TIME__/__TIMESTAMP__
macros en código C o C ++. - Marcas de tiempo incrustadas en archivos zip.
Soluciones / Ejemplos: Para eliminar las marcas de tiempo de la salida de la compilación, use las instrucciones que se dan a continuación en __DATE __ / __ TIME __ / __ TIMESTAMP__ en C / C ++. y marcas de tiempo integradas en archivos .
__FECHA __ / __ HORA __ / __ TIMESTAMP__ en C / C ++
Estas macros siempre producen diferentes salidas para diferentes compilaciones, así que no las use. Aquí hay algunas opciones para eliminar estas macros:
- Retirarlos. Para ver un ejemplo, consulte https://android.googlesource.com/platform/system/core/+/30622bbb209db187f6851e4cf0cdaa147c2fca9f .
- Para identificar de forma única el binario en ejecución, lea el ID de compilación del encabezado ELF.
- Para saber cuándo se construyó el sistema operativo, lea
ro.build.date
(esto funciona para todo, excepto las compilaciones incrementales, que pueden no actualizar esta fecha). Para ver un ejemplo, consulte https://android.googlesource.com/platform/external/libchrome/+/8b7977eccc94f6b3a3896cd13b4aeacbfa1e0f84 .
Marcas de tiempo integradas en archivos (zip, jar)
Android 7.0 solucionó el problema de las marcas de tiempo incrustadas en archivos zip agregando -X
a todos los usos del comando zip
. Esto eliminó el UID / GID del constructor y la marca de tiempo Unix extendida del archivo zip.
Una nueva herramienta, ziptime
(ubicada en /platform/build/+/master/tools/ziptime/
) restablece las marcas de tiempo normales en los encabezados zip. Para obtener más información, consulte 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, consulte CL https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028 .
Cadenas de versión
Problema: las cadenas de la versión APK a menudo tenían el BUILD_NUMBER
adjunto a sus versiones codificadas. Incluso si nada más cambia en un APK, como resultado, el APK seguirá siendo diferente.
Solución: elimine el número de compilación de la cadena de versión del APK.
Solución: elimine 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
Habilitar el cálculo de la verdad en el dispositivo
Si dm-verity está habilitado en su dispositivo, las herramientas OTA automáticamente recogen su configuración de verdad y habilitan el cálculo de verdad en el dispositivo. Esto permite que los bloques de verdad se calculen en dispositivos Android, en lugar de almacenarse como bytes sin procesar en su paquete OTA. Los bloques Verity pueden utilizar aproximadamente 16 MB para una partición de 2 GB.
Sin embargo, calcular la verdad en el dispositivo puede llevar mucho tiempo. Específicamente, el código de corrección de errores de reenvío puede tardar bastante. En los dispositivos de píxeles, suele tardar hasta 10 minutos. En dispositivos de gama baja, podría llevar más tiempo. Si desea deshabilitar el cálculo de verdad en el dispositivo, pero aún habilita dm-verity, puede hacerlo pasando --disable_fec_computation
a la herramienta ota_from_target_files
al generar una actualización OTA. Este indicador deshabilita el cálculo de la verdad en el dispositivo durante las actualizaciones de OTA. Disminuye el tiempo de instalación de OTA, pero aumenta el tamaño del paquete OTA. Si su dispositivo no tiene dm-verity habilitado, pasar esta marca no tiene ningún efecto.
Herramientas de construcción consistentes
Problema: las herramientas que generan archivos instalados deben ser coherentes (una entrada determinada siempre debe producir la misma salida).
Soluciones / Ejemplos: se requirieron cambios en las siguientes herramientas de compilación:
- AVISO creador de archivos . El creador del archivo NOTICE se cambió para crear colecciones NOTICE reproducibles. Consulte CL: https://android.googlesource.com/platform/build/+/8ae4984c2c8009e7a08e2a76b1762c2837ad4f64 .
- Kit de compilador de Android Java (Jack) . La cadena de herramientas de Jack requería una actualización para manejar cambios ocasionales en el orden del constructor generado. Se agregaron accesos deterministas para constructores a la cadena de herramientas: https://android.googlesource.com/toolchain/jack/+/056a5425b3ef57935206c19ecb198a89221ca64b .
- Compilador ART AOT (dex2oat) . El binario del compilador 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 V8 cambia para cada compilación. La solución fue eliminar la instantánea: https://android.googlesource.com/platform/external/v8/+/e537f38c36600fd0f3026adba6b3f4cbcee1fb29 . - Archivos de aplicación pre-dexopt'd (.odex) . Los archivos pre-dexopt'd (.odex) contenían relleno no inicializado en sistemas de 64 bits. Esto se corrigió: https://android.googlesource.com/platform/art/+/34ed3afc41820c72a3c0ab9770be66b6668aa029 .
Usar la herramienta de diferenciación de compilación
Para los casos en los que no es posible eliminar los cambios de archivos relacionados con la compilación, AOSP incluye una herramienta de diferenciación de compilación, target_files_diff.py
para usar en la comparación de dos paquetes de archivos. Esta herramienta realiza una diferencia recursiva entre dos compilaciones, excluyendo los cambios comunes de archivos relacionados con la compilación, como
- 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 build diff, ejecute 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.
Mantener la asignación de bloques consistente
Para un archivo determinado, aunque su contenido sigue siendo el mismo 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 de Virtual A / B OTA, las E / S innecesarias pueden aumentar en gran medida el espacio de almacenamiento necesario para almacenar la instantánea de copia en escritura. En una actualización OTA no 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 los bloques.
Para abordar este problema, en Android 7.0 Google extendió la herramienta make_ext4fs
para mantener la asignación de bloques consistente en todas las compilaciones. La herramienta make_ext4fs
acepta un indicador opcional -d base_fs
que intenta asignar archivos a los mismos bloques al generar una imagen ext4
. Puede extraer los archivos de mapeo de bloques (como los archivos de mapeo 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 del system
). 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, sí mejora el rendimiento de la actualización OTA al reducir la cantidad de E / S. Para las actualizaciones virtuales A / B, reduce drásticamente la cantidad de espacio de almacenamiento necesario para aplicar la OTA.
Evite actualizar aplicaciones
Además de minimizar las diferencias de compilación, puede reducir el tamaño de las actualizaciones de OTA al excluir las actualizaciones para las aplicaciones que obtienen actualizaciones a través de las tiendas de aplicaciones. Los APK a menudo comprenden una parte importante de varias particiones en un dispositivo. Incluir las últimas versiones de las aplicaciones que se actualizan en las tiendas de aplicaciones en una actualización OTA puede tener un impacto de gran tamaño en los paquetes OTA y proporcionar pocos beneficios para el usuario. Para cuando los usuarios reciban un paquete OTA, es posible que ya tengan la aplicación actualizada, o incluso una versión más nueva, recibida directamente de las tiendas de aplicaciones.