Развлечение на выходные: собираем Android для Raspberry Pi из исходного кода

dee43e5ea73cbf142ba3d30a90473ae4.png

Привет, Хабр! В этой статье я хочу поделиться пошаговой инструкцией по компиляции Android Open Source Project для Raspberry Pi. Эта статья поможет разобраться в первых шагах разработки ОС на базе Android. В тех местах, где можно обойтись ссылкой на официальную документацию или документацию форка AOSP для Raspberry Pi, я так и поступлю.

Если вы решите повторить мой эксперимент, вам понадобится следующее оборудование:

  • Raspberry Pi 4

  • Компьютер с Linux (официально рекомендуется использовать Ubuntu)

  • USB-C кабель

  • картридер для microSD-карт и сама карта

  • (опционально, но очень желательно) Raspberry Pi Debug probe или другой UART-переходник

Настройка окружения для сборки

На этом этапе нам понадобится установить пакеты, необходимые для сборки. Все они доступны в стандартных репозиториях, поэтому ничего интересного в этом разделе не будет.

Дополнительно к пакетам из документации мне понадобилось установить pkg-config, meson и модуль mako для python3.

Клонирование репозитория

AOSP состоит из более чем тысячи git-репозиториев + репозиториев, специфичных для конкретного устройства (в нашем случае это проект android-rpi). Для управления локальной копией git-репозиториев используется утилита repo.

Ключевые команды, которые нам понадобятся:

repo init -u https://android.googlesource.com/platform/manifest -b android-13.0.0_r66
git clone https://github.com/android-rpi/local_manifests .repo/local_manifests -b arpi-13
repo sync

Первая команда клонирует репозиторий с манифестом AOSP, в котором хранится информация обо всех репозиториях, предоставляемых Google.
Вторая команда добавляет манифест с репозиториями, специфичными для устройства, а третья выполнит git clone для всех репозиториев, указанных в одном из манифестов.

Сборка Android

Инициализация окружения

В документации команды описаны более подробно, здесь я приведу только те, которые понадобятся нам для сборки образа ОС.

. build/envsetup.sh

Lunch target

Одна и та же копия исходного кода AOSP поддерживает варианты сборки для произвольного количества устройств. Достигается это при помощи lunch targets (или lunch combos, pun intended), которые состоят из названия устройства и типа прошивки (eng, userdebug или user).

lunch rpi4-eng

Компиляция

В процессе компиляции используются несколько систем сборки: make, soong, ninja, а начиная с Android 13 к этому зоопарку добавился ещё и Bazel. В этой статье мы не будем вдаваться в детали, как эти системы взаимодействуют между собой, и просто вызовем цели make для сборки конечных образов ОС:

make ramdisk systemimage vendorimage

На моём стареньком i5–9600k чистая сборка занимает около двух с половиной часов, поэтому на этом этапе можно прерваться и сходить пообедать :)

Также на этом этапе могут возникать ошибки компиляции, но обычно они связаны с отсутствующими в системе зависимостями. После получения сообщения «Build completed successfully» можно переходить к следующему этапу — разметке SD-карты.

Сборка ядра Linux

Обычно ядро в Android-проектах не собирается одновременно с остальной ОС, а поставляется в виде предсобранного бинарного артефакта. В нашем случае такого артефакта нет, а дальше в статье мы увидим, что нам понадобится собрать дополнительный модуль ядра.

mkdir kernel
cd kernel
repo init -u https://github.com/android-rpi/kernel_manifest -b arpi-5.15
repo sync
build/build.sh

Поскольку ядро обычно собирается отдельно, у него есть свой манифест и свой набор репозиториев.

Структура разделов

В отличие от традиционного Linux, где и ОС, и пользовательские файлы могут находиться на одном разделе, Android требует наличия нескольких разделов с разным назначением:

  • boot содержит образ ядра Linux

  • system содержит Android Framework, который включает в себя всё, с чем мы привыкли работать из обычных приложений — системные сервисы, Java-библиотеки и системные ресурсы.

  • vendor содержит (обычно) проприетарные сервисы, разработанные вендором SoC, на котором работает устройство

  • data содержит пользовательские данные, включая установленные приложения и их данные

