Implementar rádio

Esta página explica como implementar rádio nos níveis de hardware e software.

Componentes do sistema

A pilha de transmissão de rádio inclui os seguintes componentes.

Arquitetura de transmissão de rádio
Figura 1. Arquitetura de transmissão de rádio

Aplicativo de referência de rádio

Para obter detalhes sobre como implementar o controle de rádio, consulte Implementação de controle de rádio .

Um exemplo de aplicativo de rádio Java ( packages/apps/Car/Radio ) serve como uma implementação de referência. Quando o serviço do aplicativo é iniciado, ele solicita que o Radio Manager abra um sintonizador de rádio. Em seguida, o aplicativo pode enviar solicitações ao sintonizador de rádio, como sintonizar uma estação de rádio específica, frequência ou buscar a próxima estação de rádio disponível. O aplicativo recebe atualizações do Radio Manager e do Radio Tuner no Radio, como informações atuais do programa, listas de programas de rádio, configurações e parâmetros definidos pelo fornecedor. O aplicativo de rádio de referência suporta apenas rádio AM e FM. Os OEMs podem modificar ou substituir o aplicativo Radio conforme desejado.

Gerente de Rádio

Quando o aplicativo solicita que o Radio Manager abra um sintonizador, o Radio Manager ( frameworks/base/core/java/android/hardware/radio/RadioManager.java ) solicita ao Broadcast Radio Service para abrir uma sessão do sintonizador e, em seguida, envolve a sessão em um Radio Tuner ( frameworks/base/core/java/android/hardware/radio/RadioTuner.java ), que é retornado ao aplicativo. O Radio Tuner define APIs (como tune, step e cancel) que podem ser chamadas de aplicativos de rádio e enviar solicitações ao Broadcast Radio Service. Os métodos de retorno de chamada ( RadioTuner.Callback ) definidos no Radio Tuner enviam atualizações sobre o HAL de transmissão de rádio, como informações atuais do programa, listas de programas e parâmetros definidos pelo fornecedor, do serviço de transmissão de rádio para os aplicativos.

Serviço de transmissão de rádio

O Broadcast Radio Service ( frameworks/base/services/core/java/com/android/server/broadcastradio ) é o serviço cliente do Broadcast Radio HAL. O serviço de transmissão de rádio coordena vários gerentes de rádio com HALs de transmissão de rádio. O serviço de transmissão de rádio oferece suporte a HALs de rádio de transmissão de linguagem de definição de interface HAL (HIDL) e linguagem de definição de interface Android (AIDL) . O serviço de transmissão de rádio se conecta ao AIDL HAL quando existe qualquer serviço AIDL HAL; caso contrário, o serviço será vinculado ao HIDL HAL. O serviço de transmissão de rádio cria um módulo de rádio para cada instância HAL disponível (como AM, FM e DAB).

Cada Gerente de Rádio pode solicitar ao Serviço de Radiodifusão que crie uma sessão de sintonizador no Módulo de Rádio correspondente, com base no tipo de rádio. Cada sessão do sintonizador pode chamar métodos, como tune, step e cancel (definidos em interfaces HAL) para executar operações na instância HAL de rádio de transmissão correspondente. Quando uma sessão de sintonizador recebe um retorno de chamada da instância HAL em uma atualização HAL, como informações do programa atual, lista de programas, sinalizadores de configuração e parâmetros do fornecedor, retornos de chamada sobre a atualização são enviados para todos os sintonizadores de rádio vinculados ao mesmo módulo de rádio.

Transmissão de rádio HAL

Para saber mais sobre as interfaces HIDL e AIDL de rádio de transmissão e as diferenças entre as duas, consulte Interface HAL de rádio de transmissão .

Camada de abstração de hardware de rádio de transmissão

As seções a seguir descrevem como trabalhar com a camada de abstração de hardware (HAL) para implementar rádio de transmissão.

Interface HAL de rádio de transmissão

O HAL de rádio de transmissão fornece estruturas de dados e interfaces no nível de hardware para implementar rádio de transmissão, como rádio AM/FM e DAB.

Interfaces HIDL 2.0 e AIDL

A transmissão de rádio HAL usa as interfaces descritas nas seções a seguir.

Ouvinte de anúncio

IAnnouncementListener é a interface de retorno de chamada para o ouvinte de anúncios, que pode ser registrado na transmissão de rádio HAL para receber anúncios. A interface possui os seguintes métodos:

IAnouncementListener
Descrição: Chamado sempre que a lista de anúncios é alterada.
HIDL 2.0 oneway onListUpdated(vec<Announcement> announcements)
AIDL oneway void onListUpdated(in Announcement[] announcements)
Fechar alça

ICloseHandle é o identificador de fechamento genérico para remover um retorno de chamada que não precisa de uma interface ativa.

ICloseHandle
Descrição: Feche a alça.
HIDL 2.0 close()
AIDL void close()

Interface de retorno de chamada

ITunerCallback é a interface de retorno de chamada chamada pelo rádio de transmissão HAL para enviar atualizações ao serviço cliente HAL.

