Google se compromete a promover la equidad racial para las comunidades negras. Ver cómo.
Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Actualizaciones del sistema A / B (sin interrupciones)

Las actualizaciones del sistema A / B, también conocidas como actualizaciones integradas, aseguran que un sistema de arranque viable permanezca en el disco durante una actualización inalámbrica (OTA) . Este enfoque reduce la probabilidad de un dispositivo inactivo después de una actualización, lo que significa menos reemplazos y actualizaciones de dispositivos en los centros de reparación y garantía. Otros sistemas operativos de grado comercial como ChromeOS también usan actualizaciones A / B con éxito.

Para obtener más información sobre las actualizaciones del sistema A / B y cómo funcionan, consulte Selección de particiones (ranuras) .

Las actualizaciones del sistema A / B brindan los siguientes beneficios:

  • Las actualizaciones de OTA pueden ocurrir mientras el sistema se está ejecutando, sin interrumpir al usuario. Los usuarios pueden continuar usando sus dispositivos durante una OTA; el único tiempo de inactividad durante una actualización es cuando el dispositivo se reinicia en la partición de disco actualizada.
  • Después de una actualización, el reinicio no lleva más tiempo que un reinicio normal.
  • Si una OTA no se aplica (por ejemplo, debido a un flash defectuoso), el usuario no se verá afectado. El usuario continuará ejecutando el sistema operativo anterior y el cliente puede volver a intentar la actualización.
  • Si se aplica una actualización OTA pero no se inicia, el dispositivo se reiniciará en la partición anterior y seguirá siendo utilizable. El cliente es libre de volver a intentar la actualización.
  • Cualquier error (como los errores de E / S) afecta solo al conjunto de particiones no utilizado y se puede volver a intentar. Tales errores también se vuelven menos probables porque la carga de E / S es deliberadamente baja para evitar degradar la experiencia del usuario.
  • Las actualizaciones se pueden transmitir a dispositivos A / B, eliminando la necesidad de descargar el paquete antes de instalarlo. La transmisión significa que no es necesario que el usuario tenga suficiente espacio libre para almacenar el paquete de actualización en /data o /cache .
  • La partición de caché ya no se usa para almacenar paquetes de actualización OTA, por lo que no es necesario asegurarse de que la partición de caché sea lo suficientemente grande para futuras actualizaciones.
  • dm-verity garantiza que un dispositivo iniciará una imagen corrupta. Si un dispositivo no se inicia debido a un problema de OTA o dm-verity incorrecto, el dispositivo puede reiniciarse en una imagen antigua. (El arranque verificado de Android no requiere actualizaciones A / B).

Acerca de las actualizaciones del sistema A / B

Las actualizaciones A / B requieren cambios tanto en el cliente como en el sistema. Sin embargo, el servidor de paquetes OTA no debería requerir cambios: los paquetes de actualización aún se sirven a través de HTTPS. Para los dispositivos que usan la infraestructura OTA de Google, todos los cambios del sistema están en AOSP, y el código del cliente lo proporcionan los servicios de Google Play. Los OEM que no utilicen la infraestructura OTA de Google podrán reutilizar el código del sistema AOSP, pero deberán suministrar su propio cliente.

Para los OEM que suministran su propio cliente, el cliente debe:

  • Decide cuándo tomar una actualización. Debido a que las actualizaciones A / B ocurren en segundo plano, ya no son iniciadas por el usuario. Para evitar interrumpir a los usuarios, se recomienda que las actualizaciones se programen cuando el dispositivo está en modo de mantenimiento inactivo, como durante la noche y con Wi-Fi. Sin embargo, su cliente puede usar cualquier heurística que desee.
  • Consulte con sus servidores de paquetes OTA y determine si hay una actualización disponible. Esto debería ser casi igual a su código de cliente existente, excepto que querrá indicar que el dispositivo es compatible con A / B. (El cliente de Google también incluye un botón Comprobar ahora para que los usuarios busquen la última actualización).
  • Llame a update_engine con la URL HTTPS para su paquete de actualización, suponiendo que haya uno disponible. update_engine actualizará los bloques sin procesar en la partición no utilizada actualmente mientras transmite el paquete de actualización.
  • Informe los éxitos o fallos de instalación a sus servidores, según el código de resultado update_engine . Si la actualización se aplica con éxito, update_engine le indicará al gestor de arranque que se inicie en el nuevo sistema operativo en el próximo reinicio. El gestor de arranque recurrirá al sistema operativo anterior si el nuevo sistema operativo no se inicia, por lo que no se requiere trabajo del cliente. Si la actualización falla, el cliente debe decidir cuándo (y si) volver a intentarlo, según el código de error detallado. Por ejemplo, un buen cliente podría reconocer que un paquete OTA parcial ("diff") falla y probar un paquete OTA completo.

