Внедрить радио

На этой странице объясняется, как реализовать радиосвязь на аппаратном и программном уровнях.

Системные компоненты

Стек радиовещания включает в себя следующие компоненты.

Архитектура радиовещания
Рисунок 1. Архитектура широковещательной радиосвязи

Справочное приложение по радио

Подробности о том, как реализовать радиоуправление, см. в разделе Реализация радиоуправления .

Пример приложения Java-радио ( packages/apps/Car/Radio ) служит эталонной реализацией. Когда служба приложения запускается, она запрашивает Radio Manager открыть радиотюнер. Затем приложение может отправлять запросы радиотюнеру, например настройку на определенную радиостанцию, частоту или поиск следующей доступной радиостанции. Приложение получает обновления от Radio Manager и Radio Tuner в Radio, такие как информация о текущих программах, списки радиопрограмм, конфигурации и параметры, определенные поставщиком. Эталонное приложение «Радио» поддерживает только радио AM и FM. OEM-производители могут изменить или заменить приложение Radio по своему усмотрению.

Радио-менеджер

Когда приложение запрашивает Radio Manager открыть тюнер, Radio Manager ( frameworks/base/core/java/android/hardware/radio/RadioManager.java ) запрашивает службу радиовещания для открытия сеанса тюнера, а затем переносит сеанс в Радиотюнер ( frameworks/base/core/java/android/hardware/radio/RadioTuner.java ), который возвращается в приложение. Радиотюнер определяет API-интерфейсы (такие как настройка, шаг и отмена), которые можно вызывать из радиоприложений и отправлять запросы в службу радиовещания. Методы обратного вызова ( RadioTuner.Callback ), определенные в Radio Tuner, отправляют обновления о HAL широковещательного радио, такие как информация о текущей программе, списки программ и параметры, определенные поставщиком, из службы радиовещания в приложения.

Служба радиовещания

Служба широковещательного радио ( frameworks/base/services/core/java/com/android/server/broadcastradio ) — это клиентская служба для Broadcast Radio HAL. Служба радиовещания координирует работу нескольких менеджеров радио с HAL радиовещания. Служба радиовещания поддерживает язык определения интерфейса HAL (HIDL) и язык определения интерфейса Android (AIDL) HAL радиовещания. Служба радиовещания связана с AIDL HAL, когда существует какая-либо служба AIDL HAL; в противном случае служба ссылается на HIDL HAL. Служба радиовещания создает радиомодуль для каждого доступного экземпляра HAL (например, AM, FM и DAB).

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

Трансляция радио HAL

Чтобы узнать больше об интерфейсах HIDL и AIDL радиовещания и различиях между ними, см. Интерфейс HAL радиовещания .

Уровень абстракции оборудования радиовещания

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

Радиовещательный интерфейс HAL

HAL радиовещания предоставляет структуры данных и интерфейсы на аппаратном уровне для реализации радиовещания, такого как радио AM/FM и DAB.

Интерфейсы HIDL 2.0 и AIDL

Вещательная радиостанция HAL использует интерфейсы, описанные в следующих разделах.

Слушатель объявлений

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

IОбъявлениеСлушатель
Описание: Вызывается всякий раз, когда список объявлений изменяется.
ХИДЛ 2.0 oneway onListUpdated(vec<Announcement> announcements)
АИДЛ oneway void onListUpdated(in Announcement[] announcements)
Закрыть ручку

ICloseHandle — это универсальный дескриптор закрытия для удаления обратного вызова, для которого не требуется активный интерфейс.

ICloseHandle
Описание: Закройте ручку.
ХИДЛ 2.0 close()
АИДЛ void close()

Интерфейс обратного вызова

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

ITunerОбратный вызов
Описание: Вызывается HAL, когда операция настройки (настройка, поиск (в AIDL) или сканирование (в HIDL) и шаг завершаются успешно) завершается асинхронно.
ХИДЛ 2.0 oneway onCurrentProgramInfoChanged(ProgramInfo info)
АИДЛ void onCurrentProgramInfoChanged(in ProgramInfo info)
Описание: Вызывается при настройке, поиске (в AIDL) или сканировании (в HIDL) или при успешном выполнении шага.
ХИДЛ 2.0 oneway onTuneFailed(Result result, ProgramSelector selector)
АИДЛ void onTuneFailed(in Result result, in ProgramSelector selector)
Описание: Вызывается при настройке, поиске (в AIDL) или сканировании (в HIDL) или при успешном выполнении шага.
ХИДЛ 2.0 oneway onCurrentProgramInfoChanged(ProgramInfo info)
АИДЛ void onCurrentProgramInfoChanged(in ProgramInfo info)
Описание: Вызывается при обновлении списка программ; размер каждого фрагмента должен быть ограничен 500 КБ.
ХИДЛ 2.0 oneway onProgramListUpdated(ProgramListChunk chunk)
АИДЛ oneway onProgramListUpdated(ProgramListChunk chunk)
Описание: Вызывается, когда антенна подключена или отключена.
ХИДЛ 2.0 oneway onAntennaStateChange(bool connected)
АИДЛ void onCurrentProgramInfoChanged(in ProgramInfo info)
Описание: Вызывается, когда значения параметров, специфичные для поставщика, обновляются внутри HAL (не следует вызывать после вызова setParameters клиентом HAL).
ХИДЛ 2.0 oneway onParametersUpdated(vec<VendorKeyValue> parameters)
АИДЛ void onParametersUpdated(in VendorKeyValue[] parameters)
Описание: Новое в AIDL. Вызывается, когда флаг конфигурации обновляется внутри HAL (не следует вызывать после вызова setConfigFlag клиентом HAL).
ХИДЛ 2.0 Непригодный.
АИДЛ void onConfigFlagUpdated(in ConfigFlag flag, in boolean value)