ITunerCallback
Descrição: Chamado pelo HAL quando uma operação de ajuste (sintonização, busca (em AIDL) ou varredura (em HIDL) e etapa é bem-sucedida) falha de forma assíncrona.
HIDL 2.0 oneway onCurrentProgramInfoChanged(ProgramInfo info)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
Descrição: Chamado quando ajuste, busca (em AIDL) ou varredura (em HIDL) ou etapa são bem-sucedidos.
HIDL 2.0 oneway onTuneFailed(Result result, ProgramSelector selector)
AIDL void onTuneFailed(in Result result, in ProgramSelector selector)
Descrição: Chamado quando ajuste, busca (em AIDL) ou varredura (em HIDL) ou etapa são bem-sucedidos.
HIDL 2.0 oneway onCurrentProgramInfoChanged(ProgramInfo info)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
Descrição: Chamado quando a lista de programas é atualizada; o tamanho de cada pedaço deve ser limitado a 500kiB.
HIDL 2.0 oneway onProgramListUpdated(ProgramListChunk chunk)
AIDL oneway onProgramListUpdated(ProgramListChunk chunk)
Descrição: Chamado quando a antena está conectada ou desconectada.
HIDL 2.0 oneway onAntennaStateChange(bool connected)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
Descrição: Chamado quando os valores dos parâmetros específicos do fornecedor são atualizados internamente no HAL (não deve ser invocado após chamar setParameters pelo cliente HAL).
HIDL 2.0 oneway onParametersUpdated(vec<VendorKeyValue> parameters)
AIDL void onParametersUpdated(in VendorKeyValue[] parameters)
Descrição: Novidade no AIDL. Chamado quando o sinalizador de configuração é atualizado internamente no HAL (não deve ser invocado após chamar setConfigFlag pelo cliente HAL).
HIDL 2.0 Não aplicável.
AIDL void onConfigFlagUpdated(in ConfigFlag flag, in boolean value)

Interface HAL de rádio de transmissão primária

IBroadcastRadio é a interface principal para a transmissão de rádio HAL. No HAL HIDL 2.0, use a interface ITunerSession para o sintonizador para chamar operações. No entanto, no máximo um sintonizador está ativo por vez (desde que cada instância HAL de transmissão de rádio tenha apenas um chip sintonizador). ITunerSession foi removido das interfaces AIDL e suas interfaces movidas para IBroadcastRadio .

IBroadcastRádio
Descrição: Obtenha a descrição de um módulo e seus recursos.
HIDL 2.0 getProperties() generates (Properties properties)
AIDL Properties getProperties()
Descrição: busca a configuração atual ou possível da região AM/FM.
HIDL 2.0 getAmFmRegionConfig(bool full) generates (Result result, AmFmRegionConfig config)
AIDL AmFmRegionConfig getAmFmRegionConfig(bool full)
Descrição: busca a configuração atual da região DAB.
HIDL 2.0 getDabRegionConfig() generates (Result result, vec<DabTableEntry> config)
AIDL DabTableEntry[] getDabRegionConfig()
Descrição: Obtém uma imagem do cache do módulo de rádio. No AIDL, o tamanho da imagem deve ser inferior a 1 MB devido a um limite rígido no buffer de transação do fichário.
HIDL 2.0 getImage(uint32_t id) generates (vec<uint8_t> image)
AIDL byte[] getImage(in int id)
Descrição: Registra o ouvinte do anúncio.
HIDL 2.0 registerAnnouncementListener(vec<AnnouncementType> enabled,IAnnouncementListener listener) generates (Result result, ICloseHandle closeHandle)
AIDL ICloseHandle registerAnnouncementListener(in IAnnouncementListener listener, in AnnouncementType[] enabled)
Descrição:
  • HIDL HAL: Quando uma nova sessão do sintonizador é aberta, a sessão antiga deve ser encerrada.
  • AIDL HAL: Como nenhuma sessão do sintonizador está disponível, apenas o retorno de chamada do sintonizador precisa ser definido. Se existir, o retorno de chamada antigo deverá ser desativado.
HIDL 2.0 openSession(ITunerCallback callback) gera (Result result, ITunerSession session)
AIDL void setTunerCallback(in ITunerCallback callback)
Descrição:
  • HIDL HAL: O fechamento de uma sessão do sintonizador não deve falhar e deve ser emitido apenas uma vez.
  • AIDL HAL: Não há sintonizador e apenas o retorno de chamada do sintonizador precisa ser desativado.
HIDL 2.0 close()
AIDL unsetTunerCallback()
Descrição: Sintoniza um programa específico.
HIDL 2.0 tune(ProgramSelector program) generates (Result result)
AIDL void tune(in ProgramSelector program)
Descrição: Busca o próximo programa válido no ar . Para evitar confusão no AIDL, scan foi renomeado para seek .
HIDL 2.0 scan(bool directionUp, bool skipSubChannel) generates (Result result)
AIDL void seek(in boolean directionUp, in boolean skipSubChannel)
Descrição: Passa para o canal adjacente, que não pode estar ocupado por nenhum programa.
HIDL 2.0 step(bool directionUp) generates (Result result)
AIDL void step(in boolean directionUp)
Descrição: Cancela operações pendentes de sintonia, varredura (em HIDL) ou busca (em AIDL) ou etapa.
HIDL 2.0 cancel()
AIDL void cancel()
Descrição: aplica um filtro à lista de programas e começa a enviar atualizações da lista de programas por meio do retorno de chamada onProgramListUpdated .
HIDL 2.0 startProgramListUpdates(ProgramFilter filter) generates (Result result)
AIDL void startProgramListUpdates(in ProgramFilter filter)
Descrição: interrompe o envio de atualizações da lista de programas.
HIDL 2.0 stopProgramListUpdates()
AIDL void stopProgramListUpdates()
Descrição: busca a configuração atual de um determinado sinalizador de configuração.
HIDL 2.0 isConfigFlagSet(ConfigFlag flag) generates (Result result, bool value)
AIDL boolean isConfigFlagSet(in ConfigFlag flag)
Descrição: Define o sinalizador de configuração fornecido.
HIDL 2.0 setConfigFlag(ConfigFlag flag, bool value) generates (Result result)
AIDL void setConfigFlag(in ConfigFlag flag, boolean value)
Descrição: Define valores de parâmetros específicos do fornecedor.
HIDL 2.0 setParameters(vec<VendorKeyValue> parameters)

