AIDL для HAL

В Android 11 появилась возможность использовать AIDL для HAL в Android, что позволяет реализовать части Android без HIDL. Переходите HAL на использование исключительно AIDL, где это возможно (когда восходящие HAL используют HIDL, необходимо использовать HIDL).

HAL, использующие AIDL для связи между компонентами платформы, например, в system.img , и аппаратными компонентами, например, vendor.img , должны использовать стабильный AIDL. Однако для связи внутри раздела, например, от одного HAL к другому, нет никаких ограничений на использование механизма IPC.

Мотивация

AIDL существует дольше, чем HIDL, и используется во многих других местах, например, между компонентами платформы Android или в приложениях. Теперь, когда AIDL имеет поддержку стабильности, можно реализовать весь стек с помощью одной среды выполнения IPC. AIDL также имеет лучшую систему управления версиями, чем HIDL. Вот некоторые преимущества AIDL:

  • Использование одного языка IPC означает, что нужно изучать, отлаживать, оптимизировать и защищать только одну вещь.
  • AIDL поддерживает управление версиями на месте для владельцев интерфейса:
    • Владельцы могут добавлять методы в конец интерфейсов или поля в объекты. Это означает, что с годами легче обновлять код, а также годовые затраты меньше (типы можно изменять на месте, и нет необходимости в дополнительных библиотеках для каждой версии интерфейса).
    • Интерфейсы расширений можно подключать во время выполнения, а не в системе типов, поэтому нет необходимости перебазировать последующие расширения на более новые версии интерфейсов.
  • Существующий интерфейс AIDL может использоваться напрямую, когда его владелец решит его стабилизировать. Раньше в HIDL приходилось создавать полную копию интерфейса.

Сборка на основе среды выполнения AIDL

AIDL имеет три разных бэкэнда: Java, NDK и CPP. Чтобы использовать стабильный AIDL, всегда используйте системную копию libbinder по адресу system/lib*/libbinder.so и общайтесь по адресу /dev/binder . Для кода в образе vendor это означает, что libbinder (из VNDK) использовать нельзя: эта библиотека имеет нестабильный C++ API и нестабильные внутренние компоненты. Вместо этого собственный код поставщика должен использовать серверную часть NDK AIDL, ссылаться на libbinder_ndk (который поддерживается системой libbinder.so ) и связываться с библиотеками NDK, созданными записями aidl_interface . Точные имена модулей см. в разделе Правила именования модулей .

Напишите интерфейс AIDL HAL.

Чтобы интерфейс AIDL мог использоваться между системой и поставщиком, в интерфейс необходимо внести два изменения:

  • Каждое определение типа должно быть аннотировано @VintfStability .
  • Объявление aidl_interface должно включать stability: "vintf", .

Эти изменения может вносить только владелец интерфейса.

Когда вы вносите эти изменения, для работы интерфейс должен присутствовать в манифесте VINTF . Проверьте это (и связанные с ним требования, например проверку того, что выпущенные интерфейсы заморожены) с помощью теста VTS vts_treble_vintf_vendor_test . Вы можете использовать интерфейс @VintfStability без этих требований, вызвав AIBinder_forceDowngradeToLocalStability в серверной части NDK, android::Stability::forceDowngradeToLocalStability в внутренней части C++ или android.os.Binder#forceDowngradeToSystemStability в внутренней части Java для объекта привязки перед его отправкой в ​​другой процесс.

Кроме того, для максимальной переносимости кода и во избежание потенциальных проблем, таких как ненужные дополнительные библиотеки, отключите серверную часть CPP.

Обратите внимание, что использование backends в следующем примере кода является правильным, поскольку имеется три серверных части (Java, NDK и CPP). Код показывает, как отключить серверную часть CPP:

    aidl_interface: {
        ...
        backends: {
            cpp: {
                enabled: false,
            },
        },
    }

Найдите интерфейсы AIDL HAL

Стабильные AOSP интерфейсы AIDL для HALS находятся в папках aidl в тех же базовых каталогах, что и интерфейсы HIDL:

  • hardware/interfaces предназначены для интерфейсов, обычно предоставляемых аппаратным обеспечением.
  • frameworks/hardware/interfaces предназначен для высокоуровневых интерфейсов, предоставляемых аппаратному обеспечению.
  • system/hardware/interfaces предназначен для низкоуровневых интерфейсов, предоставляемых аппаратному обеспечению.

