Стабильный AIDL

В Android 10 добавлена ​​поддержка стабильного языка определения интерфейса Android (AIDL) — нового способа отслеживания интерфейса прикладных программ (API) и двоичного интерфейса приложений (ABI), предоставляемых интерфейсами AIDL. Стабильный AIDL работает точно так же, как AIDL, но система сборки отслеживает совместимость интерфейсов, и существуют ограничения на ваши действия:

  • Интерфейсы определяются в системе сборки с помощью aidl_interfaces .
  • Интерфейсы могут содержать только структурированные данные. Parcelables, представляющие предпочтительные типы, автоматически создаются на основе их определения AIDL и автоматически маршаллируются и демаршаллируются.
  • Интерфейсы могут быть объявлены стабильными (обратно совместимыми). В этом случае их API отслеживается и версионируется в файле рядом с интерфейсом AIDL.

Структурированный и стабильный AIDL

Структурированный AIDL относится к типам, определённым исключительно в AIDL. Например, объявление parcelable (пользовательский parcelable) не является структурированным AIDL. Parcelable, поля которых определены в AIDL, называются структурированными parcelable .

Для стабильного AIDL требуется структурированный AIDL, чтобы система сборки и компилятор могли определить, являются ли изменения, внесённые в parcelable, обратно совместимыми. Однако не все структурированные интерфейсы стабильны. Чтобы быть стабильным, интерфейс должен использовать только структурированные типы, а также следующие функции управления версиями. И наоборот, интерфейс нестабилен, если для его сборки используется основная система сборки или установлено unstable:true .

Определить интерфейс AIDL

Определение aidl_interface выглядит так:

aidl_interface {
    name: "my-aidl",
    srcs: ["srcs/aidl/**/*.aidl"],
    local_include_dir: "srcs/aidl",
    imports: ["other-aidl"],
    versions_with_info: [
        {
            version: "1",
            imports: ["other-aidl-V1"],
        },
        {
            version: "2",
            imports: ["other-aidl-V3"],
        }
    ],
    stability: "vintf",
    backend: {
        java: {
            enabled: true,
            platform_apis: true,
        },
        cpp: {
            enabled: true,
        },
        ndk: {
            enabled: true,
        },
        rust: {
            enabled: true,
        },
    },

}
  • name : Имя модуля интерфейса AIDL, которое однозначно идентифицирует интерфейс AIDL.
  • srcs : список исходных файлов AIDL, составляющих интерфейс. Путь к типу AIDL Foo , определённому в пакете com.acme должен быть следующим: <base_path>/com/acme/Foo.aidl , где <base_path> может быть любым каталогом, связанным с каталогом, где находится Android.bp . В предыдущем примере <base_path> — это srcs/aidl .
  • local_include_dir : Путь, с которого начинается имя пакета. Соответствует <base_path> , описанному выше.
  • imports : список модулей aidl_interface , которые он использует. Если один из ваших интерфейсов AIDL использует интерфейс или пакет из другого aidl_interface , укажите его имя здесь. Это может быть само имя для обозначения последней версии или имя с суффиксом версии (например, -V1 ) для обозначения конкретной версии. Указание версии поддерживается с Android 12.
  • versions : предыдущие версии интерфейса, замороженные в api_dir . Начиная с Android 11, versions заморожены в aidl_api/ name . Если замороженных версий интерфейса нет, это поле указывать не нужно, и проверки совместимости не будут выполняться. Для Android 13 и более поздних версий это поле заменено на versions_with_info .
  • versions_with_info : список кортежей, каждый из которых содержит имя замороженной версии и список импортов версий других модулей aidl_interface, которые импортировала эта версия aidl_interface. Определение версии V интерфейса AIDL IFACE находится в aidl_api/ IFACE / V . Это поле было введено в Android 13 и не должно изменяться напрямую в Android.bp . Поле добавляется или обновляется вызовом *-update-api или *-freeze-api . Кроме того, поля versions автоматически переносятся в versions_with_info при вызове пользователем *-update-api или *-freeze-api .
  • stability : необязательный флаг для обещания стабильности данного интерфейса. Поддерживается только "vintf" . Если stability не установлен, система сборки проверяет обратную совместимость интерфейса, если только не указано unstable . Отсутствие флага соответствует интерфейсу со стабильностью в данном контексте компиляции (то есть либо все системные компоненты, например, компоненты в system.img и связанных разделах, либо все компоненты поставщика, например, компоненты в vendor.img и связанных разделах). Если stability установлен в значение "vintf" , это соответствует обещанию стабильности: интерфейс должен оставаться стабильным на протяжении всего времени использования.
  • gen_trace : необязательный флаг для включения или выключения трассировки. Начиная с Android 14, для бэкендов cpp и java значение по умолчанию равно true .
  • host_supported : необязательный флаг, который при установке значения true делает сгенерированные библиотеки доступными для среды хоста.
  • unstable : необязательный флаг, используемый для обозначения того, что этот интерфейс не обязательно должен быть стабильным. Если установлено значение true , система сборки не создаёт дамп API для интерфейса и не требует его обновления.
  • frozen : необязательный флаг, значение которого true означает, что интерфейс не изменился с момента предыдущей версии. Это позволяет проводить больше проверок во время сборки. Значение false означает, что интерфейс находится в разработке и в него внесены изменения, поэтому запуск foo-freeze-api создаст новую версию и автоматически изменит значение на true . Появилось в Android 14.
  • backend.<type>.enabled : эти флаги включают/выключают каждый из бэкендов, для которого компилятор AIDL генерирует код. Поддерживаются четыре бэкенда: Java, C++, NDK и Rust. Бэкенды Java, C++ и NDK включены по умолчанию. Если какой-либо из этих трёх бэкендов не нужен, его необходимо явно отключить. Rust по умолчанию отключен до версии Android 15.
  • backend.<type>.apex_available : список имен APEX, для которых доступна сгенерированная библиотека-заглушка.
  • backend.[cpp|java].gen_log : Необязательный флаг, который управляет необходимостью генерации дополнительного кода для сбора информации о транзакции.
  • backend.[cpp|java].vndk.enabled : Необязательный флаг, позволяющий сделать этот интерфейс частью VNDK. Значение по умолчанию — false .
  • backend.[cpp|ndk].additional_shared_libraries : Этот флаг, появившийся в Android 14, добавляет зависимости к нативным библиотекам. Он полезен с ndk_header и cpp_header .
  • backend.java.sdk_version : необязательный флаг, указывающий версию SDK, на основе которой собрана библиотека-заглушка Java. Значение по умолчанию — "system_current" . Этот флаг не следует устанавливать, если backend.java.platform_apis имеет значение true .
  • backend.java.platform_apis : необязательный флаг, который следует установить в true когда сгенерированные библиотеки должны быть созданы на основе API платформы, а не SDK.

Для каждой комбинации версий и включённых бэкендов создаётся библиотека-заглушка. Чтобы узнать, как ссылаться на конкретную версию библиотеки-заглушки для конкретного бэкенда, см. раздел Правила именования модулей .

Запись файлов AIDL

Интерфейсы в стабильном AIDL аналогичны традиционным интерфейсам, за исключением того, что им не разрешено использовать неструктурированные объекты Parcelable (поскольку они нестабильны! см. раздел Структурированный и стабильный AIDL ). Основное отличие в стабильном AIDL заключается в том, как определяются объекты Parcelable. Раньше объекты Parcelable объявлялись заранее ; в стабильном (и, следовательно, структурированном) AIDL поля и переменные Parcelable определяются явно.

// in a file like 'some/package/Thing.aidl'
package some.package;

parcelable SubThing {
    String a = "foo";
    int b;
}

Значение по умолчанию поддерживается (но не является обязательным) для boolean , char , float , double , byte , int , long и String . В Android 12 также поддерживаются значения по умолчанию для пользовательских перечислений. Если значение по умолчанию не указано, используется значение, похожее на 0, или пустое значение. Перечисления без значения по умолчанию инициализируются значением 0, даже если нулевой нумератор отсутствует.

Использовать библиотеки-заглушки

