Implementar radio

Esta página explica cómo implementar la radio a nivel de hardware y software.

Componentes del sistema

La pila de transmisión de radio incluye los siguientes componentes.

Arquitectura de radiodifusión
Figura 1. Arquitectura de radiodifusión

Aplicación de referencia de radio

Para obtener detalles sobre cómo implementar el control de radio, consulte Implementación de control de radio .

Una aplicación de radio Java de ejemplo ( packages/apps/Car/Radio ) sirve como implementación de referencia. Cuando se inicia el servicio de la aplicación, solicita a Radio Manager que abra un sintonizador de radio. Luego, la aplicación puede enviar solicitudes al sintonizador de radio, como sintonizar una estación de radio o frecuencia específica o buscar la siguiente estación de radio disponible. La aplicación recibe actualizaciones del Administrador de radio y del Sintonizador de radio en Radio, como información del programa actual, listas de programas de radio, configuraciones y parámetros definidos por el proveedor. La aplicación Radio de referencia solo admite radio AM y FM. Los OEM pueden modificar o reemplazar la aplicación Radio según lo deseen.

Gerente de radio

Cuando la aplicación solicita a Radio Manager que abra un sintonizador, Radio Manager ( frameworks/base/core/java/android/hardware/radio/RadioManager.java ) solicita al servicio de transmisión de radio que abra una sesión de sintonizador y luego envuelve la sesión en un Radio Tuner ( frameworks/base/core/java/android/hardware/radio/RadioTuner.java ), que se devuelve a la aplicación. El sintonizador de radio define API (como sintonizar, avanzar y cancelar) a las que se puede llamar desde aplicaciones de radio y enviar solicitudes al servicio de transmisión de radio. Los métodos de devolución de llamada ( RadioTuner.Callback ) definidos en Radio Tuner envían actualizaciones sobre la transmisión de radio HAL, como información del programa actual, listas de programas y parámetros definidos por el proveedor, desde el servicio de transmisión de radio a las aplicaciones.

Servicio de transmisión de radio

El servicio de radiodifusión ( frameworks/base/services/core/java/com/android/server/broadcastradio ) es el servicio de cliente para Broadcast Radio HAL. El Servicio de Radiodifusión coordina varios administradores de radio con HAL de Radiodifusión. El servicio de transmisión de radio admite transmisiones de radio HAL en lenguaje de definición de interfaz HAL (HIDL) y en lenguaje de definición de interfaz de Android (AIDL) . El Servicio de Radiodifusión se vincula con AIDL HAL cuando existe algún servicio AIDL HAL; de lo contrario, el servicio se vincula al HIDL HAL. El servicio de transmisión de radio crea un módulo de radio para cada instancia HAL disponible (como AM, FM y DAB).

Cada Administrador de Radio puede solicitar al Servicio de Transmisión de Radio la creación de una sesión de sintonizador en el Módulo de Radio correspondiente, según el tipo de radio. Cada sesión de sintonizador puede llamar a métodos, como sintonizar, avanzar y cancelar (definidos en las interfaces HAL) para realizar operaciones en la instancia HAL de transmisión de radio correspondiente. Cuando una sesión de sintonizador recibe una devolución de llamada de la instancia de HAL en una actualización de HAL, como información del programa actual, lista de programas, indicadores de configuración y parámetros del proveedor, se envían devoluciones de llamada sobre la actualización a todos los sintonizadores de radio vinculados al mismo módulo de radio.

Radiodifusión HAL

Para obtener más información sobre las interfaces HIDL y AIDL de la radiodifusión y las diferencias entre las dos, consulte Interfaz HAL de radiodifusión .

Capa de abstracción de hardware de radiodifusión

Las siguientes secciones describen cómo trabajar con la capa de abstracción de hardware (HAL) para implementar la transmisión de radio.

Interfaz HAL de radiodifusión

Broadcast radio HAL proporciona estructuras de datos e interfaces a nivel de hardware para implementar transmisiones de radio, como radio AM/FM y DAB.

Interfaces HIDL 2.0 y AIDL

La transmisión de radio HAL utiliza las interfaces que se describen en las siguientes secciones.

Oyente de anuncios

IAnnouncementListener es la interfaz de devolución de llamada para el oyente de anuncios, que puede registrarse en la transmisión de radio HAL para recibir anuncios. La interfaz tiene los siguientes métodos:

AnuncioOyente
Descripción: Se llama cada vez que la lista de anuncios cambia.
HID 2.0 oneway onListUpdated(vec<Announcement> announcements)
AIDL oneway void onListUpdated(in Announcement[] announcements)
Cerrar manija

ICloseHandle es el identificador de cierre genérico para eliminar una devolución de llamada que no necesita una interfaz activa.

ICoseHandle
Descripción: Cierra la manija.
HID 2.0 close()
AIDL void close()

Interfaz de devolución de llamada

ITunerCallback es la interfaz de devolución de llamada llamada por la transmisión de radio HAL para enviar actualizaciones al servicio del cliente HAL.

