Set-Top-Box и опыты с Андроид в контейнере LXC

Как возникла странная необходимость запустить Android в Linux контейнере, и что из этого получилось

Предыстория


Запустить Android в контейнере LXC на мой взгляд, вполне логичное решение, если хочется иметь прозрачность и надёжность Bare Linux и использовать огромный потенциал хороших (и не очень хороших) сторонних приложений для Android. Так же данная конфигурация представляет интерес, как платформа для отладки собственного образа AOSP в условиях, максимально близких к боевым.
Для экспериментов была выбрана прогрессивная и недорогая китайская приставка на базе 64-ти битного ARMv8 от Amlogic S905x (CPU — 4 ядра, ОЗУ — 2ГБ, MMC — 8ГБ). Так же веским аргументом явилась неплохая (по сравнению с другими вендорами) кодовая база в OpenSource и наличие исходника ядерного драйвера для Mali-450. А user-space библиотеки Mali сегодня в открытом доступе на официальном сайте ARM Limited. Библиотеки доступы в бинарном виде для Linux-FB, Linux-Wayland и Android.

Главной целью экспериментов были приложения онлайн-кинотеатров и приложения для работы с сетевыми медиа-хостингами. Например с Youtube в Linux сразу начались неприятности. Во-первых: хакерский метод получения ссылок на контент, путём разбора JS-скрипта и генерации сигнатуры (ранее реализованный в minitube от Тордини и в youtube-dl) начал регулярно ломаться, вследствие беспощадной борьбы Google с методами обхода рекламы. Во-вторых: максимальное разрешение контента было 720p — больше Google-API не выдавал. В-третьих: WebKit лишился нормального сопровождения и последнее время поддерживается только небольшой группой энтузиастов. Эта же участь постигла и его Qt-порт. В итоге, в один прекрасный момент, страница youtube/tv отказалась работать, сославшись на старость web-движка. Ну и под конец приподнёс сюрприз WebEngine (Qt-Chromium). Оказывается эта прелесть не поддерживает аппаратное ускорение. Исключение сделано только для его Android-порта, и маргинальной ветки VAAPI в Linux. Тупик. В общем, я не нашёл простого способа задействовать аппаратное ускорение декодирования видео для Chromium в Linux. Реализация VAAPI для Amlogic мне показалась тяжёлой и бесполезной работой. Пощупал я и pepper plugin — к сожалению PPAPI не позволяет проигрывать offscreen видео.

Android


Почему бы не запустить Android в контейнере? На подвиг вдохновил проект Anbox. Тщательное изучение Anbox показало, что он нам не подходит. Но идея была понятной. В статьях других авторов утверждалось, что запуск Android в контейнере — задача плёвая. Но на поверку всё оказалось гораздо сложнее. Простой настройкой файлов конфигурации мы отделаться не смогли.

Итак, собираю LXC и устанавливаю его в систему. Тест конфигурации ядра выявляет проблемы: надо включить поддержку пространства имён. Так как платформа встроенная, то всякие ненужности были отключены. Пришлось выявлять эти нужные ненужности.

Первым тестом была проверка работы Busybox в контейнере. Убедившись, что все работает, я начал эксперименты.

Первоначальный вид /var/lib/lxc/abox.conf:

lxc.rootfs = /var/lib/lxc/abox/rootfs 
	lxc.rootfs.backend = dir 
	lxc.utsname = abox 
	lxc.pts = 1024 
	lxc.cap.drop = mac_admin mac_override 

Качаем испохабленный китайскими ручонками AOSP 6.0.19. От ванильной версии его отличает наличие нормального лаунчера, заточенного под дисташку и жёскто пропатченного surfacelinger с поддержкой некоторых особенностей аппаратной платформы Amlogic. Ванильный AOSP был впоследствии также протестирован.

Небольшое отступление от темы: Китайцы, адаптируя софт, плюют на все правила, установленные сообществом. Вот например ядро 3.14.29. Этот ничего не говорящий номер релиза ядра используется почти на всех железках на процессорах Amlogic S8xx и S9xx. Но, почти всегда они очень серьёзно отличаются друг от друга, вплоть до полной несовместимости старых модулей с новыми образами и наоборот. Похоже ядро правилось по принципу: «максимально быстрый выход изделия на рынок». Код не просто грязный — он отвратительного качества. Изменение конфигурации как правило приводит к ошибкам при компиляции или линковке образа или модулей. Патченый Android такого же качества, и принципы адаптации похожие. Почти все рекомендации команды AOSP проигнорированы.

Ну деваться то некуда! Собираем.

