06
utorak
sijeanj
2026
How Android Boot Process Works on Embedded SBCs
On an embedded SBC, Android boot is not a single step. It is a chain of handoffs: silicon ROM code finds the boot device, a bootloader prepares DRAM and loads images, the Linux kernel brings up drivers, and then Android user space starts services until the UI becomes interactive. If you are building AOSP for your own hardware, this sequence matters because most bring-up issues are simply the wrong change placed in the wrong layer.
Below is a practical walk-through of the boot flow you will see on many Rockchip/Amlogic/NXP-class Android SBC platforms. Names vary by vendor, but the responsibilities stay consistent.

1) What Boot Complete Really Means
Boot is considered done by different people at different times:
- Factory: device reaches fastboot/recovery and can be flashed repeatedly
- Kernel engineer: kernel reaches a login shell or prints Freeing unused kernel memory without panicking
- Android engineer: System Server runs and apps can start
- Product team: UI is responsive and the main app is ready for touch
When debugging, always decide which definition you care about. Many slow boot complaints are really slow time-to-interactive problems, not slow kernel start.
2) Stage 0: Boot ROM and Boot Media Selection
Every SoC has a small Boot ROM that runs immediately after reset. It typically:
- reads straps/fuses to decide boot order (eMMC, SD, SPI-NOR, USB, etc.)
- loads the first bootloader stage into internal SRAM
- sometimes performs basic signature checks (platform-dependent)
If the board is completely silent, you are usually stuck here or in early bootloader. At this stage, there may be no DRAM, no storage drivers beyond the minimal ROM logic, and limited logging.
3) Stage 1: Bootloader Brings Up DRAM and Loads Images
On many embedded platforms, the bootloader is multi-stage. You might see names like SPL, TF-A, U-Boot, or vendor equivalents. The bootloader typically:
- initializes DRAM and clocks so larger code can run
- sets up secure monitor components if required
- loads the Linux kernel image and device tree (DTB)
- loads an initramfs/ramdisk if the build uses one
- selects the correct slot on A/B systems
- offers fastboot/recovery for flashing and rescue
Where engineers most often touch the bootloader on Android SBCs:
- Board revision handling: choose DTB based on GPIO/EEPROM/board ID
- Recovery strategy: fallback to another slot or to recovery after repeated failures
- Bring-up conveniences: enable UART logs, allow boot delay, enable USB download modes
A common mistake is trying to fix hardware differences later in Android user space. If the panel timing or power rails are wrong, you generally want the DTB and kernel driver correct first, not a workaround in an app.
4) Stage 2: Linux Kernel Initialization
Once loaded, the kernel decompresses, sets up memory management, starts the scheduler, and probes hardware described by the device tree. On Android, this is still Linux, but often with vendor patches and an Android-oriented configuration.
During early kernel boot, the most important bring-up items are:
- Storage: eMMC/UFS/NVMe drivers must load so partitions can be mounted later
- Regulators and clocks: missing supplies cause silent peripheral failures
- Display pipeline: DRM/KMS, DSI/LVDS/eDP, panel init, backlight control
- Input: touch controller, keys, GPIO interrupts
If you see a kernel panic or repeated reboots before Android logs appear, focus on dmesg/UART output. At that point Android is not running yet, so changing frameworks will not help.
5) Stage 3: init Starts Android Userspace
After the kernel is up, it starts the first user-space process: init. Android init is a property-driven init system, not systemd. It reads init.rc files, starts native daemons, and mounts partitions.
Typical init responsibilities:
- mount core filesystems (proc, sysfs, tmpfs)
- create device nodes via ueventd
- set system properties used to control services
- mount Android partitions (system, vendor, product, odm, userdata)
- start key native services (logd, vold, hwservicemanager, etc.)
This is the stage where you start to see Android-specific failures like:
- partition mount errors due to fstab mistakes
- vendor services crashing because a device node is missing
- property triggers not firing because a value is wrong or late
6) SELinux Enforcing: The Invisible Wall
Embedded Linux habits do not always transfer well to Android because SELinux is commonly enforcing. Even if file permissions look correct, SELinux can block access to device nodes, sysfs entries, or sockets.
Practical symptoms:
- a service works on userdebug builds but fails on user builds
- hardware works when run as root via adb, but not from the app
- permission denied appears even though /dev nodes are readable
If your design uses a privileged daemon to talk to GPIO/I2C/SPI, define its SELinux domain early and keep the interface to apps stable (Binder/AIDL is common). This reduces one-off policy hacks later.
7) Framework Start: Zygote and System Server
When native services are ready, Android starts Zygote, which preloads common classes and forks app processes efficiently. Then System Server starts and brings up core framework services: Activity Manager, Package Manager, Window Manager, Input Manager, and others.
If your system reaches this point, you will usually see rich logs in logcat. Boot loops here typically come from:
- a vendor service dependency that is missing or crashing repeatedly
- a broken framework configuration or incompatible overlay
- corrupted data in userdata causing package manager issues
8) Display Comes Alive: SurfaceFlinger and Hardware Composer
On an SBC, the first frame depends on multiple pieces lining up:
- kernel display driver + correct panel timing
- graphics HALs load correctly
- SurfaceFlinger starts and can talk to the composer
- input devices are present so the UI can become interactive
It is common to see a splash screen or boot animation before the system is truly ready. From a product perspective, it is useful to track two different metrics:
- time-to-first-visible: something stable on screen
- time-to-interactive: touch works and the main app responds smoothly
If the display goes black only when the UI starts animating, that is often a sign of a graphics stack mismatch, a mode-setting issue, or a panel/backlight sequence problem under load.
9) A/B Slots and Recovery Behavior in Real Products
Many modern Android embedded systems use A/B partitions for safer OTA updates. The device has two slots, and the bootloader chooses the active one. If the new slot fails to boot successfully, the system can roll back.
For field devices, this is often worth implementing because it reduces the risk of bricking during updates. The trade-off is that you must maintain:
- slot metadata logic in the bootloader
- clear boot success signals from Android to the bootloader
- recovery paths for failed updates and partial flashes
10) A Simple Debug Map: Where to Look When Boot Fails
- No logs, no UART: power, boot straps, boot device, ROM stage
- Bootloader shows, kernel never starts: image layout, load addresses, DTB selection
- Kernel starts then reboots/panics: device tree, regulators, storage, initramfs
- Android logo then loops: init rc, fstab, vendor services, SELinux denials
- UI appears but unstable: graphics HAL, display timing, thermal/power limits
For long-term maintainability, it helps to embed build identifiers in a place you can query (firmware build ID, vendor image version, slot info). In the field, the ability to answer what exactly is running? saves more time than any single boot optimization.
Conclusion
Android boot on an embedded SBC is a sequence of controlled transitions: ROM code finds the boot path, the bootloader prepares hardware and loads images, the kernel initializes drivers, Android init mounts partitions and starts services, SELinux enforces access rules, and finally the framework and graphics stack make the UI interactive.
If you treat each stage as a separate engineering surfacebootloader for hardware identity and recovery, kernel/DT for real hardware modeling, init/SELinux for controlled access, framework for UI and application behavioryou can debug faster and ship systems that stay stable long after the first demo.