gera ,

(vec<VendorKeyValue> results)
AIDL VendorKeyValue[] setParameters(in VendorKeyValue[] parameters)
Descrição: recupera valores de parâmetros específicos do fornecedor.
HIDL 2.0 getParameters(vec<string> keys) generates (vec<VendorKeyValue> parameters)
AIDL VendorKeyValue[] getParameters(in String[] keys)

Esclarecimentos sobre interface

Comportamento assíncrono

Como cada operação de ajuste (por exemplo, ajuste, varredura (em HIDL) ou busca (em AIDL) e etapa) pode ser demorada e o encadeamento não deve ser bloqueado por muito tempo, a operação deve agendar operações demoradas ocorrer mais tarde e retornar rapidamente um status ou resultado. Em detalhes, cada operação deve:

  • Cancele todas as operações de ajuste pendentes.
  • Verifique se a operação pode ser processada com base nas entradas do método e no status do sintonizador.
  • Agende a tarefa de ajuste e retorne o Result (em HIDL) ou status (em AIDL) imediatamente. Se Result ou status for OK , o retorno de chamada do sintonizador tuneFailed ou currentProgramInfoChanged deverá ser chamado quando a tarefa de ajuste falhar (por exemplo, devido a um tempo limite) ou for concluída.

Da mesma forma, startProgramListUpdates também agenda a demorada tarefa de atualização da lista de programas para ocorrer mais tarde e retornar rapidamente um status ou resultado. O método primeiro cancela solicitações de atualização pendentes e depois agenda a tarefa de atualização e retorna rapidamente o resultado.

Condição de corrida

Devido ao comportamento assíncrono das operações de ajuste (por exemplo, ajuste, varredura (em HIDL) ou busca (em AIDL) e etapa), existe uma condição de corrida entre o cancelamento da operação e as operações de ajuste. Se cancel for chamado depois que o HAL concluir uma operação de ajuste e antes que o retorno de chamada seja concluído, o cancelamento poderá ser ignorado e o retorno de chamada deverá ser concluído e recebido pelo cliente HAL.

Da mesma forma, se stopProgramListUpdates for chamado depois que o HAL concluir uma atualização da lista de programas e antes da conclusão do retorno de chamada onCurrentProgramInfoChanged , stopProgramListUpdates poderá ser ignorado e o retorno de chamada deverá ser concluído.

Limite de tamanho de dados

Como há um limite rígido no buffer de transação do binder, o limite de dados para alguns métodos de interface que passam dados de tamanho potencialmente grande são esclarecidos no AIDL HAL.

  • getImage requer que a imagem seja retornada com menos de 1 MB.
  • onProgramListUpdate exige que cada chunk tenha menos de 500kiB. Listas de programas maiores devem ser divididas pela implementação HAL em vários pedaços e enviadas através de vários retornos de chamada.

Mudanças nas estruturas de dados AIDL HAL

Além das alterações nas interfaces, essas alterações foram aplicadas às estruturas de dados definidas na transmissão de rádio AIDL HAL, que aproveita o AIDL.

  • A enumeração Constant é removida em AIDL e definida como const int em IBroadcastRadio . Enquanto isso, ANTENNA_DISCONNECTED_TIMEOUT_MS foi renomeado para ANTENNA_STATE_CHANGE_TIMEOUT_MS . Um novo const int TUNER_TIMEOUT_MS é adicionado. Todas as operações de sintonia, busca e etapa devem ser concluídas dentro desse período.
  • Enum RDS e Deemphasis são removidos em AIDL e definidos como const int em AmFmRegionConfig . Da mesma forma, fmDeemphasis e fmRds em ProgramInfo são declarados como int, um resultado de cálculo de bits dos respectivos sinalizadores. Enquanto isso, D50 e D75 foram renomeados para DEEMPHASIS_D50 e DEEMPHASIS_D75 , respectivamente.
  • Enum ProgramInfoFlags são removidos em AIDL e definidos como const int em ProgramInfo com um prefixo FLAG_ adicionado. Da mesma forma, infoFlags em ProgramInfo é declarado como int, um resultado de cálculo de bit de sinalizadores. TUNED também foi renomeado para FLAG_TUNABLE , para descrever melhor sua definição de que a estação pode ser sintonizada.
  • Em AmFmBandRange , scanSpacing é renomeado para seekSpacing , já que scan é renomeado para seek em AIDL.
  • Como o conceito de união foi introduzido no AIDL, MetadataKey e Metadata definidos em HIDL HAL não são mais usados. Um Metadata de união AIDL é definido em AIDL HAL. Cada valor enum anteriormente em MetadataKey agora é um campo em Metadata com tipo de string ou int, dependendo de suas definições.

Implementação de controle de rádio