Первичный радиовещательный интерфейс HAL

IBroadcastRadio — это основной интерфейс для радиовещания HAL. В HAL HIDL 2.0 используйте интерфейс ITunerSession к тюнеру для вызова операций. Однако одновременно активен не более одного тюнера (при условии, что каждый экземпляр HAL радиовещания имеет только один чип тюнера). ITunerSession был удален из интерфейсов AIDL, а его интерфейсы перенесены в IBroadcastRadio .

IBroadcastRadio
Описание: Получите описание модуля и его возможностей.
ХИДЛ 2.0 getProperties() generates (Properties properties)
АИДЛ Properties getProperties()
Описание: Получает текущую или возможную конфигурацию региона AM/FM.
ХИДЛ 2.0 getAmFmRegionConfig(bool full) generates (Result result, AmFmRegionConfig config)
АИДЛ AmFmRegionConfig getAmFmRegionConfig(bool full)
Описание: извлекает текущую конфигурацию региона DAB.
ХИДЛ 2.0 getDabRegionConfig() generates (Result result, vec<DabTableEntry> config)
АИДЛ DabTableEntry[] getDabRegionConfig()
Описание: Получает образ из кэша радиомодуля. В AIDL размер изображения должен быть меньше 1 МБ из-за жесткого ограничения на буфер транзакции связывателя.
ХИДЛ 2.0 getImage(uint32_t id) generates (vec<uint8_t> image)
АИДЛ byte[] getImage(in int id)
Описание: Регистрирует прослушиватель объявлений.
ХИДЛ 2.0 registerAnnouncementListener(vec<AnnouncementType> enabled,IAnnouncementListener listener) generates (Result result, ICloseHandle closeHandle)
АИДЛ ICloseHandle registerAnnouncementListener(in IAnnouncementListener listener, in AnnouncementType[] enabled)
Описание:
  • HIDL HAL: При открытии нового сеанса тюнера старый сеанс должен быть завершен.
  • AIDL HAL: поскольку сеанс настройки не доступен, необходимо установить только обратный вызов тюнера. Если он существует, старый обратный вызов должен быть отключен.
ХИДЛ 2.0 openSession(ITunerCallback callback) генерирует (Result result, ITunerSession session)
АИДЛ void setTunerCallback(in ITunerCallback callback)
Описание:
  • HIDL HAL: Закрытие сеанса настройки не должно завершаться неудачно и должно быть выполнено только один раз.
  • AIDL HAL: Тюнера нет, необходимо отключить только обратный вызов тюнера.
ХИДЛ 2.0 close()
АИДЛ unsetTunerCallback()
Описание: Настраивается на указанную программу.
ХИДЛ 2.0 tune(ProgramSelector program) generates (Result result)
АИДЛ void tune(in ProgramSelector program)
Описание: Ищет следующую действительную программу в эфире . Чтобы избежать путаницы в AIDL, scan переименовывается в seek .
ХИДЛ 2.0 scan(bool directionUp, bool skipSubChannel) generates (Result result)
АИДЛ void seek(in boolean directionUp, in boolean skipSubChannel)
Описание: Переход на соседний канал, который не может быть занят какой-либо программой.
ХИДЛ 2.0 step(bool directionUp) generates (Result result)
АИДЛ void step(in boolean directionUp)
Описание: Отменяет ожидающие настройки, сканирование (в HIDL) или поиск (в AIDL) или пошаговые операции.
ХИДЛ 2.0 cancel()
АИДЛ void cancel()
Описание: применяет фильтр к списку программ и начинает отправлять обновления списка программ через обратный вызов onProgramListUpdated .
ХИДЛ 2.0 startProgramListUpdates(ProgramFilter filter) generates (Result result)
АИДЛ void startProgramListUpdates(in ProgramFilter filter)
Описание: Останавливает отправку обновлений списка программ.
ХИДЛ 2.0 stopProgramListUpdates()
АИДЛ void stopProgramListUpdates()
Описание: извлекает текущую настройку данного флага конфигурации.
ХИДЛ 2.0 isConfigFlagSet(ConfigFlag flag) generates (Result result, bool value)
АИДЛ boolean isConfigFlagSet(in ConfigFlag flag)
Описание: Устанавливает данный флаг конфигурации.
ХИДЛ 2.0 setConfigFlag(ConfigFlag flag, bool value) generates (Result result)
АИДЛ void setConfigFlag(in ConfigFlag flag, boolean value)
Описание: Устанавливает значения параметров, зависящие от поставщика.
ХИДЛ 2.0 setParameters(vec<VendorKeyValue> parameters)