Opcionalmente, el cliente puede:

  • Mostrar una notificación pidiéndole al usuario que reinicie. Si desea implementar una política en la que se alienta al usuario a actualizar periódicamente, esta notificación se puede agregar a su cliente. Si el cliente no solicita a los usuarios, los usuarios recibirán la actualización la próxima vez que reinicien de todos modos. (El cliente de Google tiene un retraso configurable por actualización).
  • Muestre una notificación que indique a los usuarios si iniciaron en una nueva versión del sistema operativo o si se esperaba que lo hicieran, pero volvieron a la versión anterior del sistema operativo. (El cliente de Google normalmente no lo hace).

En el lado del sistema, las actualizaciones del sistema A / B afectan lo siguiente:

  • Selección de partición (slots), el demonio update_engine e interacciones del gestor de arranque (descritas a continuación)
  • Proceso de compilación y generación de paquetes de actualización OTA (descritos en Implementación de actualizaciones A / B )

Selección de partición (ranuras)

Las actualizaciones del sistema A / B usan dos conjuntos de particiones denominadas ranuras (normalmente ranura A y ranura B). El sistema se ejecuta desde la ranura actual mientras el sistema en ejecución no accede a las particiones en la ranura no utilizada durante el funcionamiento normal. Este enfoque hace que las actualizaciones sean resistentes a fallas al mantener la ranura no utilizada como respaldo: si se produce un error durante o inmediatamente después de una actualización, el sistema puede retroceder a la ranura anterior y continuar teniendo un sistema en funcionamiento. Para lograr este objetivo, ninguna partición utilizada por la ranura actual debe actualizarse como parte de la actualización OTA (incluidas las particiones para las que solo hay una copia).

Cada ranura tiene un atributo de arranque que indica si la ranura contiene un sistema correcto desde el cual el dispositivo puede arrancar. La ranura actual se puede iniciar cuando el sistema se está ejecutando, pero la otra ranura puede tener una versión antigua (aún correcta) del sistema, una versión más nueva o datos no válidos. Independientemente de cuál sea la ranura actual , hay una ranura que es la ranura activa (la que se iniciará el gestor de arranque en el próximo arranque) o la ranura preferida .

Cada ranura también tiene un atributo exitoso establecido por el espacio del usuario, que es relevante solo si la ranura también es de arranque. Una ranura exitosa debería poder arrancar, ejecutarse y actualizarse. Una ranura de arranque que no se marcó como exitosa (después de que se hicieron varios intentos de arranque desde ella) debe ser marcada como no arrancable por el gestor de arranque, incluido el cambio de la ranura activa a otra ranura de arranque (normalmente a la ranura que se ejecuta inmediatamente antes del intento de arranque en el nuevo, activo). Los detalles específicos de la interfaz se definen en boot_control.h .

Actualizar motor demonio

Las actualizaciones del sistema A / B utilizan un demonio en segundo plano llamado update_engine para preparar el sistema para que arranque en una versión nueva y actualizada. Este demonio puede realizar las siguientes acciones:

  • Lea las particiones A / B de la ranura actual y escriba cualquier dato en las particiones A / B de la ranura no utilizada según las instrucciones del paquete OTA.
  • Llame a la interfaz boot_control en un flujo de trabajo predefinido.
  • Ejecute un programa posterior a la instalación desde la nueva partición después de escribir todas las particiones de ranura no utilizadas, según las instrucciones del paquete OTA. (Para más detalles, ver Post-instalación ).