Поместите интерфейсы расширения в другие подкаталоги hardware/interfaces в vendor или hardware .

Интерфейсы расширения

В каждом выпуске Android имеется набор официальных интерфейсов AOSP. Когда партнеры Android хотят добавить возможности к этим интерфейсам, им не следует изменять их напрямую, поскольку это делает их среду выполнения Android несовместимой со средой выполнения AOSP Android. Не изменяйте эти интерфейсы, чтобы образ GSI мог продолжать работать.

Расширения могут регистрироваться двумя разными способами:

Однако расширение зарегистрировано, когда компоненты, специфичные для конкретного поставщика (то есть не являющиеся частью вышестоящего AOSP), используют интерфейс, конфликты слияния невозможны. Однако при внесении последующих изменений в вышестоящие компоненты AOSP могут возникнуть конфликты слияния, и рекомендуются следующие стратегии:

  • Дополнения к интерфейсу AOSP в следующем выпуске.
  • Дополнения к восходящему интерфейсу, обеспечивающие дополнительную гибкость (без конфликтов слияния) в следующем выпуске.

Расширение посылки: ParcelableHolder

ParcelableHolder — это экземпляр интерфейса Parcelable , который может содержать другой экземпляр Parcelable .

Основной вариант использования ParcelableHolder — сделать Parcelable расширяемым. Например, представьте, что разработчики устройств ожидают возможности расширить Parcelable , определенный AOSP, AospDefinedParcelable , включив в него дополнительные функции.

Используйте интерфейс ParcelableHolder , чтобы расширить Parcelable дополнительными функциями. Интерфейс ParcelableHolder содержит экземпляр Parcelable . Если вы попытаетесь добавить поля в Parcelable напрямую, это вызовет ошибку:

parcelable AospDefinedParcelable {
  int a;
  String b;
  String x; // ERROR: added by a device implementer
  int[] y; // added by a device implementer
}

Как видно из предыдущего кода, эта практика нарушена, поскольку поля, добавленные разработчиком устройства, могут иметь конфликт при пересмотре Parcelable в следующих выпусках Android.

Используя ParcelableHolder , владелец Parcelable может определить точку расширения в экземпляре Parcelable :

parcelable AospDefinedParcelable {
  int a;
  String b;
  ParcelableHolder extension;
}

Затем разработчики устройств могут определить свой собственный экземпляр Parcelable для своего расширения:

parcelable OemDefinedParcelable {
  String x;
  int[] y;
}

Новый экземпляр Parcelable можно прикрепить к исходному Parcelable с помощью поля ParcelableHolder :


// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;

ap.extension.setParcelable(op);

...

OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);

// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();

ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);

...

std::shared_ptr<OemDefinedParcelable> op_ptr;

ap.extension.getParcelable(&op_ptr);

// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);

...

std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);

// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });

ap.extension.set_parcelable(Rc::clone(&op));

...

let op = ap.extension.get_parcelable::<OemDefinedParcelable>();

Имена экземпляров сервера AIDL HAL

По соглашению службы AIDL HAL имеют имя экземпляра в формате $package.$type/$instance . Например, экземпляр вибратора HAL зарегистрирован как android.hardware.vibrator.IVibrator/default .

Напишите сервер AIDL HAL.

@VintfStability Серверы AIDL должны быть объявлены в манифесте VINTF, например:

    <hal format="aidl">
        <name>android.hardware.vibrator</name>
        <version>1</version>
        <fqname>IVibrator/default</fqname>
    </hal>

В противном случае им следует зарегистрировать службу AIDL обычным способом. При запуске тестов VTS ожидается, что все объявленные AIDL HAL доступны.

Напишите клиент AIDL

Клиенты AIDL должны заявить о себе в матрице совместимости, например:

    <hal format="aidl" optional="true">
        <name>android.hardware.vibrator</name>
        <version>1-2</version>
        <interface>
            <name>IVibrator</name>
            <instance>default</instance>
        </interface>
    </hal>

Преобразование существующего HAL из HIDL в AIDL

Используйте инструмент hidl2aidl для преобразования интерфейса HIDL в AIDL.