генерирует ,

(vec<VendorKeyValue> results)
АИДЛ VendorKeyValue[] setParameters(in VendorKeyValue[] parameters)
Описание: Извлекает значения параметров, зависящие от поставщика.
ХИДЛ 2.0 getParameters(vec<string> keys) generates (vec<VendorKeyValue> parameters)
АИДЛ VendorKeyValue[] getParameters(in String[] keys)

Уточнения интерфейса

Асинхронное поведение

Поскольку каждая операция настройки (например, настройка, сканирование (в HIDL) или поиск (в AIDL) и шаг) может занимать много времени, а поток не должен блокироваться на долгое время, операция должна планировать трудоемкие операции. произойти позже и быстро вернуть статус или результат. Подробно каждая операция должна:

  • Отмените все ожидающие операции настройки.
  • Проверьте, может ли операция быть обработана на основе входных данных метода и состояния тюнера.
  • Запланируйте задачу настройки, а затем немедленно верните Result (в HIDL) или status (в AIDL). Если Result или status имеют OK , обратный вызов тюнера tuneFailed или currentProgramInfoChanged должен быть вызван в случае сбоя задачи настройки (например, из-за тайм-аута) или ее завершения.

Аналогично, startProgramListUpdates также планирует выполнение трудоемкой задачи по обновлению списка программ, которая будет выполнена позже и быстро вернет статус или результат. Метод сначала отменяет ожидающие запросы на обновление, а затем планирует задачу обновления и быстро возвращает результат.

Состояние гонки

Из-за асинхронного поведения операций настройки (например, настройки, сканирования (в HIDL) или поиска (в AIDL) и шага), существует состояние гонки между отменой операции и операциями настройки. Если cancel вызывается после того, как HAL завершит операцию настройки и до завершения обратного вызова, отмену можно игнорировать, а обратный вызов должен завершиться и быть получен клиентом HAL.

Аналогично, если stopProgramListUpdates вызывается после того, как HAL завершит обновление списка программ и до завершения обратного вызова onCurrentProgramInfoChanged , stopProgramListUpdates можно игнорировать и обратный вызов должен завершиться.

Ограничение размера данных

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

  • getImage требует, чтобы возвращаемое изображение имело размер менее 1 МБ.
  • onProgramListUpdate требует, чтобы каждый chunk был меньше 500 КБ. Более крупные списки программ должны быть разделены реализацией HAL на несколько частей и отправлены через несколько обратных вызовов.

Изменения в структурах данных AIDL HAL

Помимо изменений в интерфейсах, эти изменения были применены к структурам данных, определенным в AIDL HAL вещания, который использует преимущества AIDL.

  • Перечисление Constant удалено в AIDL и определено как const int в IBroadcastRadio . При этом ANTENNA_DISCONNECTED_TIMEOUT_MS переименовывается в ANTENNA_STATE_CHANGE_TIMEOUT_MS . Добавлена ​​новая константа int TUNER_TIMEOUT_MS . Все операции настройки, поиска и пошагового выполнения должны быть завершены в течение этого времени.
  • Enum RDS и Deemphasis удалены в AIDL и определены как const int в AmFmRegionConfig . Соответственно, и fmDeemphasis , и fmRds в ProgramInfo объявлены как int, результат вычисления битов соответствующих флагов. При этом D50 и D75 переименованы в DEEMPHASIS_D50 и DEEMPHASIS_D75 соответственно.
  • Enum ProgramInfoFlags удалены в AIDL и определены как const int в ProgramInfo с добавленным префиксом FLAG_ . Соответственно, infoFlags в ProgramInfo объявляется как int, битовый результат вычисления флагов. TUNED также переименован в FLAG_TUNABLE , чтобы лучше описать его определение, на которое можно настроить станцию.
  • В AmFmBandRange scanSpacing переименован в seekSpacing , поскольку в AIDL scan переименован в seek .
  • Поскольку в AIDL введена концепция объединения , MetadataKey и Metadata определенные в HIDL HAL, больше не используются. Metadata объединения AIDL определены в AIDL HAL. Каждое значение перечисления, ранее входившее в MetadataKey , теперь является полем в Metadata с типом string или int, в зависимости от их определений.