Como el demonio update_engine no está involucrado en el proceso de arranque en sí, está limitado en lo que puede hacer durante una actualización por las políticas y características de SELinux en la ranura actual (tales políticas y características no pueden actualizarse hasta que el sistema se inicie en un nueva versión). Para mantener un sistema robusto, el proceso de actualización no debe modificar la tabla de particiones, el contenido de las particiones en la ranura actual o el contenido de las particiones que no son A / B que no se pueden borrar con un restablecimiento de fábrica.

Actualizar fuente del motor

La fuente update_engine se encuentra en system/update_engine . Los archivos dexopt A / B OTA se dividen entre installd y un administrador de paquetes:

Para un ejemplo de trabajo, consulte /device/google/marlin/device-common.mk .

Actualizar registros del motor

Para las versiones de Android update_engine anteriores, los registros de update_engine se pueden encontrar en logcat y en el informe de errores. Para que los registros de update_engine estén disponibles en el sistema de archivos, aplique los siguientes cambios en su compilación:

Estos cambios guardan una copia del registro update_engine más reciente en /data/misc/update_engine_log/update_engine. YEAR - TIME . Además del registro actual, los cinco registros más recientes se guardan en /data/misc/update_engine_log/ . Los usuarios con el ID del grupo de registro podrán acceder a los registros del sistema de archivos.

Interacciones del cargador de arranque

boot_control HAL es utilizado por update_engine (y posiblemente otros demonios) para indicarle al gestor de arranque desde qué arranque. Los escenarios de ejemplo comunes y sus estados asociados incluyen lo siguiente:

  • Caso normal : el sistema se está ejecutando desde su ranura actual, ya sea la ranura A o B. Hasta ahora no se han aplicado actualizaciones. La ranura actual del sistema es de arranque, exitosa y la ranura activa.
  • Actualización en curso : el sistema se ejecuta desde la ranura B, por lo que la ranura B es la ranura de arranque, exitosa y activa. La ranura A se marcó como no arrancable ya que los contenidos de la ranura A se están actualizando pero aún no se han completado. Un reinicio en este estado debería continuar arrancando desde la ranura B.
  • Actualización aplicada, reinicio pendiente : el sistema se está ejecutando desde la ranura B, la ranura B es de arranque y exitosa, pero la ranura A se marcó como activa (y por lo tanto se marcó como de arranque). La ranura A aún no está marcada como exitosa y el gestor de arranque debe realizar algunos intentos de arranque desde la ranura A
  • Sistema reiniciado en una nueva actualización : el sistema se está ejecutando desde la ranura A por primera vez, la ranura B todavía es arrancable y exitosa, mientras que la ranura A solo es arrancable y sigue activa pero no exitosa. Un demonio de espacio de usuario, update_verifier , debe marcar la ranura A como exitosa después de que se realicen algunas comprobaciones.

Soporte de actualización de transmisión

Los dispositivos de usuario no siempre tienen suficiente espacio en /data para descargar el paquete de actualización. Como ni los OEM ni los usuarios desean desperdiciar espacio en una partición /cache , algunos usuarios no tienen actualizaciones porque el dispositivo no tiene dónde almacenar el paquete de actualización. Para abordar este problema, Android 8.0 agregó soporte para la transmisión de actualizaciones A / B que escriben bloques directamente en la partición B a medida que se descargan, sin tener que almacenar los bloques en /data . Las actualizaciones de transmisión A / B casi no necesitan almacenamiento temporal y requieren suficiente almacenamiento para aproximadamente 100 KiB de metadatos.

Para habilitar las actualizaciones de transmisión en Android 7.1, seleccione los siguientes parches:

Estos parches son necesarios para admitir la transmisión de actualizaciones A / B en Android 7.1 y posteriores, ya sea que utilicen Google Mobile Services (GMS) o cualquier otro cliente de actualización.

Vida de una actualización A / B