A implementação do controle de rádio é baseada em MediaSession e MediaBrowse , que permitem que aplicativos de assistente de mídia e voz controlem o rádio. Para obter mais informações, consulte Criar aplicativos de mídia para carros em developer.android.com.

Uma implementação de árvore de navegação de mídia é fornecida na biblioteca car-broadcastradio-support em packages/apps/Car/libs . Esta biblioteca também contém extensões do ProgramSelector para converter de e para URI. É recomendado que as implementações de rádio utilizem esta biblioteca para construir a árvore de navegação associada.

Comutador de fonte de mídia

Para fornecer uma transição perfeita entre o rádio e outros aplicativos exibidos na mídia, a biblioteca car-media-common contém classes que devem ser integradas ao aplicativo de rádio. MediaAppSelectorWidget pode ser incluído no XML do aplicativo de rádio (o ícone e o menu suspenso usados ​​na mídia de referência e nos aplicativos de rádio):

<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 o AppSelectionFragment , que exibe uma lista de fontes de mídia para as quais é possível alternar. Se uma UI diferente da fornecida for desejada, você poderá criar um widget personalizado para iniciar o AppSelectionFragment quando o alternador for exibido.

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

Um exemplo de implementação é fornecido na implementação do aplicativo de rádio de referência, localizada em packages/apps/Car/Radio .

Especificações detalhadas de controle

A interface MediaSession (por meio de MediaSession.Callback ) fornece mecanismos de controle para o programa de rádio atualmente sendo reproduzido:

  • onPlay , onStop . (Des)silenciar a reprodução de rádio.
  • onPause . Pausa com mudança de tempo (se compatível).
  • onPlayFromMediaId . Reproduza qualquer conteúdo de uma pasta de nível superior. Por exemplo, “Reproduzir FM” ou “Reproduzir Rádio”.
  • onPlayFromUri . Toque uma frequência específica. Por exemplo, "Reproduzir 88,5 FM".
  • onSkipToNext , onSkipToPrevious . Sintonize uma estação seguinte ou anterior.
  • onSetRating . Adicione ou remova de ou dos Favoritos.

O MediaBrowser expõe um MediaItem ajustável em três tipos de diretórios de nível superior:

  • ( Opcional ) Programas (estações). Este modo é normalmente usado por rádios com sintonizador duplo para indicar todas as estações de rádio sintonizáveis ​​disponíveis no local do usuário.
  • Favoritos. Programas de rádio adicionados à lista de Favoritos, alguns podem estar indisponíveis (fora do alcance de recepção).
  • Canais de banda. Todos os canais fisicamente possíveis na região atual (87,9, 88,1, 88,3, 88,5, 88,7, 88,9, 89,1 e assim por diante). Cada banda possui um diretório de nível superior separado.
Estrutura em árvore do MediaBrowserService
Figura 2. Estrutura em árvore do MediaBrowserService

Cada elemento em cada uma dessas pastas (AM/FM/Programas) é um MediaItem com um URI que pode ser usado com MediaSession para ajuste. Cada pasta de nível superior (AM/FM/Programas) é um MediaItem com um mediaId que pode ser usado com MediaSession para acionar a reprodução e fica a critério do OEM. Por exemplo, "Play FM", "Play AM" e "Play Radio" são consultas de rádio não específicas que usam um mediaId para enviar ao aplicativo de rádio OEM. Cabe ao aplicativo de rádio determinar o que reproduzir a partir da solicitação genérica e do mediaId.

Sessão de mídia

Dado que não existe o conceito de pausar uma transmissão, as ações Reproduzir, Pausar e Parar nem sempre se aplicam ao rádio. Com o rádio, a ação Parar está associada ao silenciamento da transmissão, enquanto Reproduzir está associada à remoção do mudo.

Alguns sintonizadores de rádio (ou aplicativos) oferecem a capacidade de simular uma pausa na transmissão, armazenando o conteúdo em cache e reproduzindo-o mais tarde. Nesses casos, use onPause .

A reprodução de ações mediaId e URI destina-se a sintonizar uma estação obtida da interface MediaBrowser. O mediaId é uma string arbitrária fornecida pelo aplicativo de rádio para impor um valor único (para que um determinado ID aponte para apenas um item) e estável (para que um determinado item tenha o mesmo ID durante toda a sessão) para identificar uma determinada estação . O URI terá um esquema bem definido. Resumindo, uma forma URI de ProgramSelector. Embora isto preserve o atributo de unicidade, não precisa ser estável, embora possa mudar quando a estação se move para uma frequência diferente.

Por design, onPlayFromSearch não é usado. É responsabilidade do cliente (aplicativo complementar) selecionar um resultado de pesquisa na árvore do MediaBrowser. Transferir essa responsabilidade para o aplicativo de rádio aumentaria a complexidade, exigiria contratos formais sobre como as consultas de string deveriam aparecer e resultaria em uma experiência de usuário desigual em diferentes plataformas de hardware.

Nota: O aplicativo de rádio não contém informações adicionais que seriam úteis para pesquisar o nome de uma estação não exposta ao cliente através da interface do MediaBrowser.

Pular para a estação seguinte ou anterior depende do contexto atual:

  • Quando um aplicativo é sintonizado em uma estação da lista de Favoritos, o aplicativo pode passar para a próxima estação da lista de Favoritos.
  • Ouvir uma estação da lista de programas pode resultar na sintonização da próxima estação disponível, classificada de acordo com o número do canal.
  • Ouvir um canal arbitrário pode resultar na sintonização do próximo canal físico, mesmo quando não há sinal de transmissão.

