На этой странице описаны изменения в драйвере binder в Android 8, приведены подробные сведения об использовании binder IPC и перечислены необходимые правила SELinux.
Изменения в драйвере Binder
Начиная с Android 8, фреймворк Android и HAL-уровни взаимодействуют друг с другом с помощью binder. Поскольку такой обмен данными значительно увеличивает трафик связующего ПО, в Android 8 реализовано несколько улучшений, которые позволяют поддерживать высокую скорость межпроцессного взаимодействия связующего ПО. Поставщики процессоров и производители оригинального оборудования должны выполнять слияние непосредственно из соответствующих веток android-4.4, android-4.9 и более поздних версий проекта kernel/common.
Несколько доменов связывания (контекстов)
Common-4.4 и более поздние версии, включая upstreamЧтобы разделить трафик Binder между фреймворком (независимым от устройства) и кодом поставщика (зависимым от устройства), в Android 8 была введена концепция контекста Binder. У каждого контекста связывателя есть собственный узел устройства и собственный менеджер контекста (сервиса). Менеджер контекста доступен только через узел устройства, к которому он принадлежит. При передаче узла связывания через определенный контекст он доступен из этого контекста только другому процессу, что обеспечивает полную изоляцию доменов друг от друга. Подробнее об использовании vndbinder и vndservicemanager…
Scatter-gather
Common-4.4 и более поздние версии, включая upstreamВ предыдущих версиях Android каждый фрагмент данных в вызове Binder копировался три раза:
- Один раз для сериализации в
Parcelв вызывающем процессе. - После того как драйвер ядра скопирует
Parcelв целевой процесс, - Один раз для десериализации
Parcelв целевом процессе.
В Android 8 используется оптимизация scatter-gather, которая позволяет сократить количество копий с трех до одной. Вместо того чтобы сначала сериализовать данные в Parcel, они остаются в исходной структуре и макете памяти, а драйвер сразу копирует их в целевой процесс. После того как данные будут перенесены в целевой процесс, их структура и размещение в памяти будут одинаковыми, и их можно будет считывать без необходимости создавать ещё одну копию.
Точная блокировка
Common-4.4 и более поздние версии, включая upstreamВ предыдущих версиях Android драйвер binder использовал глобальную блокировку для защиты от одновременного доступа к критически важным структурам данных. Хотя конкуренция за блокировку была минимальной, основная проблема заключалась в том, что если поток с низким приоритетом получал блокировку, а затем прерывался, это могло серьезно задержать потоки с более высоким приоритетом, которым требовалась та же блокировка. Это приводило к сбоям в работе платформы.
Первые попытки решить эту проблему включали отключение вытеснения при удержании глобальной блокировки. Однако это было скорее временное решение, чем полноценное исправление, поэтому оно было отклонено и удалено. В последующих попытках мы сосредоточились на более детальной блокировке, и одна из таких версий работает на устройствах Pixel с января 2017 года. Большинство этих изменений были опубликованы, но значительные улучшения были внесены в последующих версиях.
После того как мы выявили небольшие проблемы в реализации точной блокировки, мы разработали улучшенное решение с другой архитектурой блокировки и отправили изменения во все распространенные ветви ядра. Мы продолжаем тестировать эту реализацию на большом количестве разных устройств. Поскольку нам неизвестно о каких-либо нерешенных проблемах, мы рекомендуем использовать эту реализацию на устройствах с Android 8.
Наследование приоритета в реальном времени
Common-4.4 и common-4.9 (в ближайшее время будет добавлена поддержка более новых версий)Драйвер Binder всегда поддерживал наследование приоритета nice. Поскольку в Android все больше процессов выполняются с приоритетом реального времени, в некоторых случаях имеет смысл, чтобы поток в процессе, который обрабатывает вызов Binder, также выполнялся с приоритетом реального времени. Чтобы поддерживать эти варианты использования, в Android 8 теперь реализовано наследование приоритетов в реальном времени в драйвере связывателя.
Помимо наследования приоритета на уровне транзакции, наследование приоритета узла позволяет узлу (объекту службы Binder) указать минимальный приоритет, с которым должны выполняться вызовы в этот узел. В предыдущих версиях Android уже поддерживалось наследование приоритета узла с помощью значений nice, но в Android 8 добавлена поддержка наследования узла для правил планирования в реальном времени.
Изменения в пространстве пользователя
Android 8 включает все изменения пользовательского пространства, необходимые для работы с текущим драйвером Binder в общем ядре, за исключением одного: в исходной реализации для отключения наследования приоритета в реальном времени для /dev/binder использовался ioctl. В последующих версиях управление наследованием приоритета было перенесено на более детальный метод, который применяется к каждому режиму связывания (а не к каждому контексту). Таким образом, ioctl отсутствует в общей ветке Android, но представлен в наших общих ядрах.
В результате этого изменения наследование приоритета в реальном времени по умолчанию отключено для каждого узла. Команда разработчиков Android обнаружила, что включение наследования приоритета в реальном времени для всех узлов в домене hwbinder дает положительный эффект. Чтобы добиться того же эффекта, выберите это изменение в пространстве пользователя.
SHA для распространенных ядер
Чтобы получить необходимые изменения драйвера связки, синхронизируйтесь с подходящим SHA:
- Common-3.18
cc8b90c121de ANDROID: binder: don't check prio permissions on restore. - Common-4.4
76b376eac7a2 ANDROID: binder: don't check prio permissions on restore. - Common-4.9
ecd972d4f9b5 ANDROID: binder: don't check prio permissions on restore.
Как работать с межпроцессным взаимодействием Binder
Исторически для взаимодействия между процессами поставщиков использовался механизм Binder IPC. В Android 8 узел устройства /dev/binder становится эксклюзивным для процессов фреймворка, то есть процессы поставщика больше не имеют к нему доступа. Процессы поставщика могут получить доступ к /dev/hwbinder, но должны преобразовать свои интерфейсы AIDL для использования HIDL. Для поставщиков, которые хотят продолжать использовать интерфейсы AIDL между процессами поставщика, Android поддерживает IPC Binder, как описано ниже. В Android 10 Stable AIDL позволяет всем процессам использовать /dev/binder, а также обеспечивает стабильность, которую гарантировали HIDL и /dev/hwbinder. Информацию о том, как использовать Stable AIDL, можно найти в разделе AIDL для HAL.
vndbinder
В Android 8 поддерживается новый домен связывания для использования поставщиками сервисов. Доступ к нему осуществляется с помощью /dev/vndbinder вместо /dev/binder. С добавлением /dev/vndbinder в Android теперь есть три домена IPC:
| Домен IPC | Описание |
|---|---|
/dev/binder |
Межпроцессное взаимодействие между процессами фреймворка и приложения с интерфейсами AIDL. |
/dev/hwbinder |
IPC между процессами фреймворка и поставщика с интерфейсами HIDL.
IPC между процессами поставщика с интерфейсами HIDL. |
/dev/vndbinder |
Межпроцессное взаимодействие между процессами поставщиков с помощью интерфейсов AIDL |
Чтобы символ /dev/vndbinder отображался, убедитесь, что для элемента конфигурации ядра CONFIG_ANDROID_BINDER_DEVICES задано значение "binder,hwbinder,vndbinder" (это значение по умолчанию в общих деревьях ядра Android).
Обычно процессы поставщика не открывают драйвер binder напрямую, а связываются с библиотекой пользовательского пространства libbinder, которая открывает драйвер binder. Добавление метода для ::android::ProcessState()
выбирает драйвер binder для libbinder. Процессы поставщика должны вызывать этот метод до вызова ProcessState,
IPCThreadState или любого другого вызова связывания. Чтобы использовать этот метод, разместите следующий вызов после main() процесса поставщика (клиента и сервера):
ProcessState::initWithDriver("/dev/vndbinder");
vndservicemanager
Ранее сервисы Binder регистрировались с помощью servicemanager, и другие процессы могли их извлекать. В Android 8
servicemanager теперь используется исключительно процессами фреймворка и приложений, а процессы поставщика больше не могут получить к нему доступ.
Однако теперь сервисы поставщиков могут использовать vndservicemanager – новый экземпляр servicemanager, в котором вместо /dev/binder используется /dev/vndbinder и который создан на основе тех же источников, что и фреймворк servicemanager. Процессам поставщика не нужно вносить изменения, чтобы взаимодействовать с vndservicemanager. Когда процесс поставщика открывает /dev/vndbinder, поиск сервисов автоматически переходит к vndservicemanager.
Двоичный файл vndservicemanager включен в файлы makefile устройств Android по умолчанию.
Правила SELinux
Процессам поставщиков, которые хотят использовать функции связывания для взаимодействия друг с другом, требуется следующее:
- Доступ к
/dev/vndbinder. - Брошюратор
{transfer, call}зацепляется заvndservicemanager. binder_call(A, B)для любого домена поставщика А, который хочет вызвать домен поставщика Б через интерфейс связывания поставщика.- Разрешение на использование сервисов
{add, find}вvndservicemanager.
Чтобы выполнить требования 1 и 2, используйте макрос vndbinder_use():
vndbinder_use(some_vendor_process_domain);
Чтобы выполнить требование 3, binder_call(A, B) для процессов A и B поставщика, которым необходимо взаимодействовать через binder, можно оставить на месте и не переименовывать.
Чтобы выполнить требование 4, необходимо изменить способ обработки названий сервисов, ярлыков сервисов и правил.
Подробнее о SELinux в Android можно узнать в документации для разработчиков. Подробнее о SELinux в Android 8.0 рассказывается в статье SELinux для Android 8.0.
Названия сервисов
Ранее процессы поставщиков регистрировали названия сервисов в файле service_contexts и добавляли соответствующие правила доступа к этому файлу. Пример файла service_contexts из device/google/marlin/sepolicy:
AtCmdFwd u:object_r:atfwd_service:s0 cneservice u:object_r:cne_service:s0 qti.ims.connectionmanagerservice u:object_r:imscm_service:s0 rcs u:object_r:radio_service:s0 uce u:object_r:uce_service:s0 vendor.qcom.PeripheralManager u:object_r:per_mgr_service:s0
В Android 8 vndservicemanager загружает файл vndservice_contexts. Сервисы поставщиков, которые переносятся в vndservicemanager (и уже есть в старом файле service_contexts), нужно добавить в новый файл vndservice_contexts.
Ярлыки сервисов
Ранее ярлыки сервисов, например u:object_r:atfwd_service:s0, определялись в файле service.te. Пример:
type atfwd_service, service_manager_type;
В Android 8 необходимо изменить тип на vndservice_manager_type и переместить правило в файл vndservice.te. Пример:
type atfwd_service, vndservice_manager_type;
правила servicemanager
Ранее правила предоставляли доменам доступ к добавлению или поиску сервисов из
servicemanager. Пример:
allow atfwd atfwd_service:service_manager find; allow some_vendor_app atfwd_service:service_manager add;
В Android 8 такие правила могут оставаться в силе и использовать тот же класс. Пример:
allow atfwd atfwd_service:service_manager find; allow some_vendor_app atfwd_service:service_manager add;