Реализация радиоуправления

Реализация радиоуправления основана на MediaSession и MediaBrowse , которые позволяют приложениям мультимедиа и голосовому помощнику управлять радио. Дополнительную информацию см. в разделе «Создание мультимедийных приложений для автомобилей» на сайте Developer.android.com.

Реализация дерева просмотра медиа предоставляется в библиотеке car-broadcastradio-support в packages/apps/Car/libs . Эта библиотека также содержит расширения ProgramSelector для преобразования в URI и обратно. Реализациям радио рекомендуется использовать эту библиотеку для построения соответствующего дерева просмотра.

Переключатель источника мультимедиа

Чтобы обеспечить плавный переход между радио и другими приложениями, отображаемыми на мультимедиа, библиотека car-media-common содержит классы, которые следует интегрировать в приложение радио. MediaAppSelectorWidget можно включить в XML для приложения радио (значок и раскрывающийся список, используемые в справочных приложениях мультимедиа и радио):

<com.android.car.media.common.MediaAppSelectorWidget
     android:id="@+id/app_switch_container"
     android:layout_width="@dimen/app_switch_widget_width"
     android:layout_height="wrap_content"
     android:background="@drawable/app_item_background"
     android:gravity="center" />

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

AppSelectionFragment newFragment = AppSelectionFragment.create(widget,
            packageName, fullScreen);
    newFragment.show(mActivity.getSupportFragmentManager(), null);

Пример реализации представлен в эталонной реализации радиоприложения, расположенной в packages/apps/Car/Radio .

Подробные характеристики управления

Интерфейс MediaSession (через MediaSession.Callback ) предоставляет механизмы управления воспроизводимой в данный момент радиопрограммой:

  • onPlay , onStop . (Включить)отключение звука радио.
  • onPause . Пауза со сдвигом по времени (если поддерживается).
  • onPlayFromMediaId . Воспроизводите любой контент из папки верхнего уровня. Например, «Включи FM» или «Включи радио».
  • onPlayFromUri . Играйте на определенной частоте. Например, «Включи 88,5 FM».
  • onSkipToNext , onSkipToPrevious . Настройтесь на следующую или предыдущую станцию.
  • onSetRating . Добавить или удалить из избранного.

MediaBrowser предоставляет настраиваемый MediaItem для трех типов каталогов верхнего уровня:

  • ( Дополнительно ) Программы (станции). Этот режим обычно используется радиостанциями с двойным тюнером для указания всех доступных настраиваемых радиостанций в местоположении пользователя.
  • Избранное. Радиопрограммы добавлены в список «Избранное», некоторые из них могут быть недоступны (вне зоны приема).
  • Бандовые каналы. Все физически возможные каналы в текущем регионе (87.9, 88.1, 88.3, ​​88.5, 88.7, 88.9, 89.1 и так далее). У каждой группы есть отдельный каталог верхнего уровня.
Древовидная структура MediaBrowserService
Рисунок 2. Древовидная структура MediaBrowserService

Каждый элемент в каждой из этих папок (AM/FM/Programs) представляет собой MediaItem с URI, который можно использовать с MediaSession для настройки. Каждая папка верхнего уровня (AM/FM/Programs) представляет собой MediaItem с идентификатором mediaId, который можно использовать с MediaSession для запуска воспроизведения и выбор которого осуществляется на усмотрение OEM-производителя. Например, «Воспроизвести FM», «Воспроизвести AM» и «Воспроизвести радио» — это неспецифические радиозапросы, которые используют mediaId для отправки в приложение OEM-радио. Приложение радио должно определить, что воспроизводить, на основе общего запроса и mediaId.

Медиасессия

Поскольку не существует концепции приостановки транслируемого потока, действия «Воспроизведение», «Пауза» и «Стоп» не всегда применимы к радио. В случае радио действие «Стоп» связано с отключением звука, а действие «Воспроизведение» связано с отключением звука.

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

Воспроизведение с помощью действий mediaId и URI предназначено для настройки на станцию, полученную из интерфейса MediaBrowser. MediaId — это произвольная строка, предоставляемая приложением радио, чтобы задать уникальное (поэтому заданный идентификатор указывает только на один элемент) и стабильное (поэтому данный элемент имеет один и тот же идентификатор на протяжении всего сеанса) значение, с помощью которого можно идентифицировать данную станцию. . URI будет иметь четко определенную схему. Короче говоря, URI-образная форма ProgramSelector. Хотя это сохраняет атрибут уникальности, он не обязательно должен быть стабильным, хотя он может меняться, когда станция переходит на другую частоту.

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

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

Переход к следующей или предыдущей станции зависит от текущего контекста:

  • Когда приложение настроено на станцию ​​из списка «Избранное», оно может перейти к следующей станции из списка «Избранное».
  • Прослушивание станции из списка программ может привести к настройке на следующую доступную станцию, отсортированную по номеру канала.
  • Прослушивание произвольного канала может привести к настройке на следующий физический канал, даже если нет широковещательного сигнала.