Возможности hidl2aidl :

  • Создайте файлы AIDL ( .aidl ) на основе файлов HAL ( .hal ) для данного пакета.
  • Создайте правила сборки для вновь созданного пакета AIDL со всеми включенными серверными модулями.
  • Создайте методы перевода в серверных модулях Java, CPP и NDK для перевода из типов HIDL в типы AIDL.
  • Создайте правила сборки для библиотек перевода с необходимыми зависимостями.
  • Создайте статические утверждения, чтобы гарантировать, что перечислители HIDL и AIDL имеют одинаковые значения в серверных модулях CPP и NDK.

Выполните следующие шаги, чтобы преобразовать пакет файлов HAL в файлы AIDL:

  1. Создайте инструмент, расположенный в system/tools/hidl/hidl2aidl .

    Создание этого инструмента из новейшего источника обеспечивает наиболее полный опыт. Вы можете использовать последнюю версию для преобразования интерфейсов в старых ветках из предыдущих выпусков:

    m hidl2aidl
  2. Запустите инструмент, указав выходной каталог, за которым следует конвертируемый пакет.

    При необходимости используйте аргумент -l , чтобы добавить содержимое нового файла лицензии вверху всех созданных файлов. Обязательно используйте правильную лицензию и дату:

    hidl2aidl -o <output directory> -l <file with license> <package>

    Например:

    hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
  3. Прочтите сгенерированные файлы и исправьте любые проблемы с преобразованием:

    • conversion.log содержит все необработанные проблемы, которые необходимо устранить в первую очередь.
    • Созданные файлы AIDL могут содержать предупреждения и предложения, требующие действий. Эти комментарии начинаются с // .
    • Очистите и внесите улучшения в пакет.
    • Проверьте аннотацию @JavaDerive на наличие функций, которые могут потребоваться, например toString или equals .
  4. Создавайте только те цели, которые вам нужны:

    • Отключите серверные части, которые не будут использоваться. Предпочитайте серверную часть NDK серверной части CPP; см. Сборка для среды выполнения AIDL .
    • Удалите библиотеки перевода или любой их сгенерированный код, который не будет использоваться.
  5. См. Основные различия между AIDL и HIDL :

    • Использование встроенного Status и исключений AIDL обычно улучшает интерфейс и устраняет необходимость в другом типе статуса, специфичном для интерфейса.
    • Аргументы интерфейса AIDL в методах по умолчанию не @nullable как в HIDL.

SEPolicy для AIDL HAL

Тип службы AIDL, видимый коду поставщика, должен иметь атрибут hal_service_type . В остальном конфигурация sepolicy такая же, как и у любой другой службы AIDL (хотя для HAL существуют специальные атрибуты). Вот пример определения контекста службы HAL:

    type hal_foo_service, service_manager_type, hal_service_type;

Для большинства сервисов, определенных платформой, контекст сервиса с правильным типом уже добавлен (например, android.hardware.foo.IFoo/default уже помечен как hal_foo_service ). Однако если клиент платформы поддерживает несколько имен экземпляров, дополнительные имена экземпляров необходимо добавить в файлы service_contexts для конкретного устройства:

    android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0

При создании нового типа HAL необходимо добавить атрибуты HAL. Конкретный атрибут HAL может быть связан с несколькими типами служб (каждый из которых может иметь несколько экземпляров, как только что обсуждалось). Для HAL foo существует hal_attribute(foo) . Этот макрос определяет атрибуты hal_foo_client и hal_foo_server . Для данного домена макросы hal_client_domain и hal_server_domain связывают домен с данным атрибутом HAL. Например, системный сервер, являющийся клиентом этого HAL, соответствует политике hal_client_domain(system_server, hal_foo) . Сервер HAL аналогичным образом включает hal_server_domain(my_hal_domain, hal_foo) .

Обычно для данного атрибута HAL также создается домен, например hal_foo_default , для справки или примеров HAL. Однако некоторые устройства используют эти домены для своих собственных серверов. Различие между доменами для нескольких серверов имеет значение только в том случае, если существует несколько серверов, которые обслуживают один и тот же интерфейс и требуют разных наборов разрешений в своих реализациях. Во всех этих макросах hal_foo не является объектом сеполитики. Вместо этого этот токен используется этими макросами для ссылки на группу атрибутов, связанных с парой клиент-сервер.