После добавления библиотек-заглушек в качестве зависимости к вашему модулю вы можете включить их в свои файлы. Ниже приведены примеры библиотек-заглушек в системе сборки ( Android.mk также можно использовать для определений устаревших модулей). Обратите внимание: в этих примерах версия отсутствует, что указывает на использование нестабильного интерфейса, но имена интерфейсов с версиями содержат дополнительную информацию, см. раздел Управление версиями интерфейсов .

cc_... {
    name: ...,
    // use `shared_libs:` to load your library and its transitive dependencies
    // dynamically
    shared_libs: ["my-module-name-cpp"],
    // use `static_libs:` to include the library in this binary and drop
    // transitive dependencies
    static_libs: ["my-module-name-cpp"],
    ...
}
# or
java_... {
    name: ...,
    // use `static_libs:` to add all jars and classes to this jar
    static_libs: ["my-module-name-java"],
    // use `libs:` to make these classes available during build time, but
    // not add them to the jar, in case the classes are already present on the
    // boot classpath (such as if it's in framework.jar) or another jar.
    libs: ["my-module-name-java"],
    // use `srcs:` with `-java-sources` if you want to add classes in this
    // library jar directly, but you get transitive dependencies from
    // somewhere else, such as the boot classpath or another jar.
    srcs: ["my-module-name-java-source", ...],
    ...
}
# or
rust_... {
    name: ...,
    rustlibs: ["my-module-name-rust"],
    ...
}

Пример на C++:

#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
    // use just like traditional AIDL

Пример на Java:

import some.package.IFoo;
import some.package.Thing;
...
    // use just like traditional AIDL

Пример в Rust:

use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
    // use just like traditional AIDL

Интерфейсы управления версиями

Объявление модуля с именем foo также создает цель в системе сборки, которую можно использовать для управления API модуля. При сборке foo-freeze-api добавляет новое определение API в api_dir или aidl_api/ name в зависимости от версии Android и добавляет .hash файл, оба из которых представляют новую замороженную версию интерфейса. foo-freeze-api также обновляет свойство versions_with_info , чтобы отразить дополнительную версию и imports для этой версии. По сути, imports в versions_with_info копируется из поля imports . Но последняя стабильная версия указывается в imports в versions_with_info для импорта, который не имеет явной версии. После того, как свойство versions_with_info указано, система сборки запускает проверки совместимости между замороженными версиями, а также между Top of Tree (ToT) и последней замороженной версией.

Кроме того, необходимо управлять определением API версии ToT. При каждом обновлении API запускайте foo-update-api для обновления aidl_api/ name /current , содержащего определение API версии ToT.

Для поддержания стабильности интерфейса владельцы могут добавлять новые:

  • Методы в конце интерфейса (или методы с явно определенными новыми сериалами)
  • Элементы в конце парцелляемого объекта (требуется добавить значение по умолчанию для каждого элемента)
  • Постоянные значения
  • В Android 11 перечислители
  • В Android 12 поля до конца объединения

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

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

  • AIDL_FROZEN_REL=true m ... - сборка требует, чтобы все стабильные интерфейсы AIDL, у которых нет owner: поле указано.
  • AIDL_FROZEN_OWNERS="aosp test" — для сборки требуется, чтобы все стабильные интерфейсы AIDL были заморожены owner: » было указано как «aosp» или «test».

Стабильность импорта

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

В коде платформы Android android.hardware.graphics.common является самым ярким примером такого типа обновления версии.

Используйте версионные интерфейсы

Методы интерфейса

Во время выполнения при попытке вызвать новые методы на старом сервере новые клиенты получают либо ошибку, либо исключение, в зависимости от бэкэнда.

  • бэкэнд cpp получает ::android::UNKNOWN_TRANSACTION .
  • Бэкэнд ndk получает STATUS_UNKNOWN_TRANSACTION .
  • бэкэнд java получает исключение android.os.RemoteException с сообщением о том, что API не реализовано.

Стратегии решения этой проблемы смотрите в разделе «Запрос версий и использование значений по умолчанию» .

Посылки

При добавлении новых полей в пакеты старые клиенты и серверы удаляют их. При получении старых пакетов новыми клиентами и серверами значения по умолчанию для новых полей автоматически заполняются. Это означает, что для всех новых полей в пакете необходимо указать значения по умолчанию.