Приложение радио обрабатывает эти действия.

Обработка ошибок

Действия TransportControls (Воспроизвести, Остановить и Далее) не предоставляют информацию о том, удалось ли действие или нет. Единственный способ указать на ошибку — установить для состояния MediaSession значение STATE_ERROR с сообщением об ошибке.

Радиоприложение должно обрабатывать эти действия и либо выполнять их, либо устанавливать состояние ошибки. Если выполнение команды Play не является немедленным, состояние воспроизведения должно быть изменено на STATE_CONNECTING (в случае прямой настройки) или STATE_SKIPPING_TO_PREVIOUS или NEXT во время выполнения команды.

Клиент должен наблюдать за PlaybackState и проверять, изменила ли сессия текущую программу на ту, что была запрошена, или ввела в состояние ошибки. STATE_CONNECTING не должно превышать 30 с. Однако прямая настройка на заданную частоту AM/FM должна работать намного быстрее.

Добавляйте и удаляйте избранное

MediaSession имеет поддержку рейтингов, которую можно использовать для управления избранным. onSetRating , вызываемый с рейтингом типа RATING_HEART добавляет или удаляет настроенную в данный момент станцию ​​в список избранного или из него.

В отличие от устаревших предустановок, эта модель предполагает неупорядоченный и неограниченный список избранного, когда каждому сохраненному избранному соответствует числовой слот (обычно от 1 до 6). В результате системы на основе предустановок будут несовместимы с операцией onSetRating .

Ограничением API MediaSession является то, что можно добавить или удалить только станцию, на которую настроена в данный момент. Например, элементы необходимо сначала выбрать, прежде чем их можно будет удалить. Это всего лишь ограничение клиента MediaBrowser, например сопутствующего приложения. Приложение радио не имеет подобных ограничений. Эта часть является необязательной, если приложение не поддерживает Избранное.

Медиабраузер

Чтобы указать, какие частоты или названия физических каналов (когда настройка на произвольный канал подходит для данной радиотехнологии) действительны для данного региона, все допустимые каналы (частоты) перечислены для каждого диапазона. В регионе США это составляет 101 канал FM в диапазоне от 87,8 до 108,0 МГц (с интервалом 0,2 МГц) и 117 каналов AM в диапазоне от 530 до 1700 кГц (с интервалом 10 кГц). Поскольку HD-радио использует одно и то же пространство каналов, оно не представлено отдельно.

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

Записи в списке избранного могут быть недоступны для настройки. Например, если данная программа находится вне диапазона. Приложение радио может определить, можно ли заранее настроить эту запись, а может и не определить. В этом случае запись может не быть отмечена как воспроизводимая.

