Развлечение на выходные: собираем Android для Raspberry Pi из исходного кода
Привет, Хабр! В этой статье я хочу поделиться пошаговой инструкцией по компиляции 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
содержит образ ядра Linuxsystem
содержит 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, нам понадобится сделать следующее:
Раскомментировать первую строчку в init.rpi4.rc.
init.rc
— это init-процесс в терминах Linux, то есть процесс с PID 1, запускающий все остальные процессы. В Android init помимо всего прочего отвечает за мониторвание USB-FFS и запуск adbd.Добавить сборку модуля ядра
dwc2
в/common/build.config.arpi
Раскомментировать две строки после комментария
enable adb over usb
в моём скрипте выше.