Стабильный AIDL

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

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

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

Структурированный AIDL относится к типам, определенным исключительно в AIDL. Например, декларация parcelable (пользовательский parcelable) не является структурированным AIDL. Parcelables с полями, определенными в AIDL, называются структурированными parcelables .

Stable AIDL требует структурированного AIDL, чтобы система сборки и компилятор могли понять, являются ли изменения, внесенные в parcelables, обратно совместимыми. Однако не все структурированные интерфейсы стабильны. Чтобы быть стабильным, интерфейс должен использовать только структурированные типы, а также он должен использовать следующие функции управления версиями. И наоборот, интерфейс не является стабильным, если для его сборки используется основная система сборки или если установлено 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 использует интерфейс или parcelable из другого aidl_interface , укажите его имя здесь. Это может быть само имя, для ссылки на последнюю версию, или имя с суффиксом версии (например, -V1 ) для ссылки на определенную версию. Указание версии поддерживается с Android 12
  • versions : Предыдущие версии интерфейса, замороженные в api_dir , начиная с Android 11, versions заморожены в aidl_api/ name . Если замороженных версий интерфейса нет, это не должно быть указано, и проверки совместимости не будут. Это поле было заменено на versions_with_info для Android 13 и выше.
  • 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 значение по умолчанию — true для бэкэндов cpp и java .
  • 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 похожи на традиционные интерфейсы, за исключением того, что им не разрешено использовать неструктурированные parcelables (потому что они нестабильны! см. Структурированный и стабильный AIDL ). Основное отличие в стабильном AIDL заключается в том, как определяются parcelables. Ранее parcelables объявлялись заранее ; в стабильном (и, следовательно, структурированном) AIDL поля и переменные parcelables определяются явно.

// 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 не реализован.

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

Посылки

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

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

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

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

Профсоюзы

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

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

Пространство имен компоновщика в Android может иметь только 1 версию определенного интерфейса 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 матрицы совместимости фреймворка (FCM) и PRODUCT_SHIPPING_API_LEVEL Cuttlefish, чтобы они отражали устройства, запускаемые с выпуском следующего года. Мы корректируем target-level и PRODUCT_SHIPPING_API_LEVEL , чтобы убедиться, что есть какое-то запускаемое устройство, которое протестировано и соответствует новым требованиям для выпуска следующего года.

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

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

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

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

  • ifacename : имя модуля aidl_interface
  • version является либо из
    • V version-number для замороженных версий
    • V latest-frozen-version-number + 1 для версии tip-of-tree (еще не замороженной)
  • 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 и т. д.) являются общими для клиента и сервера (например, классы могут находиться в пути к классам загрузки). Когда классы являются общими, сервер также связывается с новейшей версией классов, даже если он мог быть построен с использованием более старой версии интерфейса. Если этот метаинтерфейс реализован в общем классе, он всегда возвращает новейшую версию. Однако, реализуя метод, как указано выше, номер версии интерфейса встраивается в код сервера (потому что 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 в вашем интерфейсе в стабильные parcelables (сами файлы интерфейса могут оставаться неизменными). Сделайте это, выразив их структуру непосредственно в файлах AIDL. Классы управления должны быть переписаны для использования этих новых типов. Это можно сделать до создания пакета aidl_interface (ниже).

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