Для того чтобы разметить всю SD-карту одной командой, я написал небольшой shell-скрипт, который использует артефакты, собранные на предыдущих шагах:

#!/usr/bin/env bash

set -ex

AOSP_ROOT="android-rpi"
AOSP_OUT="android-rpi/out/target/product/rpi4" 
KERNEL_OUT="kernel/out/arpi-5.15/dist"

DEVICE=${1:?Device not set}
SYSTEM_PARTITION="${DEVICE}2"
VENDOR_PARTITION="${DEVICE}3"
BOOT_MOUNTPOINT=/media/$USER/android_boot

wipefs -a $DEVICE*
sfdisk /dev/sdb < partitions.sfdisk
mkfs -t vfat /dev/sdb1
mkfs -t ext4 -L userdata /dev/sdb4

dd if=$AOSP_OUT/system.img of=$SYSTEM_PARTITION bs=1M
dd if=$AOSP_OUT/vendor.img of=$VENDOR_PARTITION bs=1M

mkdir -p $BOOT_MOUNTPOINT
mount /dev/sdb1 $BOOT_MOUNTPOINT
# copy firmware & ramdisk
cp $AOSP_ROOT/device/arpi/rpi4/boot/* $BOOT_MOUNTPOINT
cp $AOSP_OUT/ramdisk.img $BOOT_MOUNTPOINT

# copy kernel binaries
cp $KERNEL_OUT/Image.gz $BOOT_MOUNTPOINT
cp $KERNEL_OUT/bcm2711-rpi-*.dtb $BOOT_MOUNTPOINT
mkdir $BOOT_MOUNTPOINT/overlays
cp $KERNEL_OUT/vc4-kms-v3d-pi4.dtbo $BOOT_MOUNTPOINT/overlays

# enable adb over usb
# cp $KERNEL_OUT/dwc2.dtbo $BOOT_MOUNTPOINT/overlays
# echo 'dtoverlay=dwc2,dr_mode=peripheral' >> $BOOT_MOUNTPOINT/config.txt

umount $BOOT_MOUNTPOINT
rmdir $BOOT_MOUNTPOINT

Схема разделов хранится рядом в partitions.sfdisk:

label: dos
label-id: 0xa35ef4be
device: /dev/sdb
unit: sectors
sector-size: 512

/dev/sdb1 : start=        2048, size=      262144, type=c, bootable
/dev/sdb2 : start=      264192, size=     2097152, type=83
/dev/sdb3 : start=     2361344, size=      262144, type=83
/dev/sdb4 : start=     2623488, size=    58493952, type=83

Вот и всё! После этого можно вставить SD-карту в Raspberry Pi, подключить питание и HDMI и дождаться окончания загрузки.

Бонус-трек: включаем adb over USB

Когда я писал эту статью, мне никак не удавалось подключиться к Pi по adb, хотя в eng-прошивке adb всегда доступен, и подключиться по сети при помощи Ethernet я смог. Оказалось, что разработчики android-rpi выключили поддержку USB device mode по дефолту, из-за чего и компьютер, и Raspberry Pi считали, что они могут быть только USB-хостом.

Для того чтобы включить поддержку device mode, нам понадобится сделать следующее:

  1. Раскомментировать первую строчку в init.rpi4.rc. init.rc — это init-процесс в терминах Linux, то есть процесс с PID 1, запускающий все остальные процессы. В Android init помимо всего прочего отвечает за мониторвание USB-FFS и запуск adbd.

  2. Добавить сборку модуля ядра dwc2 в /common/build.config.arpi

  1. Раскомментировать две строки после комментария enable adb over usb в моём скрипте выше.

© Habrahabr.ru