Задача
С выпуском Repka Pi 4 среди доступных дистрибутивов для работы на одноплатниках проекта появилась и ОС Android. Что это за операционная система (ОС) и зачем она может понадобится, думаем объяснять не нужно. Как пример - интерактивная информационная панель или экран терминала с пользовательским интерфейсом в виде мобильного приложения - это круто и в ряде случаев очень удобно. Так что Ура!
Но что делать, если есть необходимость повернуть экран вертикально? Особенно учитывая, что на Репку 4 портирован и доступен для установки Андроид на основе TV BOX и он залочен под работу только в горизонтальной, т.е. альбомной ориентации.
Расскажем, как мы в команде проекта Репка решили эту задачу, погрузившись в данный и совершенно новый для нас дистрибутив, обновили дистрибутив на сайте проекта и заодно дадим готовую инструкцию по управлению ставшим доступными параметрами ориентации экрана.
Немного истории появления Android для Repka Pi 4 и о его особенностях #
Ранее, разрабатывая наши первые одноплатники, ещё задолго до появления проекта Репка, мы уже ставили на них Андроид для эксперимента и это была совсем старая версия, кажется четвёртая, но этим тогда дело и ограничилось и с появлением проекта Repka Pi и его первой моделью Repka Pi 3 мы не стали в это ввязываться, не было запросов и оценивая свои силы и возможности, понимали, что пока не до этого.
С выпуском Repka Pi 4 наша команда значительно продвинулись в портировании различных операционных систем - к этому моменту мы приобрели минимально необходимые компетенции в этом вопросе, улучшили процессы разработки и производства, расширили экосистему проекта. Так что решили, решили, что настало время для Android.
В отличие от привычных нам дистрибутивов Linux (а мы портировали DietPi, Debian и Kali Linux), Android представляет собой совершенно иную экосистему с собственным стеком технологий. Нам пришлось насколько это возможно погрузиться в документацию, архитектуру AOSP и особенности сборки под специфичное железо. В качестве базы мы выбрали прошивку для TV-приставки на Allwinner H6, поскольку Repka Pi 4 также построена на этом чипе. Так было проще, чтобы не заниматься установкой или сборкой и настройкой кучи драйверов, особенно для GPU, что делать с нуля под Андроид очень не хотелось. После ряда итераций нам удалось успешно портировать Android на наше устройство.
На этом этапе мы получили рабочий Android TV, но довольно быстро столкнулись с интересным кейсом от одного из клиентов: ему необходимо было использовать устройство в портретной ориентации, и он не мог этого добиться.
Если побороть такое ограничение с ориентацией, заменить лаунчер (приложение - рабочий стол в Андроид) то вот уже обычный универсальный Андроид, только на одноплатном компьютере.
Как выяснилось, смена ориентации экрана на Android TV — задача не из тривиальных. Это объясняется просто: подавляющее большинство телевизоров не поворачивают вертикально, и сам Android TV не предполагает такого сценария использования.
Как разработчики, мы предположили, что где-то в конфигурации должен быть параметр, отвечающий за ориентацию. Действительно, в build.prop
мы нашли параметр ro.default.rotation
, который позволил нам повернуть экран еще на этапе запуска системы. Но радость была недолгой — с появлением лаунчера все возвращалось обратно в ландшафт.
Двинулись дальше и начали тестировать сторонние утилиты для поворота экрана. Они частично работали: позволяли крутить экран между 0 и 180 градусами, но вот добиться поворота на 90 или 270 градусов не удавалось никак — система просто игнорировала команды.
В этот момент стало понятно, что легким способом не обойтись. Придется всерьез погрузиться в исходники AOSP, поставляемые Allwinner, и разобраться, где именно в коде Android TV жестко прописано поведение, связанное с ориентацией экрана.
Итак, погружаемся в исходный код Android от AllWinner #
Обратите внимание
Если вы также, как и мы захотите погрузиться в исходный код AOSP (Android Open Source Project) для платформы Allwinner H6, то он доступен по ссылке.
Очень быстро мы заметили, что в коде встречается интересная платформа — "homlet"
— и с ней связано несколько жёстко заданных логик. Мы нашли три ключевых файла, в которых встречается упоминание этой платформы и реализовано специфическое поведение, напрямую влияющее на ориентацию экрана.
WindowManagerService.java
— диспетчер всего, что на экране
Файл: frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
Этот класс реализует WindowManagerService — центральный компонент, отвечающий за отображение всех окон в системе: от приложений до системных оверлеев, клавиатуры и уведомлений. Всё, что рисуется на экране, проходит через WMS.
Кроме прочего, он занимается определением и обновлением ориентации экрана. Вот ключевой фрагмент:
if ("homlet".equals(SystemProperties.get("ro.product.platform", "null"))) {
req = "1".equals(SystemProperties.get("ro.sf.disablerotation","0"))
? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
: dc.getOrientation();
} else {
req = dc.getOrientation();
}
Если устройство работает на "homlet"
, и системное свойство ro.sf.disablerotation
установлено в "1"
, экран насильно переводится в ландшафтную ориентацию, независимо от того, что хочет приложение или пользователь. Именно это поведение мы и наблюдали — лаунчер или системные компоненты сбрасывали нашу ориентацию обратно, игнорируя настройки.
PackageParser.java
— установка ориентации ещё до запуска приложения
Файл: frameworks/base/core/java/android/content/pm/PackageParser.java
Этот файл отвечает за разбор манифеста APK-файла и формирование системного описания приложения. Здесь мы нашли фрагмент, в котором ориентация экрана активности подменяется на лету:
a.info.screenOrientation = "homlet".equals(SystemProperties.get("ro.product.platform", "null"))
? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
: sa.getInt(R.styleable.AndroidManifestActivity_screenOrientation,
SCREEN_ORIENTATION_UNSPECIFIED);
Даже если разработчик явно указал в AndroidManifest.xml
, что активность должна быть портретной, на платформе homlet
она всё равно будет принудительно переведена в ландшафт. Это объясняло, почему наши попытки установить нужную ориентацию не работали — решение принималось на уровне PackageParser, до запуска самой активности.
SystemServer.java
— откуда всё начинается
Файл: frameworks/base/services/java/com/android/server/SystemServer.java
Этот файл — один из центральных элементов всей Android-системы. Именно он запускается после старта процесса zygote
и отвечает за инициализацию всех ключевых системных служб, таких как ActivityManagerService
, PackageManagerService
, WindowManagerService
и многих других.
Внутри SystemServer
мы нашли ещё одну важную проверку на платформу "homlet"
:
if (SystemProperties.get("ro.product.platform").equals("homlet")) {
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new TvWindowManager());
} else {
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());
}
Эта строка определяет, какой менеджер окон будет использоваться в системе:
-
TvWindowManager
— используется на"homlet"
и предназначен для Android TV-устройств. -
PhoneWindowManager
— используется на обычных Android-устройствах (смартфонах, планшетах и т.п.).
Именно здесь принимается решение, каким будет поведение всей системы: будет ли она вести себя как телевизор (фиксированная ландшафтная ориентация, упрощенное управление, фокус на D-Pad-навигации и т.д.) или как "обычный" Android с поддержкой поворота экрана, сенсорного ввода, и полноценной работы в портретной ориентации.
На первый взгляд поворот экрана в Android — задача тривиальная. Но в случае с платформой "homlet" на базе Allwinner H6 это оказалось жестко зашитым поведением на всех уровнях системы. Именно поэтому стандартные подходы вроде изменения build.prop
или использования сторонних приложений не работают — система попросту не создана для работы в портретном режиме. Чтобы это изменить, нам пришлось влезать в код AOSP, удалять хардкод-проверки на "homlet" и собирать собственный вариант двух очень важных файлов:
framework.jar
содержит общую реализацию Android API — это классы, которые используют все приложения и системные компоненты:Context
,Activity
,View
,Window
,SystemProperties
, и так далее.services.jar
— это логика запуска и работы системных сервисов, в том числеSystemServer
,WindowManagerService
,ActivityManagerService
, и других. Именно в этом JAR-файле находится большинство изменений, связанных с поведением оконной системы и ориентацией экрана.
Чтобы эти изменения заработали, нужно было заменить оригинальные services.jar
и framework.jar
на нашей сборке. Заменили, но ориентация так и не меняется - почему?
Обращаем внимание
Мы не профессиональные разработчики Java и Android - у нас иной стек. Вся информация сказанная ниже показалась нам весьма интересной (потому что пришлось потратить время, чтобы к этому подойти), поэтому мы решили упомянуть ее в статье.
Как оказалось, Android не исполняет .jar
-файлы напрямую — вместо этого они компилируются заранее в нативный байткод формата ART (Android Runtime), чтобы система запускалась быстрее.
Для этого Android использует следующие файлы:**
-
.vdex
(Verified DEX) — содержит верифицированный DEX-код, прошедший проверку типов и ссылок; -
.art
— нативный скомпилированный байткод (аналог.so
, но для классов Java); -
.odex
— в некоторых версиях Android: отдельный оптимизированный DEX-файл (в новых сборках часто интегрируется в.vdex
/.art
).
Без пересборки vdex
и art
любые изменения в системных .jar
-файлах остаются "невидимыми" для Android. Чтобы изменения действительно заработали, нужно не просто пересобрать .jar
, но и перегенерировать байткод, который Android использует при загрузке.
Сделали и это, заменили - работает! И через параметр в build.prop
, и через приложения для смены ориентации экрана.
Способы изменить ориентацию на Android TV #
Ниже представлено 3 варианта смены ориентации экрана ОС Android на Repka Pi 4. Учитывайте, что данные варианты работают только на самой актуальной версиия нашего образа Android.
ro.default.rotation
в build.prop
Обращаем внимание
После изменения данной настройки, ориентация будет применяться на этапе загрузке, когда появится надпись Repka Pi с лоадером.
- Для этого необходимо вставить SD-карту с уже записанным образом Android в компьютер. Настоятельно рекомендуем использовать для этого операционные системы на базе Linux, т.к. разделы Android не смогут определиться на Windows.
- После чего переходим в примонтированный раздел со странным названием “_“ (полный путь:
/media/your-user/_
). - Внутри данного раздела переходим в директорию
system
(полный путь:/media/your-user/_/system
). - После чего открываем файл
build.prop
(для редактирование необходимы права суперпользователя) и ищем строкуro.default.rotation=
. Изначально данная строка пустая, но она может принимать следующие значения: 0, 90, 180, 270 в зависимости от того, какую ориентацию вы хотите использовать по умолчанию. - После изменения необходимо сохранить файл. Настройка применена!
user_rotation
через терминал
Обращаем внимание
Данная настройка актуальна в случае, если у вас есть доступ к Shell на устройстве (через UART или adb)
Обращаем внимание
После изменения данной настройки, ориентация будет применяться после загрузки лаунчера.
-
В первую очередь необходимо отключить авто-поворот экрана:
settings put system accelerometer_rotation 0
-
После чего можно изменить текущую ориентацию ()
settings put system user_rotation X
где X - может принимать значения: 0 - 0 градусов, 1 - 90 градусов, 2 - 180 градусов, 3 - 270 градусов.
Приложение Rotation
-
На уже запущенном Android переходим в список приложений (плитка “App“).
-
Перед нами открывается весь список приложений, которые у нас есть. Выбираем приложение
Rotate
. -
После запуска приложения, нам необходимо будет пройти небольшой туториал перед тем, как приступить к настройке.
-
На первом экране туториала в бираем язык приложения — “Русский“ и нажимаем “Далее“ (кнопка, расположенная справа внизу).
-
После чего пробегаемся по остатку туториала - мы настроим все в конце.
-
После чего откроется экран с первоначальной настройкой. Включаем специальные возможности.
-
Запускаем службу Rotation.
-
Устанавливаем глобальное положение. В нашем случае это портретная ориентация.
-
Как мы видим, настройка уже применилась! Включаем службу, которая будет менять настройку после запуска системы.
-
После чего нажимаем на кнопку “Завершить“.
-
После успешной установки первоначальных настроек откроется основное окно приложения. В этом окне в любое время мы можем изменить наши настройки.
-
Настройка через приложение
Rotation
завершена!
Что в итоге? #
Портирование Android на Repka Pi 4 стало для нас не просто техническим экспериментом, а полноценным исследованием архитектуры Android — особенно в контексте платформы Allwinner H6. То, что изначально казалось простым запросом "сделать портретную ориентацию", в реальности потребовало глубокого погружения в AOSP, изучения системных служб и множества нестандартных решений.
Этот опыт показал, насколько глубоко завязана логика Android на предполагаемое поведение устройства, и как важно понимать устройство системы "под капотом", если вы хотите выйти за рамки типичных сценариев. В итоге нам удалось добиться стабильной работы Android в портретной ориентации — и теперь Repka Pi 4 готова к новым, нестандартным сценариям использования.
Важно отметить, что фактически данный функционал является экспериментальным и не полностью протестированным — точнее мы уже нашли несколько нерабочих моментов (которые в скором времени исправим). Но если кто-то не хочет проходить подобный путь (с пересборкой AOSP) у нас есть готовый образ, в котором ориентация экрана уже работает. Его можно скачать по ссылке. А по этой ссылке можно ознакомиться с инструкцией, как записать Android на SD-карту и eMMC.