Клиентам не следует ожидать, что серверы будут использовать новые поля, если они не знают, что сервер реализует версию, в которой определено это поле (см. запросы версий ).

Перечисления и константы

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

Профсоюзы

Попытка отправить объединение с новым полем завершится неудачей, если получатель старый и не знает о поле. Реализация никогда не увидит объединение с новым полем. Ошибка игнорируется, если транзакция односторонняя; в противном случае возникает ошибка BAD_VALUE (для бэкенда C++ или NDK) или IllegalArgumentException (для бэкенда Java). Ошибка возникает, если клиент отправляет набор объединений с новым полем на старый сервер или когда старый клиент получает объединение с нового сервера.

Управление несколькими версиями

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

Сборка Android выдаёт ошибку, когда модуль зависит от разных версий одной и той же библиотеки aidl_interface . Модуль может зависеть от этих библиотек напрямую или косвенно через зависимости их зависимостей. Эти ошибки отображают граф зависимостей от проблемного модуля до конфликтующих версий библиотеки aidl_interface . Все зависимости необходимо обновить, включив в них одинаковые (обычно последние) версии этих библиотек.

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

cc_defaults {
  name: "my.aidl.my-process-group-ndk-shared",
  shared_libs: ["my.aidl-V3-ndk"],
  ...
}