O aplicativo de rádio cuida dessas ações.

Manipulação de erros

As ações TransportControls (Play, Stop e Next) não fornecem feedback sobre se a ação foi bem-sucedida ou não. A única maneira de indicar um erro é definir o estado MediaSession como STATE_ERROR com uma mensagem de erro.

O aplicativo de rádio deve lidar com essas ações e executá-las ou definir um estado de erro. Se a execução do comando Play não for imediata, o estado de reprodução deverá ser alterado para STATE_CONNECTING (no caso de sintonia direta) ou STATE_SKIPPING_TO_PREVIOUS ou NEXT enquanto o comando estiver sendo executado.

O cliente deve observar o PlaybackState e verificar se a sessão alterou o programa atual para o que foi solicitado ou entrou em estado de erro. STATE_CONNECTING não deve exceder 30s. No entanto, uma sintonia direta para uma determinada frequência AM/FM deve funcionar muito mais rápido.

Adicionar e remover favoritos

MediaSession possui suporte de classificação, que pode ser usado para controlar Favoritos. onSetRating chamado com uma classificação do tipo RATING_HEART adiciona ou remove a estação atualmente sintonizada da lista de Favoritos.

Ao contrário das predefinições herdadas, este modelo assume uma lista de Favoritos não ordenada e ilimitada, quando cada favorito salvo foi alocado em um slot numérico (normalmente, de 1 a 6). Como resultado, os sistemas baseados em predefinições seriam incompatíveis com a operação onSetRating .

A limitação da API MediaSession é que apenas a estação atualmente sintonizada pode ser adicionada ou removida. Por exemplo, os itens devem ser selecionados primeiro antes de serem removidos. Esta é apenas uma limitação do cliente MediaBrowser, como um aplicativo complementar. O aplicativo de rádio não é restrito de forma semelhante. Esta parte é opcional quando um aplicativo não oferece suporte a Favoritos.

Navegador de mídia

Para expressar quais frequências ou nomes de canais físicos (quando sintonizar um canal arbitrário é adequado para uma determinada tecnologia de rádio) são válidos para uma determinada região, todos os canais válidos (frequências) são listados para cada banda. Na região dos EUA, isso equivale a 101 canais FM na faixa de 87,8 a 108,0 MHz (usando espaçamento de 0,2 MHz) e 117 canais AM na faixa de 530 a 1700 kHz (usando espaçamento de 10 kHz). Como o rádio HD utiliza o mesmo espaço de canal, ele não é apresentado separadamente.

A lista de programas de rádio disponíveis atualmente é plana, pois não permite esquemas de exibição, como agrupamento por conjunto de transmissão direta de áudio (DAB).

As entradas na lista de favoritos podem não ser ajustáveis. Por exemplo, se um determinado programa estiver fora do intervalo. O aplicativo de rádio pode ou não detectar se a entrada pode ser sintonizada antecipadamente. Nesse caso, pode não marcar a entrada como jogável.