Попытка №1 Устанавливаем образ в контейнер, запускаем. Не работает. Анализ показывает, что отсутствуют объекты ядра: binder и ashmem. Дособираем модули ядра.

Попытка №2 Запускаем снова. Падает installd. Выясняется, что оригинальный binder знать не знает про namespaces. Тянем биндер из Anbox.

Попытка №3 Запускается и сразу уходит на перезагрузку. Оказывается init хочет SELinux и без него работать отказывается.

Попытка №4 Включаем SELinux. Получаем вязанку проблем для хост-системы. Пришлось отключить, во-всяком случае пока — до выяснения сути и теории процесса. SELinux можно отключить и в командной строке при загрузке ядра, но я так и не понял, как передавать параметры в контейнер. Пришлось влезть в исходник init и грубо поправить его поведение. Это было первое и последнее хирургическое вмешательство, которое аукнулось мне позже.

Попытка №5 Процесс загрузки дошёл до зиготы. В логах ругань из ядра на UID init. В биндере (и биндере из Anbox) UID процесса-владельца жёскто сравнивается с единицей. Единственный способ — отключить проверку, тем более, что в контейнере эта проверка лишена смысла.

Попытка №6 Всплыли конфликты, связанные с совместным доступом к управлению оборудованием. Комментирую в скриптах init управление USB и Bluetooth. Убираю все записи из fstab, и запрещаю в скриптах монтирование и проверку всех носителей. Теперь добавим карту монтирования в конфигурацию контейнера. Она содержит только одну строку. Каталог /mnt/lxc.data смонтирован на хосте в реальный раздел MMC.

lxc.mount.entry = /mnt/lxc.data data auto rw,bind 0 0

Попытка №7 На экране появились прыгающие шарики, загрузка длится долго, потому как образ Android смонтирован по NFS, а так же идёт генерация dexx в каталоге /data. Повторная загрузка выполняется в разы быстрее. И вот, наконец, появился лаунчер.
Будем считать это последней попыткой, потому как, в общем всё работает и надо допилить детали.

Сеть не работает, точнее работает, но некоторые приложения оценивают её работоспособность по состоянию сетевых интерефейсов. Кривые руки, одним словом. Для устранения этого недостатка на хосте поднимаем сетевой мост (bridge) и виртуальный интерфейс (veth).

lxc.network.type         = veth 
	lxc.network.flags        = up 
	lxc.network.name         = eth1 
	lxc.network.link         = br0 
	lxc.network.veth.pair    = veth-01 
	lxc.network.ipv4         = 10.0.0.10/24 
	lxc.network.ipv4.gateway = 10.0.0.1 
	lxc.network.hwaddr       = 00:FE:CD:BA:09:87

Так же надо поднять сервер DHCP, в противном случае будут проблемы с DNS. К сожалению, Android не анализирует resolv.conf и его расположение в файловой системе не играет никакой роли. Можно настроить сетевое соединение вручную, но, при удалении данных с раздела data все настройки сбросятся.

Итоги


Работают все стоковые приложения. С установленными из маркета есть проблемы. Например: Youtube.tv версии 3 потребовал обновить Google service framework, после чего система сломалась. Всплыла проблема с keystore (пока не решена). Так же временно отключен TEE, соответственно widevine не работает. Игрушки и приложения без особых требований к оборудованию работают нормально. Chrome крутит HTML5 video с программным декодером, цеплять аппаратный декодер отказывается. По этому поводу существует мнение о криво запиленном китайцами AOSP. Но ванильный AOSP запускает лаунчер с тач-скрином — дисташкой управлять невозможно.

Послесловие


В ближайшей перспективе — сделать лаунчер запуска Android приложений напрямую из Linux. Пример этому имеется в исходнике wpa-supplicant. Так же можно подглядеть, как это сделано в Anbox.

Спасибо за внимание!

Дополнение 1


На днях проверил масштабируемость приложений Qt. Изначально, клиентское приложение IPTV написано на QML для Linux. Плеер работает через QtMultimedia plugin. В процессе компиляции всплыли проблемные зависимости. К счастью, ограничилось всё QtDbus, которого нет в Android. До сих пор не могу понять, зачем для Андроида нужно было изобретать велосипед — binder. Чем разработчиков DBus не устраивал? Тем что он в user-space работает? Или лицензионные соображения?
Отключил DBus. Это было безболезненно, так как канал был нужен для небольшого функционала, связанного с операционной системой. Собрался apk. Сложностью со сборкой нет, так как использую QtCreator (и вам рекомендую).
В AOSP пришлось нарисовать Mediaplayer bridge — наследник от android::MediaPlayerInterface. В нём были реализованы методы setDataSource() и stop(). Для остальных сделаны затычки. setDataSource имеет три интерфейса. Реализовать потребовалось только:
setDataSource(const sp<IMediaHTTPService> &httpService,
                       const char *uri, 
                       const KeyedVector<String8, String8> *headers)