Однако до сих пор hal_foo_service и hal_foo (пара атрибутов из hal_attribute(foo) ) не связаны. Атрибут HAL связан со службами AIDL HAL с помощью макроса hal_attribute_service (HAL HIDL использует макрос hal_attribute_hwservice ), например, hal_attribute_service(hal_foo, hal_foo_service) . Это означает, что процессы hal_foo_client могут получить доступ к HAL, а процессы hal_foo_server могут зарегистрировать HAL. Применение этих правил регистрации осуществляется менеджером контекста ( servicemanager ).

Имена служб могут не всегда соответствовать атрибутам HAL, например, hal_attribute_service(hal_foo, hal_foo2_service) . В общем, поскольку это подразумевает, что службы всегда используются вместе, вы можете удалить hal_foo2_service и использовать hal_foo_service для всех контекстов служб. Когда HAL устанавливает несколько экземпляров hal_attribute_service , это происходит потому, что исходное имя атрибута HAL недостаточно общее и не может быть изменено.

Объединив все это, пример HAL выглядит следующим образом:

    public/attributes:
    // define hal_foo, hal_foo_client, hal_foo_server
    hal_attribute(foo)

    public/service.te
    // define hal_foo_service
    type hal_foo_service, hal_service_type, protected_service, service_manager_type

    public/hal_foo.te:
    // allow binder connection from client to server
    binder_call(hal_foo_client, hal_foo_server)
    // allow client to find the service, allow server to register the service
    hal_attribute_service(hal_foo, hal_foo_service)
    // allow binder communication from server to service_manager
    binder_use(hal_foo_server)

    private/service_contexts:
    // bind an AIDL service name to the selinux type
    android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0

    private/<some_domain>.te:
    // let this domain use the hal service
    binder_use(some_domain)
    hal_client_domain(some_domain, hal_foo)

    vendor/<some_hal_server_domain>.te
    // let this domain serve the hal service
    hal_server_domain(some_hal_server_domain, hal_foo)

Присоединенные интерфейсы расширения

Расширение можно прикрепить к любому интерфейсу связывания, будь то интерфейс верхнего уровня, зарегистрированный непосредственно в диспетчере служб, или подинтерфейс. При получении расширения вы должны подтвердить, что тип расширения соответствует ожидаемому. Вы можете устанавливать расширения только из процесса, обслуживающего связующее.

Используйте прикрепленные расширения всякий раз, когда расширение изменяет функциональность существующего HAL. Когда необходимы совершенно новые возможности, этот механизм не нужен, и вы можете зарегистрировать интерфейс расширения напрямую у менеджера служб. Присоединенные интерфейсы расширения имеют наибольший смысл, когда они присоединены к подинтерфейсам, поскольку эти иерархии могут быть глубокими или состоять из нескольких экземпляров. Использование глобального расширения для отражения иерархии интерфейса связывания другой службы требует тщательного учета, чтобы обеспечить эквивалентные возможности напрямую подключенным расширениям.

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

  • Серверная часть NDK: AIBinder_setExtension
  • Серверная часть Java: android.os.Binder.setExtension
  • Серверная часть CPP: android::Binder::setExtension
  • Серверная часть Rust: binder::Binder::set_extension

Чтобы получить расширение для подшивки, используйте следующие API:

  • Серверная часть NDK: AIBinder_getExtension
  • Серверная часть Java: android.os.IBinder.getExtension
  • Серверная часть CPP: android::IBinder::getExtension
  • Серверная часть Rust: binder::Binder::get_extension

Дополнительную информацию об этих API можно найти в документации функции getExtension в соответствующем бэкэнде. Пример использования расширений находится в файле hardware/interfaces/tests/extension/vibrator .

Основные различия между AIDL и HIDL