Devolución de llamada de ITuner
Descripción: lo llama HAL cuando una operación de ajuste (sintonizar, buscar (en AIDL) o escanear (en HIDL) y el paso se realiza correctamente) falla de forma asincrónica.
HID 2.0 oneway onCurrentProgramInfoChanged(ProgramInfo info)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
Descripción: Se llama cuando sintoniza, busca (en AIDL) o escanea (en HIDL), o el paso tiene éxito.
HID 2.0 oneway onTuneFailed(Result result, ProgramSelector selector)
AIDL void onTuneFailed(in Result result, in ProgramSelector selector)
Descripción: Se llama cuando sintoniza, busca (en AIDL) o escanea (en HIDL), o el paso tiene éxito.
HID 2.0 oneway onCurrentProgramInfoChanged(ProgramInfo info)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
Descripción: Se llama cuando se actualiza la lista de programas; el tamaño de cada fragmento debe limitarse a 500 kiB.
HID 2.0 oneway onProgramListUpdated(ProgramListChunk chunk)
AIDL oneway onProgramListUpdated(ProgramListChunk chunk)
Descripción: Llamado cuando la antena está conectada o desconectada.
HID 2.0 oneway onAntennaStateChange(bool connected)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
Descripción: Se llama cuando los valores de los parámetros específicos del proveedor se actualizan internamente en HAL (no se debe invocar después de llamar setParameters por parte del cliente HAL).
HID 2.0 oneway onParametersUpdated(vec<VendorKeyValue> parameters)
AIDL void onParametersUpdated(in VendorKeyValue[] parameters)
Descripción: Nuevo en AIDL. Se llama cuando el indicador de configuración se actualiza internamente en HAL (no debe invocarse después de llamar setConfigFlag por parte del cliente HAL).
HID 2.0 No aplica.
AIDL void onConfigFlagUpdated(in ConfigFlag flag, in boolean value)

Interfaz HAL de radio de transmisión primaria

IBroadcastRadio es la interfaz principal para la transmisión de radio HAL. En HIDL 2.0 HAL, utilice la interfaz ITunerSession para que el sintonizador llame a operaciones. Sin embargo, como máximo hay un sintonizador activo a la vez (siempre que cada instancia HAL de radiodifusión solo tenga un chip sintonizador). ITunerSession se eliminó de las interfaces AIDL y sus interfaces se trasladaron a IBroadcastRadio .

IBroadcastRadio
Descripción: Obtenga la descripción de un módulo y sus capacidades.
HID 2.0 getProperties() generates (Properties properties)
AIDL Properties getProperties()
Descripción: Obtiene la configuración actual o posible de la región AM/FM.
HID 2.0 getAmFmRegionConfig(bool full) generates (Result result, AmFmRegionConfig config)
AIDL AmFmRegionConfig getAmFmRegionConfig(bool full)
Descripción: Obtiene la configuración actual de la región DAB.
HID 2.0 getDabRegionConfig() generates (Result result, vec<DabTableEntry> config)
AIDL DabTableEntry[] getDabRegionConfig()
Descripción: Obtiene una imagen del caché del módulo de radio. En AIDL, el tamaño de la imagen debe ser inferior a 1 MB debido a un límite estricto en el búfer de transacciones de la carpeta.
HID 2.0 getImage(uint32_t id) generates (vec<uint8_t> image)
AIDL byte[] getImage(in int id)
Descripción: Registra el oyente del anuncio.
HID 2.0 registerAnnouncementListener(vec<AnnouncementType> enabled,IAnnouncementListener listener) generates (Result result, ICloseHandle closeHandle)
AIDL ICloseHandle registerAnnouncementListener(in IAnnouncementListener listener, in AnnouncementType[] enabled)
Descripción:
  • HIDL HAL: Cuando se abre una nueva sesión de sintonizador, se debe finalizar la sesión anterior.
  • AIDL HAL: dado que no hay ninguna sesión de sintonizador disponible, solo es necesario configurar la devolución de llamada del sintonizador. Si existe, la antigua devolución de llamada debe anularse.
HID 2.0 openSession(ITunerCallback callback) genera (Result result, ITunerSession session)
AIDL void setTunerCallback(in ITunerCallback callback)
Descripción:
  • HIDL HAL: El cierre de una sesión de sintonizador no debe fallar y solo debe realizarse una vez.
  • AIDL HAL: No hay sintonizador y solo es necesario desarmar la devolución de llamada del sintonizador.
HID 2.0 close()
AIDL unsetTunerCallback()
Descripción: Sintoniza un programa específico.
HID 2.0 tune(ProgramSelector program) generates (Result result)
AIDL void tune(in ProgramSelector program)
Descripción: Busca el siguiente programa válido al aire . Para evitar confusiones en AIDL, se cambia el nombre scan a seek .
HID 2.0 scan(bool directionUp, bool skipSubChannel) generates (Result result)
AIDL void seek(in boolean directionUp, in boolean skipSubChannel)
Descripción: Pasos al canal adyacente, que no podrá estar ocupado por ningún programa.
HID 2.0 step(bool directionUp) generates (Result result)
AIDL void step(in boolean directionUp)
Descripción: Cancela las operaciones pendientes de sintonización, escaneo (en HIDL) o búsqueda (en AIDL) o de pasos.
HID 2.0 cancel()
AIDL void cancel()
Descripción: Aplica un filtro a la lista de programas y comienza a enviar actualizaciones de la lista de programas a través de la devolución de llamada onProgramListUpdated .
HID 2.0 startProgramListUpdates(ProgramFilter filter) generates (Result result)
AIDL void startProgramListUpdates(in ProgramFilter filter)
Descripción: Deja de enviar actualizaciones de la lista de programas.
HID 2.0 stopProgramListUpdates()
AIDL void stopProgramListUpdates()
Descripción: Obtiene la configuración actual de un indicador de configuración determinado.
HID 2.0 isConfigFlagSet(ConfigFlag flag) generates (Result result, bool value)
AIDL boolean isConfigFlagSet(in ConfigFlag flag)
Descripción: establece el indicador de configuración dado.
HID 2.0 setConfigFlag(ConfigFlag flag, bool value) generates (Result result)
AIDL void setConfigFlag(in ConfigFlag flag, boolean value)
Descripción: establece valores de parámetros específicos del proveedor.
HID 2.0 setParameters(vec<VendorKeyValue> parameters)