El proceso de actualización comienza cuando un paquete OTA (denominado en el código como carga útil ) está disponible para descargar. Las políticas en el dispositivo pueden diferir la descarga de la carga útil y la aplicación en función del nivel de batería, la actividad del usuario, el estado de carga u otras políticas. Además, debido a que la actualización se ejecuta en segundo plano, es posible que los usuarios no sepan que hay una actualización en curso. Todo esto significa que el proceso de actualización podría interrumpirse en cualquier momento debido a políticas, reinicios inesperados o acciones del usuario.

Opcionalmente, los metadatos en el paquete OTA indican que la actualización se puede transmitir; el mismo paquete también se puede usar para la instalación sin transmisión. El servidor puede usar los metadatos para decirle al cliente que está transmitiendo, por lo que el cliente entregará la OTA para update_engine correctamente. Los fabricantes de dispositivos con su propio servidor y cliente pueden habilitar las actualizaciones de transmisión asegurándose de que el servidor identifique que la actualización está transmitiendo (o asume que todas las actualizaciones están transmitiendo) y el cliente realiza la llamada correcta a update_engine para transmitir. Los fabricantes pueden usar el hecho de que el paquete es de la variante de transmisión para enviar una bandera al cliente para activar la transferencia al lado del marco como transmisión.

Una vez que hay una carga útil disponible, el proceso de actualización es el siguiente:

Paso Ocupaciones
1 La ranura actual (o "ranura de origen") se marca como exitosa (si aún no está marcada) con markBootSuccessful() .
2 La ranura no utilizada (o "ranura de destino") se marca como no setSlotAsUnbootable() llamando a la función setSlotAsUnbootable() . La ranura actual siempre se marca como exitosa al comienzo de la actualización para evitar que el gestor de arranque vuelva a la ranura no utilizada, que pronto tendrá datos no válidos. Si el sistema ha llegado al punto en el que puede comenzar a aplicar una actualización, la ranura actual se marca como exitosa incluso si otros componentes principales están rotos (como la interfaz de usuario en un bucle de bloqueo), ya que es posible impulsar un nuevo software para solucionarlos. problemas.

La carga útil de actualización es un blob opaco con las instrucciones para actualizar a la nueva versión. La carga útil de actualización consta de lo siguiente:
  • Metadatos Una porción relativamente pequeña de la carga útil de actualización, los metadatos contienen una lista de operaciones para producir y verificar la nueva versión en la ranura de destino. Por ejemplo, una operación podría descomprimir un determinado blob y escribirlo en bloques específicos en una partición de destino, o leer desde una partición de origen, aplicar un parche binario y escribir en ciertos bloques en una partición de destino.
  • Datos extra Como la mayor parte de la carga útil de actualización, los datos adicionales asociados con las operaciones consisten en el blob comprimido o parche binario en estos ejemplos.
3 Los metadatos de la carga útil se descargan.
4 4 Para cada operación definida en los metadatos, en orden, los datos asociados (si los hay) se descargan en la memoria, se aplica la operación y se descarta la memoria asociada.
5 5 Todas las particiones se vuelven a leer y se verifican con el hash esperado.
6 6 Se ejecuta el paso posterior a la instalación (si corresponde). En el caso de un error durante la ejecución de cualquier paso, la actualización falla y se vuelve a intentar con una carga útil diferente. Si todos los pasos hasta ahora han tenido éxito, la actualización tiene éxito y se ejecuta el último paso.
7 7 La ranura no utilizada se marca como activa llamando a setActiveBootSlot() . Marcar la ranura no utilizada como activa no significa que finalizará el arranque. El gestor de arranque (o el sistema en sí) puede volver a cambiar la ranura activa si no lee un estado exitoso.
8 La instalación posterior (descrita a continuación) implica ejecutar un programa desde la versión de "nueva actualización" mientras aún se ejecuta en la versión anterior. Si se define en el paquete OTA, este paso es obligatorio y el programa debe regresar con el código de salida 0 ; de lo contrario, la actualización falla.
9 Después de que el sistema arranca con éxito lo suficiente en la nueva ranura y finaliza las comprobaciones posteriores al reinicio, la ranura actual (anteriormente la "ranura de destino") se marca como exitosa llamando a markBootSuccessful() .