При использовании AIDL HAL или интерфейсов AIDL HAL помните о различиях по сравнению с написанием HIDL HAL.

  • Синтаксис языка AIDL ближе к Java. Синтаксис HIDL аналогичен синтаксису C++.
  • Все интерфейсы AIDL имеют встроенные статусы ошибок. Вместо создания пользовательских типов состояния создайте целые числа постоянного состояния в файлах интерфейса и используйте EX_SERVICE_SPECIFIC в серверных модулях CPP и NDK и ServiceSpecificException в серверной части Java. См. Обработка ошибок .
  • AIDL не запускает пулы потоков автоматически при отправке объектов связывания. Вы должны запустить их вручную (см. Управление потоками ).
  • AIDL не прерывается при непроверенных ошибках транспорта (HIDL Return прерывается при непроверенных ошибках).
  • AIDL может объявлять только один тип в файле.
  • Аргументы AIDL могут быть указаны как in , out или inout в дополнение к выходному параметру (синхронные обратные вызовы отсутствуют).
  • AIDL использует fd в качестве примитивного типа вместо handle .
  • HIDL использует основные версии для несовместимых изменений и второстепенные версии для совместимых изменений. В AIDL обратно совместимые изменения выполняются на месте. AIDL не имеет четкого понятия основных версий; вместо этого это включается в имена пакетов. Например, AIDL может использовать имя пакета bluetooth2 .
  • AIDL по умолчанию не наследует приоритет реального времени. Функцию setInheritRt необходимо использовать для каждого связывателя, чтобы включить наследование приоритета в реальном времени.

Тесты Vendor Test Suite (VTS) для HAL

Android использует Vendor Test Suite (VTS) для проверки ожидаемых реализаций HAL. VTS помогает обеспечить обратную совместимость Android со старыми реализациями поставщиков. Реализации, не работающие с VTS, имеют известные проблемы совместимости, которые могут помешать им работать с будущими версиями ОС.

Существует две основные части VTS для HAL.

1. Убедитесь, что HAL на устройстве известны и ожидаются Android.

Этот набор тестов можно найти в test/vts-testcase/hal/treble/vintf . Эти тесты отвечают за проверку:

  • Каждый интерфейс @VintfStability , объявленный в манифесте VINTF, фиксируется на известной выпущенной версии. Это гарантирует, что обе стороны интерфейса согласуют точное определение этой версии интерфейса. Это необходимо для базовой работы.
  • Все HAL, объявленные в манифесте VINTF, доступны на этом устройстве. Любой клиент, обладающий достаточными разрешениями на использование объявленной службы HAL, должен иметь возможность получать и использовать эти службы в любое время.
  • Все HAL, объявленные в манифесте VINTF, обслуживают версию интерфейса, объявленную в манифесте.
  • На устройстве не обслуживаются устаревшие HAL. Android прекращает поддержку более ранних версий интерфейсов HAL, как описано в жизненном цикле FCM .
  • На устройстве присутствуют необходимые HAL. Для правильной работы Android необходимы некоторые HAL.

2. Проверьте ожидаемое поведение каждого HAL.

Каждый интерфейс HAL имеет свои собственные тесты VTS для проверки ожидаемого поведения клиентов. Тестовые случаи выполняются для каждого экземпляра объявленного интерфейса HAL и обеспечивают определенное поведение в зависимости от реализованной версии интерфейса.

Эти тесты пытаются охватить все аспекты реализации HAL, на которые опирается платформа Android или которые могут использоваться в будущем.

Эти тесты включают проверку поддержки функций, обработку ошибок и любое другое поведение, которое клиент может ожидать от службы.

Вехи VTS для развития HAL

Ожидается, что тесты VTS будут обновляться при создании или изменении интерфейсов HAL Android.

Тесты VTS должны быть завершены и готовы к проверке реализаций поставщиков, прежде чем они будут заморожены для выпусков Android Vendor API. Они должны быть готовы до того, как интерфейсы будут заморожены, чтобы разработчики могли создавать свои реализации, проверять их и предоставлять отзывы разработчикам интерфейса HAL.

СУДС на каракатицах

Когда оборудование недоступно, Android использует Cuttlefish в качестве средства разработки интерфейсов HAL. Это позволяет масштабировать VTS и интеграционное тестирование Android.

hal_implementation_test проверяет, что у Cuttlefish есть реализации последних версий интерфейса HAL, чтобы убедиться, что Android готов обрабатывать новые интерфейсы, а тесты VTS готовы протестировать реализации новых поставщиков, как только станут доступны новые аппаратные средства и устройства.