genera ,

(vec<VendorKeyValue> results)
AIDL VendorKeyValue[] setParameters(in VendorKeyValue[] parameters)
Descripción: recupera valores de parámetros específicos del proveedor.
HID 2.0 getParameters(vec<string> keys) generates (vec<VendorKeyValue> parameters)
AIDL VendorKeyValue[] getParameters(in String[] keys)

Aclaraciones de interfaz

Comportamiento asincrónico

Dado que cada operación de ajuste (por ejemplo, sintonizar, escanear (en HIDL) o buscar (en AIDL) y paso) puede consumir mucho tiempo y el subproceso no debe bloquearse durante mucho tiempo, la operación debe programar operaciones que consumen mucho tiempo. que ocurra más tarde y devolver rápidamente un estado o resultado. En detalle, cada operación debería:

  • Cancele todas las operaciones de ajuste pendientes.
  • Compruebe si la operación se puede procesar según las entradas del método y el estado del sintonizador.
  • Programe la tarea de ajuste y luego devuelva el Result (en HIDL) o status (en AIDL) inmediatamente. Si el Result o status es OK , se debe llamar a la devolución de llamada del sintonizador tuneFailed o currentProgramInfoChanged cuando la tarea de ajuste haya fallado (por ejemplo, debido a un tiempo de espera) o esté completa.

De manera similar, startProgramListUpdates también programa la lenta tarea de actualizar la lista de programas para que se realice más tarde y devuelva rápidamente un estado o resultado. El método primero cancela las solicitudes de actualización pendientes y luego programa la tarea de actualización y devuelve rápidamente el resultado.

Condición de carrera

Debido al comportamiento asincrónico de las operaciones de ajuste (por ejemplo, sintonizar, escanear (en HIDL) o buscar (en AIDL) y paso), existe una condición de carrera entre la cancelación de la operación y las operaciones de ajuste. Si se llama cancel después de que HAL complete una operación de ajuste y antes de que se complete la devolución de llamada, la cancelación se puede ignorar y la devolución de llamada debe completarse y ser recibida por el cliente HAL.

De manera similar, si se llama stopProgramListUpdates después de que HAL complete una actualización de la lista de programas y antes de que se complete la devolución de llamada onCurrentProgramInfoChanged , se puede ignorar stopProgramListUpdates y la devolución de llamada debería completarse.

Límite de tamaño de datos

Dado que existe un límite estricto en el búfer de transacciones de la carpeta, el límite de datos para algunos métodos de interfaz que pasan datos de un tamaño potencialmente grande se aclara en AIDL HAL.

  • getImage requiere que la imagen devuelta tenga menos de 1 MB.
  • onProgramListUpdate requiere que cada chunk tenga menos de 500 kiB. La implementación de HAL debe dividir las listas de programas más grandes en varios fragmentos y enviarlas a través de múltiples devoluciones de llamada.

Cambios en las estructuras de datos AIDL HAL

Además de los cambios en las interfaces, estos cambios se han aplicado a las estructuras de datos definidas en la transmisión de radio AIDL HAL, que aprovecha el AIDL.

  • La enumeración Constant se elimina en AIDL y se define como const int en IBroadcastRadio . Mientras tanto, ANTENNA_DISCONNECTED_TIMEOUT_MS pasa a llamarse ANTENNA_STATE_CHANGE_TIMEOUT_MS . Se agrega una nueva constante TUNER_TIMEOUT_MS . Todas las operaciones de sintonización, búsqueda y paso deben completarse dentro de este tiempo.
  • Enum RDS y Deemphasis se eliminan en AIDL y se definen como const int en AmFmRegionConfig . En consecuencia, tanto fmDeemphasis como fmRds en ProgramInfo se declaran como int, un resultado del cálculo de bits de las respectivas banderas. Mientras tanto, D50 y D75 pasan a llamarse DEEMPHASIS_D50 y DEEMPHASIS_D75 , respectivamente.
  • Enum ProgramInfoFlags se eliminan en AIDL y se definen como const int en ProgramInfo con un prefijo FLAG_ agregado. En consecuencia, infoFlags en ProgramInfo se declara como int, un resultado del cálculo de bits de las banderas. TUNED también pasa a llamarse FLAG_TUNABLE , para describir mejor su definición de que se puede sintonizar la estación.
  • En AmFmBandRange , se cambia el nombre scanSpacing a seekSpacing , ya que el nombre scan cambia a seek en AIDL.
  • Dado que el concepto de unión se introduce en AIDL, MetadataKey y Metadata definidos en HIDL HAL ya no se utilizan. Metadata de una unión AIDL se definen en AIDL HAL. Cada valor de enumeración que anteriormente estaba en MetadataKey ahora es un campo en Metadata con tipo de cadena o int, según sus definiciones.