Posterior a la instalación

Para cada partición donde se define un paso posterior a la instalación, update_engine monta la nueva partición en una ubicación específica y ejecuta el programa especificado en la OTA en relación con la partición montada. Por ejemplo, si el programa posterior a la instalación se define como usr/bin/postinstall en la partición del sistema, esta partición de la ranura no utilizada se montará en una ubicación fija (como /postinstall_mount ) y /postinstall_mount/usr/bin/postinstall Se ejecuta el comando /postinstall_mount/usr/bin/postinstall .

Para que la instalación posterior tenga éxito, el núcleo antiguo debe ser capaz de:

  • Montar el nuevo formato del sistema de archivos . El tipo de sistema de archivos no puede cambiar a menos que haya soporte para él en el núcleo antiguo, incluidos detalles como el algoritmo de compresión utilizado si se usa un sistema de archivos comprimido (es decir, SquashFS).
  • Comprenda el formato del programa posterior a la instalación de la nueva partición . Si utiliza un binario de formato ejecutable y enlazable (ELF), debe ser compatible con el núcleo antiguo (por ejemplo, un nuevo programa de 64 bits que se ejecuta en un núcleo antiguo de 32 bits si la arquitectura cambia de compilaciones de 32 a 64 bits). A menos que el cargador ( ld ) ld instrucciones de usar otras rutas o construir un binario estático, las bibliotecas se cargarán desde la imagen del sistema anterior y no desde la nueva.

Por ejemplo, podría usar un script de shell como un programa posterior a la instalación interpretado por el binario de shell del sistema antiguo con un #! marcador en la parte superior), luego configure rutas de biblioteca desde el nuevo entorno para ejecutar un programa binario más complejo posterior a la instalación. Alternativamente, puede ejecutar el paso posterior a la instalación desde una partición más pequeña dedicada para permitir que el formato del sistema de archivos en la partición del sistema principal se actualice sin incurrir en problemas de compatibilidad con versiones anteriores o actualizaciones de trampolín; esto permitiría a los usuarios actualizar directamente a la última versión desde una imagen de fábrica.

El nuevo programa posterior a la instalación está limitado por las políticas de SELinux definidas en el sistema anterior. Como tal, el paso posterior a la instalación es adecuado para realizar tareas requeridas por el diseño en un dispositivo dado u otras tareas de mejor esfuerzo (es decir, actualizar el firmware o el cargador de arranque con capacidad A / B, preparar copias de bases de datos para la nueva versión, etc. ) El paso posterior a la instalación no es adecuado para reparaciones de errores únicas antes del reinicio que requieren permisos imprevistos.

El programa seleccionado posterior a la instalación se ejecuta en el contexto de SELinux postinstall la postinstall . Todos los archivos en la nueva partición montada se etiquetarán con postinstall_file , independientemente de cuáles sean sus atributos después de reiniciar en ese nuevo sistema. Los cambios en los atributos de SELinux en el nuevo sistema no afectarán el paso posterior a la instalación. Si el programa posterior a la instalación necesita permisos adicionales, estos deben agregarse al contexto posterior a la instalación.

Después de reiniciar

Después de reiniciar, update_verifier activa la verificación de integridad usando dm-verity. Esta comprobación comienza antes de cigoto para evitar que los servicios de Java realicen cambios irreversibles que impidan una reversión segura. Durante este proceso, el gestor de arranque y el kernel también pueden activar un reinicio si el arranque verificado o dm-verity detectan algún daño. Una vez completada la comprobación, update_verifier marca el inicio con éxito.

update_verifier leerá solo los bloques enumerados en /data/ota_package/care_map.txt , que se incluye en un paquete A / B OTA cuando se utiliza el código AOSP. El cliente de actualización del sistema Java, como GmsCore, extrae care_map.txt , configura el permiso de acceso antes de reiniciar el dispositivo y elimina el archivo extraído una vez que el sistema arranca con éxito en la nueva versión.