Для идентификации папок верхнего уровня применяется тот же механизм, что и в Bluetooth. То есть пакет Extras объекта MediaDescription содержит поле, специфичное для тюнера, так же, как Bluetooth с EXTRA_BT_FOLDER_TYPE . В случае радиовещания это приводит к определению следующих новых полей в общедоступном API:

  • EXTRA_BCRADIO_FOLDER_TYPE = "android.media.extra.EXTRA_BCRADIO_FOLDER_TYPE" . Одно из следующих значений:
    • BCRADIO_FOLDER_TYPE_PROGRAMS = 1 . Доступные на данный момент программы.
    • BCRADIO_FOLDER_TYPE_FAVORITES = 2 . Избранное.
    • BCRADIO_FOLDER_TYPE_BAND = 3 . Все физические каналы для данного диапазона.

    Нет необходимости определять какие-либо пользовательские поля метаданных, специфичные для радио, поскольку все соответствующие данные вписываются в существующую схему MediaBrowser.MediaItem :

    • Название программы (RDS PS, имя службы DAB). MediaDescription.getTitle .
    • FM-частота. URI (см. ProgramSelector ) или MediaDescription.getTitle (если запись находится в папке BROADCASTRADIO_FOLDER_TYPE_BAND ).
    • Радиоспецифичные идентификаторы (RDS PI, DAB SId). MediaDescription.getMediaUri анализируется в ProgramSelector.

    Как правило, нет необходимости выбирать частоту FM для записи в текущей программе или списке избранного (поскольку клиент должен работать с идентификаторами мультимедиа). Однако, если такая необходимость возникнет (например, для целей отображения), она присутствует в URI и может быть проанализирована с помощью ProgramSelector . Тем не менее, не рекомендуется использовать URI для выбора элементов в текущем сеансе. Подробности см. в разделе ProgramSelector .

    Чтобы избежать проблем с производительностью или связующими, служба MediaBrowser должна поддерживать нумерацию страниц:

    Примечание. По умолчанию нумерация страниц реализована в варианте onLoadChildren() без обработки параметров.

    Связанные записи из всех типов списков (необработанные каналы, найденные программы и избранное) могут иметь разные идентификаторы mediaId (это зависит от приложения радио; в библиотеке поддержки они будут разные). URI (в форме ProgramSelector) различаются между необработанными каналами и найденными программами в большинстве случаев (за исключением FM без RDS), но в основном одинаковы между найденными и избранными программами (за исключением, например, случаев обновления AF).

    Наличие разных mediaId для записей из разных типов списков позволяет выполнять над ними разные действия. Вы можете просмотреть либо список «Избранное», либо список «Все программы» в onSkipToNext , в зависимости от папки недавно выбранного MediaItem (см. MediaSession ).

    Специальные действия по настройке

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

    Для поддержки таких действий в некоторых каталогах верхнего уровня установлен флаг FLAG_PLAYABLE (наряду с FLAG_BROWSABLE для папок).

    Действие Настраивается на Как выдать
    Включить радио Любой радиоканал startService(ACTION_PLAY_BROADCASTRADIO)

    или

    playFromMediaId(MediaBrowser. getRoot() )
    Воспроизвести FM Любой FM-канал Воспроизведение с mediaId FM-диапазона.

    Выбор программы для настройки зависит от приложения. Обычно это последний настроенный канал из данного списка. Подробную информацию о ACTION_PLAY_BROADCASTRADIO см. в разделе «Общие игровые намерения» .

    Обнаружение и подключение услуги

    PackageManager может напрямую найти MediaBrowserService, обслуживающий дерево радиовещания. Для этого resolveService с намерением ACTION_PLAY_BROADCASTRADIO (см. Общие намерения воспроизведения ) и флагом MATCH_SYSTEM_ONLY . Чтобы найти все службы, обслуживающие радио (их может быть несколько; например, отдельные AM/FM и спутниковые), используйте queryIntentServices .

    Разрешенная служба также обрабатывает намерение привязки android.media.browse.MediaBrowserService . Это подтверждено GTS.

    Чтобы подключиться к выбранному MediaBrowserService, создайте экземпляр MediaBrowser для данного компонента службы и connect . После установления соединения дескриптор MediaSession можно получить с помощью getSessionToken .

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

    Если приложение для конкретного источника (например, приложение радио) установлено на устройстве без такой поддержки источника, оно все равно будет рекламировать себя как обрабатывающее намерение ACTION_PLAY_BROADCASTRADIO , но его дерево MediaBrowser не будет содержать теги, специфичные для радио. Таким образом, клиент, желающий проверить, доступен ли данный источник на устройстве, должен:

    1. Откройте для себя службу радио ( resolveService для ACTION_PLAY_BROADCASTRADIO ).
    2. Создайте MediaBrowser и затем подключитесь к нему.
    3. Определите наличие MediaItem с доп EXTRA_BCRADIO_FOLDER_TYPE .

    Примечание. В большинстве случаев клиент должен сканировать все доступные деревья MediaBrowser, чтобы обнаружить все доступные источники для данного устройства.

    Названия групп

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

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

    • AM
    • FM
    • DAB

    Если группы нет в этом списке, тег имени группы устанавливать не следует. Однако если группа есть в списке, для нее должен быть установлен тег. Радио HD не выделяет отдельные диапазоны, поскольку использует ту же среду, что и AM/FM.

    Общие цели игры

    Каждое приложение, предназначенное для воспроизведения определенного источника (например, радио или компакт-диска), должно обрабатывать общее намерение воспроизведения , чтобы начать воспроизведение некоторого контента, возможно, из неактивного состояния (например, после загрузки). Выбор контента для воспроизведения зависит от приложения, но обычно это недавно воспроизведенная радиопрограмма или трек компакт-диска. Для каждого источника звука определяется отдельное намерение:

    • android.car.intent.action.PLAY_BROADCASTRADIO
    • android.car.intent.action.PLAY_AUDIOCD : CD-DA или CD-текст
    • android.car.intent.action.PLAY_DATADISC : оптический диск с данными, такой как CD/DVD, но не CD-DA (может быть компакт-диск смешанного режима)
    • android.car.intent.action.PLAY_AUX : без указания порта AUX.
    • android.car.intent.action.PLAY_BLUETOOTH
    • android.car.intent.action.PLAY_USB : без указания какого USB-устройства
    • android.car.intent.action.PLAY_LOCAL : локальное хранилище мультимедиа (встроенная флэш-память)

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

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

    Чтобы упростить реализацию некоторых клиентов, существует альтернативный способ выдачи такой команды Play (который также должен быть реализован радиоприложением): выдача playFromMediaId с rootId корневого узла (используется как mediaId). Хотя корневой узел не предназначен для воспроизведения, его rootId представляет собой произвольную строку, которую можно использовать как mediaId. Однако клиенты не обязаны понимать этот нюанс.

    Селектор программ

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

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

    • Первичный идентификатор. Уникальный и стабильный идентификатор данной радиостанции, который не меняется, но может оказаться недостаточным для настройки на эту станцию. Например, код RDS PI, который можно преобразовать в позывной в США.
    • Вторичные идентификаторы. Дополнительные идентификаторы, полезные для настройки на эту станцию ​​(например, частота), возможно, включая идентификаторы других радиотехнологий. Например, станция DAB может иметь резервный вариант аналогового вещания.

    Чтобы включить ProgramSelector в решение на основе MediaBrowser или MediaSession , определите схему URI для ее сериализации. Схема определяется следующим образом:

    broadcastradio://program/<primary ID type>/<primary ID>?
    <secondary ID type>=<secondary ID>&<secondary ID type>=<secondary ID>
    

    В этом примере часть вторичных идентификаторов (после вопросительного знака ( ? )) является необязательной и может быть удалена, чтобы предоставить стабильный идентификатор для использования в качестве mediaId . Например:

    • broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=88500&AMFM_FREQUENCY=103300
    • broadcastradio://program/AMFM_FREQUENCY/102100
    • broadcastradio://program/DAB_SID_EXT/14895264?RDS_PI=1234

    Авторитетная часть (также известная как хост) program предоставляет некоторые возможности для расширения схемы в будущем. Строки типа идентификатора точно указаны как их имена в определении IdentifierType HAL 2.x, а формат значения представляет собой десятичное или шестнадцатеричное (с префиксом 0x ) число.

    Все идентификаторы, специфичные для поставщика, представлены префиксом VENDOR_ . Например, VENDOR_0 для VENDOR_START и VENDOR_1 для VENDOR_START плюс 1. Такие URI относятся к радиооборудованию, на котором они были созданы, и не могут передаваться между устройствами, изготовленными разными OEM-производителями.

    Эти URI должны быть назначены каждому MediaItem в радио-папках верхнего уровня. Кроме того, MediaSession должен поддерживать как playFromMediaId , так и playFromUri . Однако URI в первую очередь предназначен для извлечения радиометаданных (например, частоты FM) и постоянного хранения. Нет никакой гарантии, что URI будет доступен для всех элементов мультимедиа (например, если основной тип идентификатора еще не поддерживается платформой). С другой стороны, Media ID работает всегда. Клиентам не рекомендуется использовать URI для выбора элементов из текущего сеанса MediaBrowser. Вместо этого используйте playFromMediaId . Тем не менее, это не является обязательным для обслуживающего приложения, и отсутствующие URI зарезервированы для вполне обоснованных случаев.

    В первоначальном проекте вместо последовательности :// после части схемы использовалось одно двоеточие. Однако первый вариант не поддерживается android.net.Uri для абсолютных иерархических ссылок URI.

    Другие типы источников

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

    Одно приложение может обслуживать несколько типов источников. В таких случаях рекомендуется создать отдельный MediaBrowserService для каждого типа источника. Даже в конфигурации с несколькими обслуживаемыми источниками/MediaBrowserServices настоятельно рекомендуется иметь один MediaSession в одном приложении.

    Аудио компакт-диск

    Аналогично Audio CD в том, что приложение, обслуживающее такие диски, предоставляет MediaBrowser одну доступную для просмотра запись (или несколько, если в системе есть CD-чейнджер), которая, в свою очередь, будет содержать все дорожки данного компакт-диска. Если у системы нет знаний о треках на каждом компакт-диске (например, когда все диски вставлены в картридж сразу и она не читает их все), то MediaItem для всего диска будет просто PLAYABLE , а не BROWSABLE плюс PLAYABLE . Если в данном слоте нет диска, элемент не будет ни PLAYABLE , ни BROWSABLE (но каждый слот всегда должен присутствовать в дереве).

    Древовидная структура аудио компакт-диска
    Рисунок 3. Древовидная структура аудио компакт-диска

    Эти записи будут помечены так же, как и папки радиовещания; они будут содержать дополнительные поля, определенные в API MediaDescription:

    • EXTRA_CD_TRACK : для каждого MediaItem на аудио компакт-диске номер дорожки начинается с 1.
    • EXTRA_CD_DISK : номер диска, отсчитываемый от 1.

    Для системы с поддержкой CD-Text и совместимого диска MediaItem верхнего уровня будет иметь название диска. Аналогично, MediaItems для дорожек будут иметь название дорожки.

    Дополнительный вход

    Приложение, которое обслуживает вспомогательный ввод, предоставляет дерево MediaBrowser с одной записью (или несколькими, если существует несколько портов), представляющей входной порт AUX. Соответствующий MediaSession принимает свой mediaId и переключается на этот источник после получения запроса playFromMediaId .

    Древовидная структура AUX
    Рисунок 4. Древовидная структура AUX

    Каждая запись AUX MediaItem будет иметь дополнительное поле EXTRA_AUX_PORT_NAME , в котором будет указано нелокализованное имя порта без фразы «AUX». Например, для «AUX 1» должно быть установлено значение «1», для «AUX front» — «front», а для «AUX» — пустая строка. В неанглоязычных локалях тег имени останется той же английской строкой. Маловероятно, как для EXTRA_BCRADIO_BAND_NAME_EN значения определяются OEM и не ограничены предопределенным списком.

    Если аппаратное обеспечение может обнаружить устройства, подключенные к порту AUX, оборудование должно пометить MediaItem как PLAYABLE , только в том случае, если ввод подключен. Аппаратное обеспечение все еще должно быть перечислено (но не PLAYABLE ), если ничего не подключено к этому порту. Если у аппаратного обеспечения нет таких возможностей, MediaItem всегда должен быть настроен на PLAYABLE .

    Дополнительные поля

    Определите следующие поля:

    • EXTRA_CD_TRACK = "android.media.extra.CD_TRACK"
    • EXTRA_CD_DISK = "android.media.extra.CD_DISK"
    • EXTRA_AUX_PORT_NAME = "android.media.extra.AUX_PORT_NAME"

    Клиенту необходимо просмотреть медиатематические средства верхнего уровня для элементов, имеющих EXTRA_CD_DISK или EXTRA_AUX_PORT_NAME Extra Extra Extrail Field.

    Подробные примеры

    Следующие примеры посвящены структуре дерева MediaBrowser для типов источников, которые являются частью этой конструкции.

    Froadcast Radio MediaBrowserService (обрабатывает ACTION_PLAY_BROADCASTRADIO ):

    • Станции (Browable) EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_PROGRAMS
      • BBC One (Playable) URI: broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=90500
      • ABC 88.1 (Playable) URI: broadcastradio://program/RDS_PI/5678?AMFM_FREQUENCY=88100
      • ABC 88.1 HD1 (Playable) URI: broadcastradio://program/HD_STATION_ID_EXT/158241DEADBEEF?AMFM_FREQUENCY=88100&RDS_PI=5678
      • ABC 88.1 HD2 (Playable) URI: broadcastradio://program/HD_STATION_ID_EXT/158242DEADBEFE
      • 90.5 FM (Playable) - FM без rdsuri: broadcastradio://program/AMFM_FREQUENCY/90500
      • 620 AM (Playable) URI: broadcastradio://program/AMFM_FREQUENCY/620
      • BBC One (Playable) URI: broadcastradio://program/DAB_SID_EXT/1E24102?RDS_PI=1234
    • Фавориты (Browbable, Playable) EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_FAVORITES
      • BBC One (Playable) URI: broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=101300
      • BBC Two (не играется) URI: broadcastradio://program/RDS_PI/1300?AMFM_FREQUENCY=102100
    • Am (Browbable, Playable): EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_BANDEXTRA_BCRADIO_BAND_NAME_EN="AM"
      • 530 AM (Playable) URI: broadcastradio://program/AMFM_FREQUENCY/530
      • 540 AM (Playable) URI: broadcastradio://program/AMFM_FREQUENCY/540
      • 550 AM (Playable) URI: broadcastradio://program/AMFM_FREQUENCY/550
    • Fm (Browbable, Playable): EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_BANDEXTRA_BCRADIO_BAND_NAME_EN="FM"
      • 87.7 FM (Playable) URI: broadcastradio://program/AMFM_FREQUENCY/87700
      • 87.9 FM (Playable) URI: broadcastradio://program/AMFM_FREQUENCY/87900
      • 88.1 FM (Playable) URI: broadcastradio://program/AMFM_FREQUENCY/88100
    • Dab (Playable): EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_BANDEXTRA_BCRADIO_BAND_NAME_EN="DAB"

    Audio CD MediaBrowserService (обрабатывает ACTION_PLAY_AUDIOCD ):

    • Диск 1 (воспроизводимый) EXTRA_CD_DISK=1
    • Disc 2 (Browsable, Playable) EXTRA_CD_DISK=2
      • Трек 1 (воспроизводимый) EXTRA_CD_TRACK=1
      • Трек 2 (воспроизводимый) EXTRA_CD_TRACK=2
    • Мой музыкальный компакт -диск (Browsable, Playable) EXTRA_CD_DISK=3
      • Все сам (играбельно) EXTRA_CD_TRACK=1
      • Reise, Reise (Playable) EXTRA_CD_TRACK=2
    • Пустой слот 4 (не воспроизводится) EXTRA_CD_DISK=4

    Aux MediabrowsbrowserService (обрабатывает ACTION_PLAY_AUX ):

    • Aux Front (Playable) EXTRA_AUX_PORT_NAME="front"
    • Aux задний (воспроизводимый) EXTRA_AUX_PORT_NAME="rear"