Implementación de radiocontrol.

La implementación del control de radio se basa en MediaSession y MediaBrowse , que permiten que las aplicaciones multimedia y de asistente de voz controlen la radio. Para obtener más información, consulte Crear aplicaciones multimedia para automóviles en desarrollador.android.com.

Se proporciona una implementación del árbol de exploración de medios en la biblioteca car-broadcastradio-support en packages/apps/Car/libs . Esta biblioteca también contiene extensiones de ProgramSelector para convertir hacia y desde URI. Se recomienda que las implementaciones de radio utilicen esta biblioteca para crear el árbol de exploración asociado.

Conmutador de fuente de medios

Para proporcionar una transición fluida entre la radio y otras aplicaciones que se muestran en los medios, la biblioteca común de medios para automóviles contiene clases que deben integrarse en la aplicación de radio. MediaAppSelectorWidget se puede incluir en el XML de la aplicación de radio (el icono y el menú desplegable utilizados en las aplicaciones de radio y medios de referencia):

<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" />

Este widget inicia AppSelectionFragment , que muestra una lista de fuentes de medios a las que se puede cambiar. Si desea una interfaz de usuario distinta a la proporcionada, puede crear un widget personalizado para iniciar AppSelectionFragment cuando se debe mostrar el conmutador.

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

Se proporciona una implementación de muestra en la implementación de la aplicación de radio de referencia, ubicada en packages/apps/Car/Radio .

Especificaciones de control detalladas

La interfaz MediaSession (a través de MediaSession.Callback ) proporciona mecanismos de control para el programa de radio que se reproduce actualmente:

  • onPlay , onStop . (Des)silenciar la reproducción de radio.
  • onPause . Pausa en diferido (si es compatible).
  • onPlayFromMediaId . Reproduzca cualquier contenido de una carpeta de nivel superior. Por ejemplo, "Reproducir FM" o "Reproducir radio".
  • onPlayFromUri . Reproduce una frecuencia específica. Por ejemplo, "Reproducir 88.5 FM".
  • onSkipToNext , onSkipToPrevious . Sintonice una estación anterior o siguiente.
  • onSetRating . Agregar o eliminar desde o hacia Favoritos.

MediaBrowser expone un MediaItem ajustable en tres tipos de directorios de nivel superior:

  • ( Opcional ) Programas (estaciones). Este modo lo utilizan normalmente las radios con sintonizador dual para indicar todas las estaciones de radio sintonizables disponibles en la ubicación del usuario.
  • Favoritos. Programas de radio agregados a la lista de Favoritos, algunos pueden no estar disponibles (fuera del alcance de recepción).
  • Canales de banda. Todos los canales físicamente posibles en la región actual (87.9, 88.1, 88.3, ​​88.5, 88.7, 88.9, 89.1, etc.). Cada banda tiene un directorio de nivel superior independiente.
Estructura de árbol de MediaBrowserService
Figura 2. Estructura de árbol de MediaBrowserService

Cada elemento en cada una de estas carpetas (AM/FM/Programas) es un MediaItem con un URI que se puede usar con MediaSession para sintonizar. Cada carpeta de nivel superior (AM/FM/Programas) es un MediaItem con un mediaId que se puede usar con MediaSession para activar la reproducción y queda a discreción del OEM. Por ejemplo, "Reproducir FM", "Reproducir AM" y "Reproducir radio" son consultas de radio no específicas que utilizan un mediaId para enviar a la aplicación de radio OEM. Depende de la aplicación de radio determinar qué reproducir a partir de la solicitud genérica y el mediaId.

Sesión de medios

Dado que no existe el concepto de pausar una transmisión, las acciones Reproducir, Pausar y Detener no siempre se aplican a la radio. Con la radio, la acción Detener está asociada con silenciar la transmisión, mientras que Reproducir está asociada con eliminar el silencio.

Algunos sintonizadores de radio (o aplicaciones) brindan la capacidad de simular una pausa en la transmisión almacenando en caché el contenido y luego reproduciéndolo más tarde. En tales casos, utilice onPause .

La reproducción desde acciones mediaId y URI tiene como objetivo sintonizar una estación obtenida de la interfaz MediaBrowser. El mediaId es una cadena arbitraria proporcionada por la aplicación de radio para imponer un valor único (por lo que una ID determinada apunta a un solo elemento) y estable (por lo que un elemento determinado tiene la misma ID durante toda la sesión) con el que identificar una estación determinada. . El URI tendrá un esquema bien definido. En resumen, una forma de ProgramSelector con URI. Si bien esto preserva el atributo de unicidad, no es necesario que sea estable, aunque puede cambiar cuando la estación pasa a una frecuencia diferente.