cc_library {
  name: "foo",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

cc_binary {
  name: "bar",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

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

aidl_interfaces_defaults можно использовать для хранения одного определения последних версий зависимостей для aidl_interface , которые можно обновлять в одном месте и использовать всеми модулями aidl_interface , которые хотят импортировать этот общий интерфейс.

aidl_interface_defaults {
  name: "android.popular.common-latest-defaults",
  imports: ["android.popular.common-V3"],
  ...
}

aidl_interface {
  name: "android.foo",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

aidl_interface {
  name: "android.bar",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

Развитие на основе флагов

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

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

Флаг сборки AIDL

Флаг, управляющий этим поведением, — RELEASE_AIDL_USE_UNFROZEN , определённый в build/release/build_flags.bzl . Значение true означает, что размороженная версия интерфейса используется во время выполнения, а значение false означает, что библиотеки размороженных версий ведут себя так же, как их последняя замороженная версия. Вы можете переопределить этот флаг на true для локальной разработки, но перед выпуском необходимо сбросить его на false . Обычно разработка выполняется с конфигурацией, в которой этот флаг установлен в true .

Матрица совместимости и манифесты

Объекты интерфейса поставщика (объекты VINTF) определяют, какие версии ожидаются и какие версии предоставляются по обе стороны интерфейса поставщика.

Большинство устройств, отличных от Cuttlefish, используют последнюю матрицу совместимости только после заморозки интерфейсов, поэтому в библиотеках AIDL на основе RELEASE_AIDL_USE_UNFROZEN нет никакой разницы.

Матрицы

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

Например, при добавлении незамороженной версии 4 используйте <version>3-4</version> .

Если версия 4 заморожена, вы можете удалить версию 3 из матрицы совместимости, поскольку замороженная версия 4 используется, когда RELEASE_AIDL_USE_UNFROZEN имеет значение false .

Манифесты

В Android 15 внесено изменение в libvintf для изменения файлов манифеста во время сборки на основе значения RELEASE_AIDL_USE_UNFROZEN .

Манифесты и фрагменты манифеста определяют версию интерфейса, реализуемую службой. При использовании последней размороженной версии интерфейса манифест необходимо обновить для отображения этой новой версии. При RELEASE_AIDL_USE_UNFROZEN=false записи манифеста корректируются libvintf для отражения изменений в сгенерированной библиотеке AIDL. Версия изменяется с размороженной версии N на последнюю замороженную версию N - 1 Таким образом, пользователям не нужно управлять несколькими манифестами или фрагментами манифеста для каждой из своих служб.

Изменения клиента HAL

Клиентский код HAL должен быть обратно совместим с каждой предыдущей поддерживаемой замороженной версией. Если RELEASE_AIDL_USE_UNFROZEN равно false службы всегда выглядят как последняя замороженная версия или более ранняя (например, вызов новых размороженных методов возвращает UNKNOWN_TRANSACTION , или новые поля, parcelable , имеют значения по умолчанию). Клиенты фреймворка Android должны быть обратно совместимы с другими предыдущими версиями, но это новая деталь для клиентов вендоров и клиентов интерфейсов, принадлежащих партнёрам.

Изменения в реализации HAL

Наибольшее отличие разработки HAL от разработки на основе флагов заключается в требовании обратной совместимости реализаций HAL с последней замороженной версией для корректной работы при RELEASE_AIDL_USE_UNFROZEN , равном false . Учёт обратной совместимости в реализациях и коде устройств — это новое направление. См. раздел Использование версионных интерфейсов .

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

Пример: интерфейс имеет три замороженные версии. Интерфейс обновлён новым методом. Клиент и служба обновлены для использования новой библиотеки версии 4. Поскольку библиотека V4 основана на незамороженной версии интерфейса, она ведёт себя как последняя замороженная версия, версия 3, когда RELEASE_AIDL_USE_UNFROZEN имеет false , и запрещает использование нового метода.

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

При вызове методов в обратных вызовах необходимо корректно обрабатывать случай возврата UNKNOWN_TRANSACTION . Клиенты могут реализовывать две разные версии обратного вызова в зависимости от конфигурации выпуска, поэтому нельзя предполагать, что клиент отправляет последнюю версию, и новые методы могут вернуть именно её. Это похоже на то, как стабильные клиенты AIDL поддерживают обратную совместимость с серверами, описанную в разделе «Использование версионных интерфейсов» .

// Get the callback along with the version of the callback
ScopedAStatus RegisterMyCallback(const std::shared_ptr<IMyCallback>& cb) override {
    mMyCallback = cb;
    // Get the version of the callback for later when we call methods on it
    auto status = mMyCallback->getInterfaceVersion(&mMyCallbackVersion);
    return status;
}

// Example of using the callback later
void NotifyCallbackLater() {
  // From the latest frozen version (V2)
  mMyCallback->foo();
  // Call this method from the unfrozen V3 only if the callback is at least V3
  if (mMyCallbackVersion >= 3) {
    mMyCallback->bar();
  }
}

Новые поля в существующих типах ( parcelable , enum , union ) могут отсутствовать или содержать значения по умолчанию, если RELEASE_AIDL_USE_UNFROZEN имеет false , а значения новых полей, которые служба пытается отправить, отбрасываются при выходе из процесса.

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

Реализация никогда не получает вызов новых методов от каких-либо клиентов, если RELEASE_AIDL_USE_UNFROZEN имеет значение false .

Будьте внимательны и используйте новые перечислители только с той версией, в которой они были введены, а не с предыдущей версией.

Обычно для определения версии удалённого интерфейса используется foo->getInterfaceVersion() . Однако при использовании управления версиями на основе флагов вы реализуете две разные версии, поэтому может потребоваться узнать версию текущего интерфейса. Это можно сделать, получив версию интерфейса текущего объекта, например, this->getInterfaceVersion() или других методов метода my_ver . Подробнее см. в разделе «Запрос версии интерфейса удалённого объекта» .

Новые стабильные интерфейсы VINTF

При добавлении нового пакета интерфейса AIDL последняя замороженная версия отсутствует, поэтому нет возможности вернуться к поведению, если RELEASE_AIDL_USE_UNFROZEN равно false . Не используйте эти интерфейсы. Если RELEASE_AIDL_USE_UNFROZEN равно false , Service Manager не позволит службе зарегистрировать интерфейс, и клиенты не смогут его найти.

Вы можете добавлять службы условно, основываясь на значении флага RELEASE_AIDL_USE_UNFROZEN в makefile устройства:

ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
    android.hardware.health.storage-service
endif

Если служба является частью более крупного процесса и добавить её на устройство по условию невозможно, можно проверить, объявлена ​​ли служба с помощью IServiceManager::isDeclared() . Если служба объявлена, но не зарегистрирована, процесс прерывается. Если служба не объявлена, ожидается, что её регистрация не произойдёт.

Новые стабильные интерфейсы расширения VINTF

Новые интерфейсы расширения не имеют предыдущей версии, к которой можно было бы вернуться, и поскольку они не зарегистрированы в ServiceManager или не объявлены в манифестах VINTF, IServiceManager::isDeclared() нельзя использовать для определения того, когда следует присоединить интерфейс расширения к другому интерфейсу.

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

Тесты VTS vts_treble_vintf_vendor_test и vts_treble_vintf_framework_test определяют, когда в выпущенном устройстве используется незамороженный интерфейс расширения, и выдают ошибку.

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

Каракатица как инструмент развития

Каждый год после заморозки VINTF мы корректируем target-level PRODUCT_SHIPPING_API_LEVEL Cuttlefish, чтобы они соответствовали устройствам, которые будут выпущены в следующем году. Мы корректируем target-level и уровень PRODUCT_SHIPPING_API_LEVEL , чтобы убедиться, что какое-либо устройство, запускаемое в следующем году, протестировано и соответствует новым требованиям.

Если RELEASE_AIDL_USE_UNFROZEN равно true , Cuttlefish используется для разработки будущих версий Android. Она ориентирована на уровень FCM и PRODUCT_SHIPPING_API_LEVEL версии Android следующего года, что требует соответствия требованиям к программному обеспечению поставщика (VSR) для следующей версии.

Если RELEASE_AIDL_USE_UNFROZEN равно false , Cuttlefish использует предыдущий target-level и PRODUCT_SHIPPING_API_LEVEL для отображения устройства выпуска. В Android 14 и ниже эта дифференциация будет осуществляться с помощью различных веток Git, которые не учитывают изменение target-level FCM, уровня API доставки или любого другого кода, ориентированного на следующий выпуск.

Правила именования модулей

В Android 11 для каждой комбинации версий и включённых бэкендов автоматически создаётся модуль библиотеки-заглушки. Чтобы сослаться на конкретный модуль библиотеки-заглушки для линковки, используйте не имя модуля aidl_interface , а имя модуля библиотеки-заглушки, то есть ifacename - version - backend , где

  • ifacename : имя модуля aidl_interface
  • version является либо из
    • V version-number для замороженных версий
    • V latest-frozen-version-number + 1 для версии, которая находится на вершине дерева (еще не заморожена)
  • backend — это либо
    • java для бэкэнда Java,
    • cpp для бэкэнда C++,
    • ndk или ndk_platform для бэкенда NDK. Первый предназначен для приложений, а второй — для использования платформой до версии Android 13. В Android 13 и более поздних версиях используйте только ndk .
    • rust для бэкенда Rust.

Предположим, что существует модуль с именем foo и его последняя версия — 2 , поддерживающий как NDK, так и C++. В этом случае AIDL генерирует следующие модули:

  • На основе версии 1
    • foo-V1-(java|cpp|ndk|ndk_platform|rust)
  • На основе версии 2 (последней стабильной версии)
    • foo-V2-(java|cpp|ndk|ndk_platform|rust)
  • На основе версии ToT
    • foo-V3-(java|cpp|ndk|ndk_platform|rust)

По сравнению с Android 11:

  • foo- backend , который ссылается на последнюю стабильную версию, становится foo- V2 - backend
  • foo-unstable- backend , которая ссылалась на версию ToT, становится foo- V3 - backend

Имена выходных файлов всегда совпадают с именами модулей.

  • На основе версии 1: foo-V1-(cpp|ndk|ndk_platform|rust).so
  • На основе версии 2: foo-V2-(cpp|ndk|ndk_platform|rust).so
  • На основе версии ToT: foo-V3-(cpp|ndk|ndk_platform|rust).so

Обратите внимание, что компилятор AIDL не создаёт ни unstable версию модуля, ни модуль без версии для стабильного интерфейса AIDL. Начиная с Android 12, имя модуля, сгенерированное из стабильного интерфейса AIDL, всегда включает его версию.

Новые методы метаинтерфейса

Android 10 добавляет несколько методов метаинтерфейса для стабильной версии AIDL.

Запросить версию интерфейса удаленного объекта

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

Пример с бэкэндом cpp :

sp<IFoo> foo = ... // the remote object
int32_t my_ver = IFoo::VERSION;
int32_t remote_ver = foo->getInterfaceVersion();
if (remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::HASH;
std::string remote_hash = foo->getInterfaceHash();

Пример с бэкэндом ndkndk_platform ):

IFoo* foo = ... // the remote object
int32_t my_ver = IFoo::version;
int32_t remote_ver = 0;
if (foo->getInterfaceVersion(&remote_ver).isOk() && remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::hash;
std::string remote_hash;
foo->getInterfaceHash(&remote_hash);

Пример с бэкэндом java :

IFoo foo = ... // the remote object
int myVer = IFoo.VERSION;
int remoteVer = foo.getInterfaceVersion();
if (remoteVer < myVer) {
  // the remote side is using an older interface
}

String myHash = IFoo.HASH;
String remoteHash = foo.getInterfaceHash();

Для языка Java удаленная сторона ДОЛЖНА реализовать getInterfaceVersion() и getInterfaceHash() следующим образом ( super используется вместо IFoo , чтобы избежать ошибок копирования и вставки. Для отключения предупреждений может потребоваться аннотация @SuppressWarnings("static") в зависимости от конфигурации javac ):

class MyFoo extends IFoo.Stub {
    @Override
    public final int getInterfaceVersion() { return super.VERSION; }

    @Override
    public final String getInterfaceHash() { return super.HASH; }
}

Это связано с тем, что сгенерированные классы ( IFoo , IFoo.Stub и т. д.) являются общими для клиента и сервера (например, классы могут находиться в загрузочном classpath). При использовании общих классов сервер также линкуется с последней версией классов, даже если он мог быть собран с использованием более старой версии интерфейса. Если этот метаинтерфейс реализован в общем классе, он всегда возвращает самую новую версию. Однако при реализации метода, как указано выше, номер версии интерфейса встраивается в код сервера (поскольку IFoo.VERSION — это static final int , которое встраивается при обращении к нему), и, таким образом, метод может вернуть точную версию, с которой был собран сервер.

Работа со старыми интерфейсами

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

Благодаря стабильной версии AIDL клиенты получают больше контроля. На стороне клиента можно задать реализацию по умолчанию для интерфейса AIDL. Метод в реализации по умолчанию вызывается только тогда, когда он не реализован на удалённой стороне (поскольку был создан с использованием более старой версии интерфейса). Поскольку значения по умолчанию задаются глобально, их не следует использовать в потенциально общих контекстах.

Пример на C++ в Android 13 и более поздних версиях:

class MyDefault : public IFooDefault {
  Status anAddedMethod(...) {
   // do something default
  }
};

// once per an interface in a process
IFoo::setDefaultImpl(::android::sp<MyDefault>::make());

foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
                         // remote side is not implementing it

Пример на Java:

IFoo.Stub.setDefaultImpl(new IFoo.Default() {
    @Override
    public xxx anAddedMethod(...)  throws RemoteException {
        // do something default
    }
}); // once per an interface in a process

foo.anAddedMethod(...);

Вам не нужно предоставлять реализацию по умолчанию для всех методов в интерфейсе AIDL. Методы, которые гарантированно реализуются на удалённой стороне (поскольку вы уверены, что удалённая сторона была собрана на момент описания методов в интерфейсе AIDL), не нужно переопределять в классе impl по умолчанию.

Преобразовать существующий AIDL в структурированный или стабильный AIDL

Если у вас есть существующий интерфейс AIDL и код, который его использует, выполните следующие шаги для преобразования интерфейса в стабильный интерфейс AIDL.

  1. Определите все зависимости вашего интерфейса. Для каждого пакета, от которого зависит интерфейс, проверьте, определен ли пакет в стабильной версии AIDL. Если пакет не определен, его необходимо преобразовать.

  2. Преобразуйте все парцелевые объекты (parcelables) в вашем интерфейсе в стабильные (сами файлы интерфейсов могут оставаться неизменными). Для этого необходимо выразить их структуру непосредственно в файлах AIDL. Классы управления необходимо переписать для использования этих новых типов. Это можно сделать до создания пакета aidl_interface (см. ниже).

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