Android OTA [Seamless] Update Overview [Part2] - Detailed Analysis of OTA[A/B] Update
Detailed
Analysis
In the previous tutorial we saw an
overviw of the OTA [A/B Based] Update mechanism introduced by Android and
mandated after Android 11.
Changes to Android Makefiles for enabling Android A/B OTA Updates
With the
introduction of A/B style of updates, The device manufacturers must make
changes to the existing Compilation and Flashing steps. Let us have a look at
them below.
For the
present analysis, Let us use the Google’s Marlin device as an example.
A/B MUST
Define Flags
Detailed
Analysis
In the previous tutorial we saw an
overviw of the OTA [A/B Based] Update mechanism introduced by Android and
mandated after Android 11.
Changes to Android Makefiles for enabling Android A/B OTA Updates
With the
introduction of A/B style of updates, The device manufacturers must make
changes to the existing Compilation and Flashing steps. Let us have a look at
them below.
For the
present analysis, Let us use the Google’s Marlin device as an example.
A/B MUST
Define Flags
Variables that the system must define
AB_OTA_UPDATER := true
AB_OTA_PARTITIONS := boot system vendor
BOARD_BUILD_SYSTEM_ROOT_IMAGE := true
#Put the boot ramdisk in the system
partition
TARGET_NO_RECOVERY := true
BOARD_USES_RECOVERY_AS_BOOT := true
#Put the recovery ramdisk in the boot.img
file
PRODUCT_PACKAGES += update_engine
update_verifier
A/B Optionally
Defined Flags
AB_OTA_UPDATER := true
AB_OTA_PARTITIONS := boot system vendor
BOARD_BUILD_SYSTEM_ROOT_IMAGE := true
#Put the boot ramdisk in the system
partition
TARGET_NO_RECOVERY := true
BOARD_USES_RECOVERY_AS_BOOT := true
#Put the recovery ramdisk in the boot.img
file
PRODUCT_PACKAGES += update_engine
update_verifier
A/B Optionally
Defined Flags
A/B Variables optionally defined by the
system
PRODUCT_PACKAGES_DEBUG +=
update_engine_client
A/B Variables optionally defined by the
system
PRODUCT_PACKAGES_DEBUG +=
update_engine_client
A/B MUST
NOT Define Flags
A/B Variables that the system cannot
define
BOARD_RECOVERYIMAGE_PARTITION_SIZE
BOARD_CACHEIMAGE_PARTITION_SIZE
BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
More details can be obtained by searching
these variables in the build/core/Makefile
A/B Variables that the system cannot
define
BOARD_RECOVERYIMAGE_PARTITION_SIZE
BOARD_CACHEIMAGE_PARTITION_SIZE
BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
More details can be obtained by searching
these variables in the build/core/Makefile
Communication
between the bootloader & the A/B System
The variable bootloader_message_ab is
used to communicate the structure of the message to be passed to the bootloader
using the bootcontrol HAL.
/**
* The A/B-specific bootloader message structure (4-KiB).
*
* We separate A/B boot control metadata from the regular bootloader
* message struct and keep it here. Everything that's A/B-specific
* stays after struct bootloader_message, which belongs to the vendor
* space of /misc partition. Also, the A/B-specific contents should be
* managed by the A/B-bootloader or boot control HAL.
*
* The slot_suffix field is used for A/B implementations where the
* bootloader does not set the androidboot.ro.boot.slot_suffix kernel
* commandline parameter. This is used by fs_mgr to mount /system and
* other partitions with the slotselect flag set in fstab. A/B
* implementations are free to use all 32 bytes and may store private
* data past the first NUL-byte in this field. It is encouraged, but
* not mandatory, to use 'struct bootloader_control' described below.
*
* The update_channel field is used to store the Omaha update channel
* if update_engine is compiled with Omaha support.
*/
struct bootloader_message_ab {
struct bootloader_message message;
char slot_suffix[32];
char update_channel[128];
// Round up the entire struct to 4096-byte.
char reserved[1888];
};
The bootloader_message is further explained below
/* Bootloader Message (2-KiB)
*
* This structure describes the content of a block in flash
* that is used for recovery and the bootloader to talk to
* each other.
*
* The command field is updated by linux when it wants to
* reboot into recovery or to update radio or bootloader firmware.
* It is also updated by the bootloader when firmware update
* is complete (to boot into recovery for any final cleanup)
*
* The status field was used by the bootloader after the completion
* of an "update-radio" or "update-hboot" command, which has been
* deprecated since Froyo.
*
* The recovery field is only written by linux and used
* for the system to send a message to recovery or the
* other way around.
*
* The stage field is written by packages which restart themselves
* multiple times, so that the UI can reflect which invocation of the
* package it is. If the value is of the format "#/#" (eg, "1/3"),
* the UI will add a simple indicator of that status.
*
* We used to have slot_suffix field for A/B boot control metadata in
* this struct, which gets unintentionally cleared by recovery or
* uncrypt. Move it into struct bootloader_message_ab to avoid the
* issue.
*/
struct bootloader_message {
char command[32];
char status[32];
char recovery[768];
// The 'recovery' field used to be 1024 bytes. It has only ever
// been used to store the recovery command line, so 768 bytes
// should be plenty. We carve off the last 256 bytes to store the
// stage string (for multistage packages) and possible future
// expansion.
char stage[32];
// The 'reserved' field used to be 224 bytes when it was initially
// carved off from the 1024-byte recovery field. Bump it up to
// 1184-byte so that the entire bootloader_message struct rounds up
// to 2048-byte.
char reserved[1184];
};
Every OEM/SOC is supposed to have a
custom implementation of the boot control HAL and I would not like to talk
about any specific implementation.
However Google also has a reference
implementation for its IOT devices [Brillo], Let’s have a close look at the
contents of the boot_loader message for Brillo.
The major point of interest are
// Currently active slot.
uint8_t active_slot;
// Information about each slot.
BrilloSlotInfo slot_info[2];
This is used by the boot control HAL to
implement logic to inform about the slot status to the bootloader to help chose
the right slot to boot.
Let’s see the API’s of the boot control
HAL below with the most important API's marked.
bootctrl Test
In addition to defining the interface of
the HAL layer, AOSP also provides boot_control tools in a module called
bootctl, located at:
system/extras/bootctl/bootctl.c
We can run the bootctrl –help to get
more information.
Update Verifier & Boot Control HAL
The update_verifier module in
bootable/recovery/update_verifier also uses the bootcontrol HAL to check if the
update has been verified before marking the slot successful.
Finally, The bootloader reads the slot
to boot from using the “metadata” partition as below.
The variable bootloader_message_ab is
used to communicate the structure of the message to be passed to the bootloader
using the bootcontrol HAL.
/**
* The A/B-specific bootloader message structure (4-KiB).
*
* We separate A/B boot control metadata from the regular bootloader
* message struct and keep it here. Everything that's A/B-specific
* stays after struct bootloader_message, which belongs to the vendor
* space of /misc partition. Also, the A/B-specific contents should be
* managed by the A/B-bootloader or boot control HAL.
*
* The slot_suffix field is used for A/B implementations where the
* bootloader does not set the androidboot.ro.boot.slot_suffix kernel
* commandline parameter. This is used by fs_mgr to mount /system and
* other partitions with the slotselect flag set in fstab. A/B
* implementations are free to use all 32 bytes and may store private
* data past the first NUL-byte in this field. It is encouraged, but
* not mandatory, to use 'struct bootloader_control' described below.
*
* The update_channel field is used to store the Omaha update channel
* if update_engine is compiled with Omaha support.
*/
struct bootloader_message_ab {
struct bootloader_message message;
char slot_suffix[32];
char update_channel[128];
// Round up the entire struct to 4096-byte.
char reserved[1888];
};
The bootloader_message is further explained below
/* Bootloader Message (2-KiB)
*
* This structure describes the content of a block in flash
* that is used for recovery and the bootloader to talk to
* each other.
*
* The command field is updated by linux when it wants to
* reboot into recovery or to update radio or bootloader firmware.
* It is also updated by the bootloader when firmware update
* is complete (to boot into recovery for any final cleanup)
*
* The status field was used by the bootloader after the completion
* of an "update-radio" or "update-hboot" command, which has been
* deprecated since Froyo.
*
* The recovery field is only written by linux and used
* for the system to send a message to recovery or the
* other way around.
*
* The stage field is written by packages which restart themselves
* multiple times, so that the UI can reflect which invocation of the
* package it is. If the value is of the format "#/#" (eg, "1/3"),
* the UI will add a simple indicator of that status.
*
* We used to have slot_suffix field for A/B boot control metadata in
* this struct, which gets unintentionally cleared by recovery or
* uncrypt. Move it into struct bootloader_message_ab to avoid the
* issue.
*/
struct bootloader_message {
char command[32];
char status[32];
char recovery[768];
// The 'recovery' field used to be 1024 bytes. It has only ever
// been used to store the recovery command line, so 768 bytes
// should be plenty. We carve off the last 256 bytes to store the
// stage string (for multistage packages) and possible future
// expansion.
char stage[32];
// The 'reserved' field used to be 224 bytes when it was initially
// carved off from the 1024-byte recovery field. Bump it up to
// 1184-byte so that the entire bootloader_message struct rounds up
// to 2048-byte.
char reserved[1184];
};
Every OEM/SOC is supposed to have a
custom implementation of the boot control HAL and I would not like to talk
about any specific implementation.
However Google also has a reference
implementation for its IOT devices [Brillo], Let’s have a close look at the
contents of the boot_loader message for Brillo.
The major point of interest are
// Currently active slot.
uint8_t active_slot;
// Information about each slot.
BrilloSlotInfo slot_info[2];
This is used by the boot control HAL to
implement logic to inform about the slot status to the bootloader to help chose
the right slot to boot.
Let’s see the API’s of the boot control
HAL below with the most important API's marked.
bootctrl Test
In addition to defining the interface of
the HAL layer, AOSP also provides boot_control tools in a module called
bootctl, located at:
system/extras/bootctl/bootctl.c
We can run the bootctrl –help to get
more information.
Update Verifier & Boot Control HAL
The update_verifier module in
bootable/recovery/update_verifier also uses the bootcontrol HAL to check if the
update has been verified before marking the slot successful.
Finally, The bootloader reads the slot
to boot from using the “metadata” partition as below.
Comments
Post a Comment