Por diseño, no se utiliza onPlayFromSearch . Es responsabilidad del cliente (aplicación complementaria) seleccionar un resultado de búsqueda del árbol de MediaBrowser. Trasladar esa responsabilidad a la aplicación de radio aumentaría la complejidad, requeriría contratos formales sobre cómo deberían aparecer las consultas de cadena y daría como resultado una experiencia de usuario desigual en diferentes plataformas de hardware.

Nota: La aplicación de radio no contiene información adicional que sería útil para buscar el nombre de una estación que no esté expuesta al cliente a través de la interfaz MediaBrowser.

Saltar a la estación anterior o siguiente depende del contexto actual:

  • Cuando una aplicación está sintonizada en una estación de la lista de Favoritos, la aplicación puede pasar a la siguiente estación de la lista de Favoritos.
  • Escuchar una emisora ​​de la lista de programas puede provocar que se sintonice la siguiente emisora ​​disponible, ordenada según el número de canal.
  • Escuchar un canal arbitrario puede resultar en la sintonización del siguiente canal físico, incluso cuando no hay señal de transmisión.

La aplicación de radio se encarga de estas acciones.

Manejo de errores

Las acciones TransportControls (Reproducir, Detener y Siguiente) no proporcionan información sobre si la acción se realizó correctamente o no. La única forma de indicar un error es establecer el estado de MediaSession en STATE_ERROR con un mensaje de error.

La aplicación de radio debe manejar esas acciones y ejecutarlas o establecer un estado de error. Si la ejecución del comando Reproducir no es inmediata, el estado de reproducción debe cambiarse a STATE_CONNECTING (en caso de sintonización directa) o STATE_SKIPPING_TO_PREVIOUS o NEXT mientras se ejecuta el comando.

El cliente debe observar PlaybackState y verificar que la sesión cambió el programa actual a lo que se solicitó o entró en el estado de error. STATE_CONNECTING no debe exceder los 30 segundos. Sin embargo, una sintonización directa a una frecuencia AM/FM determinada debería funcionar mucho más rápido.

Agregar y eliminar favoritos

MediaSession tiene soporte de clasificación, que se puede utilizar para controlar Favoritos. onSetRating llamado con una calificación de tipo RATING_HEART agrega o elimina la estación sintonizada actualmente en o desde la lista de Favoritos.

A diferencia de los ajustes preestablecidos heredados, este modelo supone una lista de Favoritos desordenada e ilimitada, cuando cada favorito guardado se asignaba a una ranura numérica (normalmente, del 1 al 6). Como resultado, los sistemas basados ​​en ajustes preestablecidos serían incompatibles con la operación onSetRating .

La limitación de la API MediaSession es que solo se puede agregar o eliminar la estación actualmente sintonizada. Por ejemplo, los elementos deben seleccionarse primero antes de poder eliminarse. Esto es sólo una limitación del cliente MediaBrowser, como una aplicación complementaria. La aplicación de radio no tiene restricciones similares. Esta parte es opcional cuando una aplicación no admite Favoritos.

Navegador de medios

Para expresar qué frecuencias o nombres de canales físicos (cuando sintonizar un canal arbitrario es adecuado para una tecnología de radio determinada) son válidos para una región determinada, se enumeran todos los canales (frecuencias) válidos para cada banda. En la región de EE. UU., esto equivale a 101 canales de FM en el rango de 87,8 a 108,0 MHz (usando un espaciado de 0,2 MHz) y 117 canales de AM en el rango de 530 a 1700 kHz (usando un espaciado de 10 kHz). Dado que la radio HD utiliza el mismo espacio de canales, no se presenta por separado.

La lista de programas de radio disponibles actualmente es plana porque no permite esquemas de visualización como la agrupación por conjunto de transmisión directa de audio (DAB).

Es posible que las entradas de la lista de favoritos no se puedan ajustar. Por ejemplo, si un programa determinado está fuera de rango. La aplicación de radio puede detectar o no si la entrada se puede sintonizar de antemano. Si es así, es posible que no marque la entrada como reproducible.