Para identificar pastas de nível superior, é aplicado o mesmo mecanismo usado pelo Bluetooth. Ou seja, um pacote Extras do objeto MediaDescription contém um campo específico do sintonizador, assim como o Bluetooth faz com EXTRA_BT_FOLDER_TYPE . No caso de transmissão de rádio, isso leva à definição dos seguintes novos campos na API pública:

  • EXTRA_BCRADIO_FOLDER_TYPE = "android.media.extra.EXTRA_BCRADIO_FOLDER_TYPE" . Um dos seguintes valores:
    • BCRADIO_FOLDER_TYPE_PROGRAMS = 1 . Programas atualmente disponíveis.
    • BCRADIO_FOLDER_TYPE_FAVORITES = 2 . Favoritos.
    • BCRADIO_FOLDER_TYPE_BAND = 3 . Todos os canais físicos para uma determinada banda.

    Não há necessidade de definir nenhum campo de metadados personalizado específico do rádio, pois todos os dados relevantes se enquadram no esquema MediaBrowser.MediaItem existente:

    • Nome do programa (RDS PS, nome do serviço DAB). MediaDescription.getTitle .
    • Frequência FM. URI (consulte ProgramSelector ) ou MediaDescription.getTitle (se uma entrada estiver na pasta BROADCASTRADIO_FOLDER_TYPE_BAND ).
    • Identificadores específicos de rádio (RDS PI, DAB SId). MediaDescription.getMediaUri analisado para ProgramSelector.

    Normalmente, não há necessidade de buscar a frequência FM para uma entrada no programa atual ou na lista de Favoritos (já que o cliente deve operar em IDs de mídia). No entanto, se tal necessidade surgir (por exemplo, para fins de exibição), ela estará presente no URI e poderá ser analisada para ProgramSelector . Dito isto, não é recomendado que o URI seja usado para selecionar itens na sessão atual. Para obter detalhes, consulte ProgramSelector .

    Para evitar problemas de desempenho ou relacionados ao fichário, o serviço MediaBrowser deve oferecer suporte à paginação:

    Nota: Por padrão, a paginação é implementada por padrão na variante onLoadChildren() sem manipulação de opções.

    Entradas relacionadas de todos os tipos de listas (canais brutos, programas encontrados e favoritos) podem ter mediaIds diferentes (depende do aplicativo de rádio; a biblioteca de suporte os terá diferentes). Os URIs (na forma ProgramSelector) diferem entre canais brutos e programas encontrados na maioria dos casos (exceto para FM sem RDS), mas são basicamente os mesmos entre programas encontrados e favoritos (exceto, por exemplo, quando o AF foi atualizado).

    Ter diferentes mediaIds para entradas de diferentes tipos de listas torna possível realizar diferentes ações sobre elas. Você pode percorrer a lista de Favoritos ou a lista de Todos os Programas em onSkipToNext , dependendo da pasta do MediaItem selecionado recentemente (consulte MediaSession ).

    Ações de sintonia especiais

    A lista de programas permite aos usuários sintonizar uma estação específica, mas não permite que os usuários façam solicitações gerais, como "Sintonizar FM", o que poderia resultar na sintonização de uma estação ouvida recentemente na banda FM.

    Para suportar tais ações, alguns diretórios de nível superior possuem o sinalizador FLAG_PLAYABLE definido (junto com FLAG_BROWSABLE para pastas).

    Ação Sintoniza para Como emitir
    Tocar rádio Qualquer canal de rádio startService(ACTION_PLAY_BROADCASTRADIO)

    ou

    playFromMediaId(MediaBrowser. getRoot() )
    Tocar FM Qualquer canal FM Reproduza a partir do mediaId da banda FM.

    A determinação de qual programa sintonizar depende do aplicativo. Normalmente, este é o canal sintonizado mais recentemente na lista fornecida. Para obter detalhes sobre ACTION_PLAY_BROADCASTRADIO , consulte Intenções gerais de jogo .

    Descoberta e conexão de serviço

    PackageManager pode encontrar diretamente o MediaBrowserService que serve a árvore de rádio de transmissão. Para fazer isso, chame resolveService com a intenção ACTION_PLAY_BROADCASTRADIO (consulte Intenções gerais de reprodução ) e o sinalizador MATCH_SYSTEM_ONLY . Para encontrar todos os serviços que atendem rádio (pode haver mais de um; por exemplo, AM/FM e satélite separados), use queryIntentServices .

    O serviço resolvido também lida com a intenção de ligação android.media.browse.MediaBrowserService . Isto é verificado com GTS.

    Para se conectar ao MediaBrowserService selecionado, crie uma instância MediaBrowser para um determinado componente de serviço e connect . Após estabelecer a conexão, um identificador para MediaSession pode ser obtido via getSessionToken .

    O aplicativo Radio pode restringir pacotes de clientes com permissão para se conectar em uma implementação onGetRoot de seu serviço. O aplicativo deve permitir que aplicativos do sistema se conectem sem entrar na lista de permissões. Para obter detalhes sobre a lista de permissões, consulte Aceitar o pacote e a assinatura do aplicativo Assistant .

    Se o aplicativo específico de origem (por exemplo, um aplicativo de rádio) for instalado em um dispositivo sem esse suporte de origem, ele ainda se anunciará como manipulando a intenção ACTION_PLAY_BROADCASTRADIO , mas sua árvore MediaBrowser não conterá tags específicas de rádio. Assim, um cliente que queira verificar se uma determinada fonte está disponível em um dispositivo deve:

    1. Descubra o serviço de rádio (chame resolveService para ACTION_PLAY_BROADCASTRADIO ).
    2. Crie o MediaBrowser e conecte-se a ele.
    3. Determine a presença de MediaItem com EXTRA_BCRADIO_FOLDER_TYPE extra.

    Nota: Na maioria dos casos, o cliente deve verificar todas as árvores MediaBrowser disponíveis para detectar todas as fontes disponíveis para um determinado dispositivo.

    Nomes de bandas

    A lista de bandas é representada por um conjunto de diretórios de nível superior com uma tag de tipo de pasta definida como BCRADIO_FOLDER_TYPE_BAND . Os títulos de MediaItem são strings localizadas que representam nomes de bandas. Na maioria dos casos será igual à tradução em inglês, mas o cliente não pode depender dessa suposição.

    Para fornecer um mecanismo estável para procurar certas bandas, uma tag extra é adicionada para pastas de bandas, EXTRA_BCRADIO_BAND_NAME_EN . Este é um nome não localizado da banda e só pode assumir um destes valores predefinidos:

    • AM
    • FM
    • DAB

    Se a banda não estiver nesta lista, o nome da banda não deverá ser definido. Porém, se a banda estiver na lista, ela deverá ter uma tag definida. O rádio HD não enumera bandas separadas, pois usa o mesmo meio subjacente que AM/FM.

    Intenções gerais de jogo

    Cada aplicativo dedicado à reprodução de determinada fonte (como rádio ou CD) deve lidar com uma intenção de reprodução geral para iniciar a reprodução de algum conteúdo, possivelmente do estado inativo (por exemplo, após a inicialização). Cabe ao aplicativo selecionar o conteúdo a ser reproduzido, mas geralmente é o programa de rádio ou faixa de CD reproduzido recentemente. Há uma intenção separada definida para cada fonte de áudio:

    • android.car.intent.action.PLAY_BROADCASTRADIO
    • android.car.intent.action.PLAY_AUDIOCD : CD-DA ou CD-Texto
    • android.car.intent.action.PLAY_DATADISC : disco óptico de dados como CD/DVD, mas não CD-DA (pode ser CD de modo misto)
    • android.car.intent.action.PLAY_AUX : Sem especificar qual porta AUX
    • android.car.intent.action.PLAY_BLUETOOTH
    • android.car.intent.action.PLAY_USB : Sem especificar qual dispositivo USB
    • android.car.intent.action.PLAY_LOCAL : armazenamento de mídia local (flash integrado)

    As intenções foram escolhidas para serem utilizadas no comando de jogo geral, pois resolvem dois problemas ao mesmo tempo: o próprio comando de jogo geral e a descoberta de serviço. O benefício adicional de ter tal intenção seria a possibilidade de executar uma ação tão simples sem abrir a sessão do MediaBrowser.

    A descoberta de serviço é, na verdade, o problema mais importante resolvido com essas intenções. O procedimento para descoberta de serviço é fácil e inequívoco desta forma (consulte Descoberta e conexão de serviço ).

    Para facilitar algumas implementações de clientes, existe uma forma alternativa de emitir esse comando Play (que também deve ser implementado pelo aplicativo de rádio): emitindo playFromMediaId com o rootId do nó raiz (usado como mediaId). Embora o nó raiz não seja reproduzível, seu rootId é uma string arbitrária que pode ser consumível como mediaId. No entanto, os clientes não são obrigados a compreender esta nuance.

    Seletor de programas

    Embora mediaId seja suficiente para selecionar um canal do MediaBrowserService , ele fica vinculado a uma sessão e não é consistente entre os provedores. Em alguns casos, o cliente pode precisar de um ponteiro absoluto (como uma frequência absoluta) para mantê-lo entre sessões e dispositivos.

    Na era das transmissões de rádio digital, uma frequência simples não é suficiente para sintonizar uma estação específica. Portanto, use ProgramSelector para sintonizar um canal analógico ou digital. ProgramSelector consiste em duas partes:

    • Identificador primário. Um identificador único e estável para uma determinada estação de rádio que não muda, mas pode não ser suficiente para sintonizar aquela estação. Por exemplo, código RDS PI, que pode ser traduzido para o indicativo de chamada nos EUA.
    • Identificadores secundários. Identificadores adicionais úteis para sintonizar essa estação (por exemplo, frequência), possivelmente incluindo identificadores de outras tecnologias de rádio. Por exemplo, uma estação DAB pode ter um recurso de transmissão analógica.

    Para permitir que ProgramSelector se ajuste à solução baseada em MediaBrowser ou MediaSession , defina um esquema URI para serializá-lo. O esquema é definido da seguinte forma:

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

    Neste exemplo, a parte secundária dos Identificadores (após o ponto de interrogação ( ? )) é opcional e pode ser removida para fornecer um identificador estável para uso como mediaId . Por exemplo:

    • 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

    A parte da autoridade (também conhecida como anfitriã) do program oferece algum espaço para extensão do esquema no futuro. As strings do tipo identificador são especificadas precisamente como seus nomes na definição HAL 2.x de IdentifierType e o formato do valor é um número decimal ou hexadecimal (com prefixo 0x ).

    Todos os identificadores específicos do fornecedor são representados pelo prefixo VENDOR_ . Por exemplo, VENDOR_0 para VENDOR_START e VENDOR_1 para VENDOR_START mais 1. Esses URIs são específicos do hardware de rádio no qual foram gerados e não podem ser transferidos entre dispositivos fabricados por OEMs diferentes.

    Esses URIs devem ser atribuídos a cada MediaItem nas pastas de rádio de nível superior. Além disso, o MediaSession deve oferecer suporte a playFromMediaId e playFromUri . No entanto, o URI destina-se principalmente à extração de metadados de rádio (como frequência FM) e armazenamento persistente. Não há garantia de que o URI estará disponível para todos os itens de mídia (por exemplo, quando o tipo de ID primário ainda não é suportado pela estrutura). Por outro lado, o Media ID sempre funciona. Não é recomendado que os clientes usem URI para selecionar itens da sessão atual do MediaBrowser. Em vez disso, use playFromMediaId . Dito isto, não é opcional para o aplicativo de serviço e os URIs ausentes são reservados para casos bem justificados.

    O design inicial usava dois pontos em vez da sequência :// após a parte do esquema. No entanto, o primeiro não é compatível com android.net.Uri para referências de URI hierárquicas absolutas.

    Outros tipos de fonte

    Outras fontes de áudio podem ser tratadas de forma semelhante. Por exemplo, entrada auxiliar e reprodutor de CD de áudio.

    Um único aplicativo pode servir vários tipos de fontes. Nesses casos, é recomendável criar um MediaBrowserService separado para cada tipo de fonte. Mesmo em uma configuração com múltiplas fontes/MediaBrowserServices servidos, é altamente recomendável ter uma única MediaSession em um único aplicativo.

    CD de áudio

    Semelhante ao CD de áudio, pois o aplicativo que fornece esses discos exporia o MediaBrowser com uma única entrada navegável (ou mais, se o sistema tiver um trocador de CD), que por sua vez conteria todas as faixas de um determinado CD. Se o sistema não tiver conhecimento sobre as faixas de cada CD (por exemplo, quando todos os discos são inseridos em um cartucho de uma vez e ele não lê todos eles), então MediaItem para o disco inteiro seria apenas PLAYABLE , não BROWSABLE e PLAYABLE . Se não houver disco em um determinado slot, o item não será PLAYABLE nem BROWSABLE (mas cada slot deve estar sempre presente na árvore).

    Estrutura em árvore do CD de áudio
    Figura 3. Estrutura em árvore do CD de áudio

    Essas entradas seriam marcadas de maneira semelhante às pastas de transmissão de rádio; eles conteriam campos extras adicionais definidos na API MediaDescription:

    • EXTRA_CD_TRACK : Para cada MediaItem em CD de áudio, número de faixa baseado em 1.
    • EXTRA_CD_DISK : número de disco baseado em 1.

    Para sistemas habilitados para CD-Text e discos compatíveis, o MediaItem de nível superior teria um título do disco. Da mesma forma, os MediaItems para faixas teriam um título da faixa.

    Entrada auxiliar

    O aplicativo que fornece entrada auxiliar expõe uma árvore MediaBrowser com uma única entrada (ou mais, quando existem várias portas) representando a porta AUX in. A respectiva MediaSession pega seu mediaId e muda para essa fonte após obter a solicitação playFromMediaId .

    Estrutura de árvore AUX
    Figura 4. Estrutura da árvore AUX

    Cada entrada AUX MediaItem teria um campo extra EXTRA_AUX_PORT_NAME definido como o nome não localizado da porta sem a frase "AUX". Por exemplo, "AUX 1" teria sido definido como "1", "AUX front" como "front" e "AUX" como uma string vazia. Em localidades diferentes do inglês, a tag de nome permaneceria a mesma string em inglês. Improvável como para EXTRA_BCRADIO_BAND_NAME_EN , os valores são definidos pelo OEM e não estão restritos a uma lista predefinida.

    Se o hardware puder detectar dispositivos conectados à porta AUX, o hardware deverá marcar o MediaItem como PLAYABLE , somente se a entrada estiver conectada. O hardware ainda deverá ser enumerado (mas não PLAYABLE ) se nada estiver conectado a esta porta. Se o hardware não tiver tal capacidade, o MediaItem deverá sempre ser definido como PLAYABLE .

    Campos extras

    Defina os seguintes 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"

    O cliente precisa revisar os MediaItems de nível superior em busca de elementos que tenham o campo extra EXTRA_CD_DISK ou EXTRA_AUX_PORT_NAME definido.

    Exemplos detalhados

    Os exemplos a seguir abordam a estrutura em árvore do MediaBrowser para tipos de origem que fazem parte desse design.

    Transmissão de rádio MediaBrowserService (lida com ACTION_PLAY_BROADCASTRADIO ):

    • Estações (navegáveis) EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_PROGRAMS
      • URI BBC One (jogável): broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=90500
      • ABC 88.1 (jogável) URI: broadcastradio://program/RDS_PI/5678?AMFM_FREQUENCY=88100
      • ABC 88.1 HD1 (reproduzível) URI: broadcastradio://program/HD_STATION_ID_EXT/158241DEADBEEF?AMFM_FREQUENCY=88100&RDS_PI=5678
      • ABC 88.1 HD2 (jogável) URI: broadcastradio://program/HD_STATION_ID_EXT/158242DEADBEFE
      • 90,5 FM (reproduzível) - FM sem RDSURI: broadcastradio://program/AMFM_FREQUENCY/90500
      • 620 AM (reproduzível) URI: broadcastradio://program/AMFM_FREQUENCY/620
      • URI BBC One (jogável): broadcastradio://program/DAB_SID_EXT/1E24102?RDS_PI=1234
    • Favoritos (navegáveis, reproduzíveis) EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_FAVORITES
      • URI BBC One (jogável): broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=101300
      • BBC Two (não jogável)URI: broadcastradio://program/RDS_PI/1300?AMFM_FREQUENCY=102100
    • AM (navegável, reproduzível): EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_BANDEXTRA_BCRADIO_BAND_NAME_EN="AM"
      • 530 AM (reproduzível) URI: broadcastradio://program/AMFM_FREQUENCY/530
      • 540 AM (reproduzível) URI: broadcastradio://program/AMFM_FREQUENCY/540
      • 550 AM (reproduzível) URI: broadcastradio://program/AMFM_FREQUENCY/550
    • FM (navegável, reproduzível): EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_BANDEXTRA_BCRADIO_BAND_NAME_EN="FM"
      • 87,7 FM (reproduzível) URI: broadcastradio://program/AMFM_FREQUENCY/87700
      • 87,9 FM (reproduzível) URI: broadcastradio://program/AMFM_FREQUENCY/87900
      • URI 88.1 FM (reproduzível): broadcastradio://program/AMFM_FREQUENCY/88100
    • DAB (jogável): EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_BANDEXTRA_BCRADIO_BAND_NAME_EN="DAB"

    CD de áudio MediaBrowserService (lida com ACTION_PLAY_AUDIOCD ):

    • Disco 1 (reproduzível) EXTRA_CD_DISK=1
    • Disco 2 (navegável, reproduzível) EXTRA_CD_DISK=2
      • Faixa 1 (reproduzível) EXTRA_CD_TRACK=1
      • Faixa 2 (reproduzível) EXTRA_CD_TRACK=2
    • Meu CD de música (navegável, reproduzível) EXTRA_CD_DISK=3
      • Sozinho (reproduzível) EXTRA_CD_TRACK=1
      • Reise, Reise (jogável) EXTRA_CD_TRACK=2
    • Slot 4 vazio (não reproduzível) EXTRA_CD_DISK=4

    AUX MediaBrowserService (lida com ACTION_PLAY_AUX ):

    • AUX frontal (jogável) EXTRA_AUX_PORT_NAME="front"
    • AUX traseiro (reproduzível) EXTRA_AUX_PORT_NAME="rear"