Skip to content
This repository has been archived by the owner on Sep 11, 2023. It is now read-only.

[mbtool] ROM installer

Andrew Gunnerson edited this page Jan 20, 2016 · 2 revisions

Introduction

The base ROM installation code is handled in mbtool/installer.cpp. The mb::Installer class handles the chroot creation, filesystem mounting, update-binary execution, and logging. Subclasses of mb::Installer supply the input (target device, target ROM ID, input zip file, etc.) and a method of output (eg. write to TWRP's file descriptor using ui_print commands or simply dumping to stdout). Subclasses are able to hook into each of the stages described below.

The installer goes through several stages as described below:

  1. Initialization

    The installer first scans the input zip file for files named system.transfer.list, system.new.dat, and system.img. If any of those files exist, the zip is determined to contain a system image, which will affect the behavior of later stages.

    Subclasses can implement mb::Installer::on_initialize() to hook into this stage.

  2. Create chroot

    The /system, /cache, and /data partitions will first be mounted. This is done my simply running the mount command (eg. mount /system), so the host system is responsible for parsing the fstab file to correctly mount the partitions. /proc/mounts will be read to verify that the partitions were indeed mounted.

    The installer will then attempt to create the chroot at /chroot. If a prior installation failed and left stale mounts or leftover files, they will be unmounted or removed before the installation continues. If unmounting the old mount points fails, the installation will fail to prevent data loss.

    The following paths are then created in the chroot:

    Directories:

    • /mb/ - Temporary multiboot files
    • /dev/ - Block, character, and other special devices
    • /etc/ - Contains fstab file in TWRP/CWM. Mostly unused.
    • /proc/ - (Standard procfs directory)
    • /sbin/ - Usually contains programs in the ramdisk
    • /sys/ - (Standard sysfs directory)
    • /tmp/ - Temporary directory. Unused by mbtool. Possibly used by ROM's installer
    • /system/ - Mount point for target system directory
    • /cache/ - Mount point for target cache directory
    • /data/ - Mount point for target data directory

    Mounts:

    • '/dev/ (tmpfs)`
    • /dev/pts/ (devpts)
    • /proc/ (proc)
    • /sbin/ (tmpfs)
    • /sys/ (sysfs)
    • /tmp/ (tmpfs)

    Special files:

    • /dev/console (maj=5, min=1)
    • /dev/null (maj=1, min=3)
    • /dev/ptmx (maj=5, min=2)
    • /dev/random (maj=1, min=8)
    • /dev/tty (maj=5, min=0)
    • /dev/urandom (maj=1, min=9)
    • /dev/zero (maj=1, min=5)
    • /dev/loop-control (maj=10, min=237)

    Copied:

    • Host's /sbin/ -> chroot's /sbin/
    • Host's /dev/input/ -> chroot's /dev/input/ (Required for AROMA)
    • Host's /dev/graphics/ -> chroot's /dev/graphics/ (Required for AROMA)

    Subclasses can implement mb::Installer::on_created_chroot() to hook into this stage.

  3. Set up environment

    The temporary directory (usually /multiboot/) will be deleted to prevent issues. The multiboot files below are extracted from the patched zip.

    • META-INF/com/google/android/update-binary.orig -> <temp dir>/updater - The ROM's original update-binary file
    • multiboot/bb-wrapper.sh -> <temp dir>/bb-wrapper.sh - A wrapper around busybox to prevent the mount, umount, and reboot commands from being run inside the chroot
    • multiboot/info.prop -> <temp dir>/info.prop - Properties file containing various settings for the installer, such as the target device and ROM ID

    This stage cannot be hooked by subclasses.

  4. Check device

    This stage will perform several actions related to the target device:

    • Verify that mbtool.installer.device in info.prop matches one of the following properties (unless mbtool.installer.ignore-codename is set to true, in which case the check will be skipped)
      • ro.product.device
      • ro.build.product
      • ro.patcher.device
    • Copy boot partition block devices to chroot (as specified in libmbp via Device::setBootBlockDevs())
    • Copy recovery partition block devices to chroot (as specified in libmbp via Device::setRecoveryBlockDevs())
    • Copy extra partition block devices to chroot (as specified in libmbp via Device::setExtraBlockDevs())

    Subclasses can implement mb::Installer::on_checked_device() to hook into this stage.

  5. Get install type

    This stage gathers various information regarding the target ROM ID. It first calls mb::Installer::get_install_type(), which must be implemented by subclasses. If the ROM ID is valid, then a mb::Rom object will be created. Otherwise, the installation will fail.

    This stage cannot be hooked by subclasses.

  6. Set up chroot

    In this stage, the installer will calculate the SHA1 hash of the boot partition. This is used at the end of the installation to determine if the boot image needs to be modified. The boot partition will also be copied to <temp dir>/boot.orig, which is restored if the installation fails. The busybox wrapper is also set up in this stage.

    The following operations are now performed in the chroot:

    • Move /sbin/busybox to /sbin/busybox.orig
    • Copy busybox wrapper to /sbin/busybox
    • Copy mbtool to /update-binary-tool
    • Copy host's /default.prop to chroot's /default.prop
    • Copy host's /file_contexts to chroot's /file_contexts

    Subclasses can implement mb::Installer::on_set_up_chroot() to hook into this stage.

  7. Mount filesystems

    In this stage, the /system, /cache, and /data directories for the target ROM will be mounted. If the target system, cache, or data paths are images, they will be created as follows:

    • System image: Size of actual device's /system partition (Used by extsd-slot-*)
    • Cache image: 4 * 1024 * 1024 * 1024 bytes (4 GiB) (Not used by any install target)
    • Data image: 4 * 1024 * 1024 * 1024 bytes (4 GiB) (Not used by any install target)

    If the target system path is an image, then the image file will be bind mounted to /mb/system.img in the chroot. This is necessary for ROMs that flash system images instead of copying files.

    No further operations occur if a system image is used. If the system path is a directory, then multiple actions could occur:

    • If installing to non-primary and the zip contains no system image, then the system path is simply bind mounted to /system in the chroot
    • If install to primary or the zip contains a system image, then a temporary image is created at /data/.system.img.tmp (or <external SD>/.system.img.tmp if space is insufficient) and the contents of the system path are copied into the image. The temporary image is then bind mounted into /mb/system.img in the chroot.

    Subclasses can implement mb::Installer::on_mounted_filesystems() to hook into this stage.

  8. Installation

    This stage handles the actual installation of the ROM. The original update-binary (<temp dir>/updater) will be bind mounted to /mb/updater in the chroot and the zip file will also be bind mounted in the chroot (/mb/install.zip). The update-binary will be searched for several strings to determine if it's likely an AROMA installer. If it is, then SIGSTOP will be sent to the parent process to prevent the recovery interface from fighting over the framebuffer. (SIGCONT will be sent after installation.)

    Subclasses can implement mb::Installer::get_properties() to provide a set of properties that will be available in the installation environment (eg. via getprop() in the updater-script). The subclass can also specify whether the passthrough setting. If passthrough is enabled, the subclass will provide a file descriptor that is passed to the updater binary as the second argument. This is what the recovery does when it flashes a zip. The file descriptor allows the ROM installer to display messages on screen (among other things). If the passthrough option is disabled, mbtool will create a pipe, pass it to the ROM installer, and will parse and process all of the data. Note that mbtool will discard all messages besides ui_print.

    The following command will then be executed to start the ROM's installer: /mb/updater <interface> <output fd> </path/to/zip>

    Subclasses can implement mb::Installer::on_pre_install() to hook into this stage before the installation. Subclasses can implement mb::Installer::on_post_install() to hook into this stage after the installation.

  9. Unmount filesystems

    /system, /cache, and /data in the chroot will be unmounted. If the system path is an image file, the installer will run e2fsck to repair the filesystem (if corrupted). If the system path is a temporary image file, the contents will be copied back to the appropriate system directory.

    Subclasses can implement mb::Installer::on_unmounted_filesystems() to hook into this stage.

  10. Completion

The SHA1 hash of the boot partition will be calculated and compared to the previous hash. If they are not equal, a new file, /romid, containing the ROM ID string, will be added to the ramdisk. If /default.prop exists in the ramdisk, the property ro.patcher.device=<target device> will be appended to the end of the file. If the boot image had any hacks or workarounds for locked bootloaders that are supported by libmbp (eg. loki, bump), they will be reapplied.

Afterwards, the 'good' copy of the boot image will be saved in /sdcard/MultiBoot/<ROM ID>/boot.img and the SHA512 hash of it added to /data/multiboot/checksums.prop. The permissions, ownership, and SELinux label on /sdcard/MultiBoot will be recursively set such that they match /data/media/0.

Subclasses can implement mb::Installer::on_finished() to hook into this stage.

  1. Cleanup

This stage is always called regardless if the installation succeeds or fails. If the installation fails, the backup copy of the boot partition will be restored. If a temporary system image was created, it will be removed. All mount points in the chroot will be unmounted and the directory will be deleted (unless a filesystem could not be unmounted).

Subclasses can implement mb::Installer::on_cleanup() to hook into this stage.

Debugging installation issues

Unfortunately, installation issues will inevitably occur. mbtool provides a few methods to make debugging issues easier.

Manual installation from recovery through adb

If the device's recovery does not log stdout/stderr when flashing zips, it is possible to run mbtool manually through adb. First, reboot to recovery, and then run the following commands:

adb push /path/to/mbtool_recovery /tmp/updater
adb shell chmod 755 /tmp/updater
adb shell /tmp/updater 3 1 /sdcard/example_rom_data-slot-blah.zip

Gaining access to a shell inside the chroot

In mbtool/installer.cpp, change the following defines to 1:

#define DEBUG_PRE_SHELL 1
#define DEBUG_POST_SHELL 1

When running the installation manually via the command line as shown above, a shell will be launched from inside the chroot immediately before and after the installation. Exiting the shell causes the installation to proceed.

Running the installer through a wrapper

mbtool also allows the installer to be run under a wrapper program, such as strace or valgrind. To do so, set DEBUG_USE_UPDATER_WRAPPER to 1 in mbtool/installer.cpp and set the path and arguments accordingly.

// Use a wrapper around the updater
#define DEBUG_USE_UPDATER_WRAPPER 1
#define DEBUG_UPDATER_WRAPPER_PATH "/tmp/strace_static"
//#define DEBUG_UPDATER_WRAPPER_ARGS "-f" // Comma-separated strings