Para identificar las carpetas de nivel superior se aplica el mismo mecanismo que utiliza Bluetooth. Es decir, un paquete Extras del objeto MediaDescription contiene un campo específico del sintonizador tal como lo hace Bluetooth con EXTRA_BT_FOLDER_TYPE . En el caso de la radiodifusión, esto lleva a definir los siguientes nuevos campos en la API pública:

  • EXTRA_BCRADIO_FOLDER_TYPE = "android.media.extra.EXTRA_BCRADIO_FOLDER_TYPE" . Uno de los siguientes valores:
    • BCRADIO_FOLDER_TYPE_PROGRAMS = 1 . Programas disponibles actualmente.
    • BCRADIO_FOLDER_TYPE_FAVORITES = 2 . Favoritos.
    • BCRADIO_FOLDER_TYPE_BAND = 3 . Todos los canales físicos para una banda determinada.

    No es necesario definir ningún campo de metadatos personalizados específicos de la radio, ya que todos los datos relevantes encajan en el esquema MediaBrowser.MediaItem existente:

    • Nombre del programa (RDS PS, nombre del servicio DAB). MediaDescription.getTitle .
    • Frecuencia FM. URI (consulte ProgramSelector ) o MediaDescription.getTitle (si una entrada está en la carpeta BROADCASTRADIO_FOLDER_TYPE_BAND ).
    • Identificadores específicos de radio (RDS PI, DAB SId). MediaDescription.getMediaUri analizado en ProgramSelector.

    Normalmente, no es necesario buscar la frecuencia FM para una entrada en el programa actual o en la lista de Favoritos (ya que el cliente debe operar con ID de medios). Sin embargo, si surgiera tal necesidad (por ejemplo, para fines de visualización), está presente en el URI y se puede analizar en ProgramSelector . Dicho esto, no se recomienda utilizar el URI para seleccionar elementos dentro de la sesión actual. Para obtener más información, consulte ProgramSelector .

    Para evitar problemas de rendimiento o relacionados con la carpeta, el servicio MediaBrowser debe admitir la paginación:

    Nota: De forma predeterminada, la paginación se implementa de forma predeterminada en la variante onLoadChildren() sin manejo de opciones.

    Las entradas relacionadas de todo tipo de listas (canales sin procesar, programas encontrados y favoritos) pueden tener diferentes mediaIds (depende de la aplicación de radio; la biblioteca de soporte los tendrá diferentes). Los URI (en formato ProgramSelector) difieren entre los canales sin formato y los programas encontrados en la mayoría de los casos (excepto FM sin RDS), pero son prácticamente los mismos entre los programas encontrados y los favoritos (excepto, por ejemplo, cuando se actualizó AF).

    Tener diferentes mediaIds para entradas de diferentes tipos de listas permite realizar diferentes acciones sobre ellas. Puede recorrer la lista de Favoritos o la lista de Todos los programas en onSkipToNext , dependiendo de la carpeta del MediaItem seleccionado recientemente (consulte MediaSession ).

    Acciones de melodía especiales

    La lista de programas permite a los usuarios sintonizar una estación específica, pero no les permite realizar solicitudes generales como "Sintonizar FM", lo que podría resultar en sintonizar una estación escuchada recientemente en la banda FM.

    Para admitir tales acciones, algunos directorios de nivel superior tienen configurado el indicador FLAG_PLAYABLE (junto con FLAG_BROWSABLE para carpetas).

    Acción Sintoniza como emitir
    Reproducir radio cualquier canal de radio startService(ACTION_PLAY_BROADCASTRADIO)

    o

    playFromMediaId(MediaBrowser. getRoot() )
    Reproducir FM Cualquier canal FM Reproduce desde el mediaId de la banda FM.

    La determinación de qué programa sintonizar depende de la aplicación. Este suele ser el canal sintonizado más recientemente de la lista proporcionada. Para obtener detalles sobre ACTION_PLAY_BROADCASTRADIO , consulte Intenciones de juego generales .

    Conexión de descubrimiento y servicio.

    PackageManager puede encontrar directamente el árbol de transmisión de radio que sirve MediaBrowserService. Para hacerlo, llame a resolveService con la intención ACTION_PLAY_BROADCASTRADIO (consulte Intenciones de reproducción generales ) y el indicador MATCH_SYSTEM_ONLY . Para buscar todos los servicios que ofrecen radio (puede haber más de uno; por ejemplo, AM/FM y satélite separados), utilice queryIntentServices .

    El servicio resuelto también maneja la intención de enlace android.media.browse.MediaBrowserService . Esto se verifica con GTS.

    Para conectarse al MediaBrowserService seleccionado, cree una instancia MediaBrowser para un componente de servicio determinado y connect . Después de establecer la conexión, se puede obtener un identificador de MediaSession a través de getSessionToken .

    La aplicación Radio puede restringir los paquetes de clientes a los que se les permite conectarse en una implementación onGetRoot de su servicio. La aplicación debería permitir que las aplicaciones del sistema se conecten sin incluirse en la lista blanca. Para obtener detalles sobre la inclusión en la lista blanca, consulte Aceptar el paquete y la firma de la aplicación Asistente .

    Si la aplicación de fuente específica (por ejemplo, una aplicación de radio) está instalada en un dispositivo sin dicho soporte de fuente, aún se anunciará como manejando la intención ACTION_PLAY_BROADCASTRADIO , pero su árbol MediaBrowser no contendrá etiquetas específicas de radio. Por lo tanto, un cliente que desee comprobar si una fuente determinada está disponible en un dispositivo debe:

    1. Descubra el servicio de radio (llame a resolveService para ACTION_PLAY_BROADCASTRADIO ).
    2. Cree MediaBrowser y luego conéctese a él.
    3. Determine la presencia de MediaItem con EXTRA_BCRADIO_FOLDER_TYPE extra.

    Nota: En la mayoría de los casos, el cliente debe escanear todos los árboles de MediaBrowser disponibles para detectar todas las fuentes disponibles para un dispositivo determinado.

    Nombres de bandas

    La lista de bandas está representada por un conjunto de directorios de nivel superior con una etiqueta de tipo de carpeta establecida en BCRADIO_FOLDER_TYPE_BAND . Los títulos de sus MediaItem son cadenas localizadas que representan nombres de bandas. En la mayoría de los casos será lo mismo que la traducción al inglés, pero el cliente no puede depender de esa suposición.

    Para proporcionar un mecanismo estable para buscar ciertas bandas, se agrega una etiqueta adicional para las carpetas de bandas, EXTRA_BCRADIO_BAND_NAME_EN . Este es un nombre no localizado de la banda y solo puede tomar uno de estos valores predefinidos:

    • AM
    • FM
    • DAB

    Si la banda no está en esta lista, no se debe configurar la etiqueta con el nombre de la banda. Sin embargo, si la banda está en la lista, debe tener una etiqueta establecida. La radio HD no enumera bandas separadas ya que utiliza el mismo medio subyacente que AM/FM.

    Intenciones generales de juego

    Cada aplicación dedicada a reproducir una fuente determinada (como radio o CD) debe manejar una intención de reproducción general para comenzar a reproducir algún contenido posiblemente desde un estado inactivo (por ejemplo, después del arranque). Depende de la aplicación cómo seleccionar el contenido para reproducir, pero generalmente es el programa de radio o la pista de CD reproducidos recientemente. Hay una intención separada definida para cada fuente de audio:

    • android.car.intent.action.PLAY_BROADCASTRADIO
    • android.car.intent.action.PLAY_AUDIOCD : CD-DA o CD-Texto
    • android.car.intent.action.PLAY_DATADISC : disco de datos óptico como CD/DVD, pero no CD-DA (puede ser un CD de modo mixto)
    • android.car.intent.action.PLAY_AUX : Sin especificar qué puerto AUX
    • android.car.intent.action.PLAY_BLUETOOTH
    • android.car.intent.action.PLAY_USB : Sin especificar qué dispositivo USB
    • android.car.intent.action.PLAY_LOCAL : almacenamiento de medios local (flash incorporado)

    Se eligieron intenciones para usarlas en el comando de reproducción general porque resuelven dos problemas a la vez: el comando de reproducción general en sí y el descubrimiento de servicios. El beneficio adicional de tener tal intención sería la posibilidad de ejecutar una acción tan simple sin abrir la sesión de MediaBrowser.

    El descubrimiento de servicios es en realidad el problema más importante que se resuelve con estos intentos. El procedimiento para el descubrimiento de servicios es fácil e inequívoco de esta manera (consulte Descubrimiento y conexión de servicios ).

    Para facilitar algunas implementaciones de clientes, existe una forma alternativa de emitir dicho comando Play (que también debe implementarse mediante la aplicación de radio): emitir playFromMediaId con el rootId del nodo raíz (usado como mediaId). Si bien el nodo raíz no está destinado a ser reproducible, su rootId es una cadena arbitraria que se puede utilizar como mediaId. Sin embargo, los clientes no están obligados a comprender este matiz.

    Selector de programa

    Si bien mediaId es suficiente para seleccionar un canal de MediaBrowserService , queda vinculado a una sesión y no es coherente entre los proveedores. En algunos casos, el cliente puede necesitar un puntero absoluto (como una frecuencia absoluta) para mantenerlo entre sesiones y dispositivos.

    En la era de las transmisiones de radio digitales, una simple frecuencia no es suficiente para sintonizar una estación específica. Por lo tanto, utilice ProgramSelector para sintonizar un canal analógico o digital. ProgramSelector consta de dos partes:

    • Identificador primario. Un identificador único y estable para una estación de radio determinada que no cambia pero que puede no ser suficiente para sintonizar esa estación. Por ejemplo, el código RDS PI, que puede traducirse al distintivo de llamada en EE. UU.
    • Identificadores secundarios. Identificadores adicionales útiles para sintonizar esa estación (por ejemplo, frecuencia), que posiblemente incluyan identificadores de otras tecnologías de radio. Por ejemplo, una estación DAB puede tener un respaldo de transmisión analógica.

    Para permitir que ProgramSelector se ajuste a la solución basada MediaBrowser o MediaSession , defina un esquema de URI para serializarlo. El esquema se define de la siguiente manera:

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

    En este ejemplo, la parte de Identificadores secundarios (después del signo de interrogación ( ? )) es opcional y se puede eliminar para proporcionar un identificador estable para usar como mediaId . Por ejemplo:

    • 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

    La parte de autoridad (también conocida como anfitrión) del program ofrece cierto margen para ampliar el plan en el futuro. Las cadenas de tipo de identificador se especifican con precisión como sus nombres en la definición HAL 2.x de IdentifierType y el formato del valor es un número decimal o hexadecimal (con prefijo 0x ).

    Todos los identificadores específicos del proveedor están representados por el prefijo VENDOR_ . Por ejemplo, VENDOR_0 para VENDOR_START y VENDOR_1 para VENDOR_START más 1. Dichos URI son específicos del hardware de radio en el que se generaron y no se pueden transferir entre dispositivos fabricados por diferentes OEM.

    Estos URI deben asignarse a cada MediaItem en las carpetas de radio de nivel superior. Además, MediaSession debe admitir tanto playFromMediaId como playFromUri . Sin embargo, el URI está destinado principalmente a la extracción de metadatos de radio (como la frecuencia de FM) y al almacenamiento persistente. No hay garantía de que el URI esté disponible para todos los elementos multimedia (por ejemplo, cuando el marco aún no admite el tipo de ID principal). Por otro lado, Media ID siempre funciona. No se recomienda que los clientes utilicen URI para seleccionar elementos de la sesión actual de MediaBrowser. En su lugar, utilice playFromMediaId . Dicho esto, no es opcional para la aplicación de servicio y los URI faltantes se reservan para casos bien justificados.

    El diseño inicial utilizó dos puntos en lugar de la secuencia :// después de la parte del esquema. Sin embargo, el primero no es compatible con android.net.Uri para referencias de URI jerárquicas absolutas.

    Otros tipos de fuentes

    Otras fuentes de audio se pueden manejar de manera similar. Por ejemplo, la entrada auxiliar y el reproductor de CD de audio.

    Una sola aplicación puede servir a varios tipos de fuentes. En tales casos, se recomienda crear un MediaBrowserService independiente para cada tipo de fuente. Incluso en una configuración con múltiples fuentes/MediaBrowserServices servidos, se recomienda encarecidamente tener una única MediaSession dentro de una sola aplicación.

    CD de audio

    Similar al CD de audio en el sentido de que la aplicación que sirve dichos discos expondría MediaBrowser con una única entrada explorable (o más, si el sistema tiene un cambiador de CD), que a su vez contendría todas las pistas de un CD determinado. Si el sistema no tiene conocimiento sobre las pistas de cada CD (por ejemplo, cuando todos los discos se insertan en un cartucho a la vez y no los lee todos), entonces MediaItem para todo el disco sería simplemente PLAYABLE , no BROWSABLE y PLAYABLE . Si no hay ningún disco en una ranura determinada, el elemento no será PLAYABLE ni BROWSABLE (pero cada ranura siempre debe estar presente en el árbol).

    Estructura de árbol de CD de audio
    Figura 3. Estructura de árbol del CD de audio

    Estas entradas se marcarían de forma similar a como lo están las carpetas de radiodifusión; contendrían campos adicionales adicionales definidos en la API MediaDescription:

    • EXTRA_CD_TRACK : para cada MediaItem en un CD de audio, número de pista basado en 1.
    • EXTRA_CD_DISK : número de disco basado en 1.

    Para sistemas habilitados para CD-Text y discos compatibles, el MediaItem de nivel superior tendría un título del disco. De manera similar, los MediaItems para pistas tendrían un título de la pista.

    Entrada auxiliar

    La aplicación que proporciona entrada auxiliar expone un árbol de MediaBrowser con una sola entrada (o más, cuando existen varios puertos) que representa el puerto de entrada AUX. La MediaSession respectiva toma su mediaId y cambia a esa fuente después de recibir la solicitud playFromMediaId .

    Estructura de árbol auxiliar
    Figura 4. Estructura de árbol AUX

    Cada entrada AUX MediaItem tendría un campo adicional EXTRA_AUX_PORT_NAME configurado para el nombre no localizado del puerto sin la frase "AUX". Por ejemplo, "AUX 1" se habría configurado en "1", "AUX front" en "front" y "AUX" en una cadena vacía. En configuraciones regionales distintas al inglés, la etiqueta de nombre seguirá siendo la misma cadena en inglés. Es poco probable que, en cuanto a EXTRA_BCRADIO_BAND_NAME_EN , los valores estén definidos por el OEM y no estén restringidos a una lista predefinida.

    Si el hardware puede detectar dispositivos conectados al puerto AUX, el hardware debe marcar el MediaItem como PLAYABLE , solo si la entrada está conectada. El hardware aún debe enumerarse (pero no PLAYABLE ) si nada se conectó a este puerto. Si el hardware no tiene tal capacidad, el MediaItem siempre debe estar configurado para PLAYABLE .

    Campos adicionales

    Defina los siguientes campos:

    • 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"

    El cliente necesita revisar los mediacciones de nivel superior para elementos que tienen el conjunto de campo adicional EXTRA_CD_DISK o EXTRA_AUX_PORT_NAME adicional.

    Ejemplos detallados

    Los siguientes ejemplos abordan la estructura del árbol MediaBrowser para los tipos de origen que forman parte de este diseño.

    Broadcast Radio MediaBrowserService (maneja ACTION_PLAY_BROADCASTRADIO ):

    • Estaciones (Browsable) 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 sin 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
    • Favoritos (Browsable, Playable) EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_FAVORITES
      • BBC One (Playable) URI: broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=101300
      • BBC Two (no jugable) URI: broadcastradio://program/RDS_PI/1300?AMFM_FREQUENCY=102100
    • AM (Browsable, 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 (Browsable, 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 (maneja ACTION_PLAY_AUDIOCD ):

    • Disco 1 (Playable) EXTRA_CD_DISK=1
    • Disco 2 (navegable, jugable) EXTRA_CD_DISK=2
      • Track 1 (Playable) EXTRA_CD_TRACK=1
      • Track 2 (Playable) EXTRA_CD_TRACK=2
    • Mi CD de música (navegable, reproducible) EXTRA_CD_DISK=3
      • Todo solo (jugable) EXTRA_CD_TRACK=1
      • Reise, Reise (Playable) EXTRA_CD_TRACK=2
    • Slot vacío 4 (no jugable) EXTRA_CD_DISK=4

    Aux MediaBrowserService (maneja ACTION_PLAY_AUX ):

    • Aux front (playable) EXTRA_AUX_PORT_NAME="front"
    • Aux tras (playable) EXTRA_AUX_PORT_NAME="rear"