Если захотите крутить файлы с медиа-носителей, придётся повозиться с
setDataSource(int fd, int64_t offset, int64_t length)

Имя файла нужно будет получить через "/proc/self/fd/" + fd;


После установки, приложение заработало и пошла трансляция. Супер! Я ожидал вороха проблем, а их почти нет. Спасибо разработчикам Qt и QtCreator за огромную и полезную работу!
В итоге получил такую связку: на хосте запущен демон плеера. В контейнере — клиентская программа и прокладка, транслирующая вызовы android::Mediaplayer на хост. Связка работает через UDP-socket.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 11

    0
    простого способа задействовать аппаратное ускорение декодирования видео для Chromium в Linux

    У Amlogic есть порт хромиума под Линукс с поддержкой аппаратного декодирования. YoutubeTV работает.
      0
      Может дадите наводку? Может вы имеете ввиду WPE?
        0
        Кстати, в WPE тоже работает декодер через gstreamer.
        0
        Нашёл chrome собранный под wayland без исходников.
        список зависимостей
        [libEGL.so]
        [libwayland-client.so.0]
        [libpthread.so.0]
        [libdl.so.2]
        [libnss3.so]
        [libnssutil3.so]
        [libsmime3.so]
        [libnspr4.so]
        [libfontconfig.so.1]
        [libfreetype.so.6]
        [libexpat.so.1]
        [libpangocairo-1.0.so.0]
        [libpango-1.0.so.0]
        [libglib-2.0.so.0]
        [libcairo.so.2]
        [libxkbcommon.so.0]
        [libasound.so.2]
        [libamcodec.so]
        [libamadec.so]
        [libamavutils.so]
        [libdbus-1.so.3]
        [libstdc++.so.6]
        [libm.so.6]
        [libgcc_s.so.1]
        [libc.so.6]
        [ld-linux-aarch64.so.1]

        Даже если это оно, то не интересно. Отсутствие исходников сводит всё к нулю
          0
          Там есть и просто набор патчей поверх какой-то версии хрома.
        0
        Предположу, что поскольку вы не указали lxc.arch = armhf — ваш контейнер использует архитектуру хоста, что смущает андроид чуть более, чем полностью. Опишите хост-систему и процесс создания контейнера подробнее — глядишь, и сами найдете ошибки.
        Имхо, для таких архитектурных изысков больше подходит KVM/QEMU в чистом виде.
          0
          Совершенно верно. Ошибка существует изначально и я её уже описал: отключен функционал SELinux. Андроиду это сильно не понравилось и он отказался работать, по-началу. Отключить оказалось легче, чем разобраться. Хотелось получить быстрый результат. А эмулятор — потеря производительности. У меня в планах разобраться настройками SELinux, но, так как эта тема — хобби, то только по мере появления свободного времени
            0
            Не думаю, что производительность критически отличается, т.к. для контейнеров с отличной от хоста архитектурой все равно используется эмуляция. Иначе CPU хоста не совсем понимает что происходит. Поэтому, ставят пакет qemu-user-static и, в вашем случае:
            # lxc-create -t abox -n p3 — -a armhf, а в конфиге явно указывается lxc.arch = armhf

            Есть отличная книжка Practical LXC and LXD: Linux Containers for Virtualization and Orchestration может помочь

          0
          ИМХО — Андроид на STB — это пустая трата времени. У меня есть опыт портирования Андроида на СТБ, там дальше вылазят проблемы с тем, что сама система не адаптирована на работу с ДУ. К примеру, мы писали собственный обработчик на pintch zoom на уровне линукс дарйверов — нужно было зажать кнопку и дальше +\-. А в системе эмулировани нажатия 2мя пальцами. И так со многими вещами. Для решения изложенных в статье задач — есть смысл посмотреть в сторону GoogleTV и тд. — решений котороые изначально рассчитаны на RCU.
            0
            Пустая трата времени это просмотр шоу. Тут же интерес. Даже если я получу отрицательный результат, я его получу. Во-всяком случае, я использовал шанс, и мне не в чем себя будет упрекнуть. По сути: написать нормальное приложение, адаптированное под ПДУ — работа серьёзная. На эргономику времени уходит больше, чем на написание кода. В GoogleTV многое решено, так что я с вами частично согласен.
              0
              Вы в каком веке живете?
              ГуглТВ давно сдох. АндроидТВ имеет в базе тот же AOSP.

            Only users with full accounts can post comments. Log in, please.