Implementar Rádio

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

Componentes do sistema

A pilha de rádio de transmissão 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 aplicativo de rádio Java de amostra ( packages/apps/Car/Radio ) serve como uma implementação de referência. Quando o serviço de 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 Radio Tuner in Radio, como informações do programa atual, 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 de rádio conforme desejado.

Gerenciador 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 que o Broadcast Radio Service abra uma sessão do Tuner e, em seguida, encerra 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 para Broadcast Radio Service. Os métodos de retorno de chamada ( RadioTuner.Callback ) definidos no Radio Tuner enviam atualizações sobre o HAL de rádio transmitido, como informações do programa atual, listas de programas e parâmetros definidos pelo fornecedor, do Broadcast Radio Service para aplicativos.

Serviço de rádio de transmissão

O Broadcast Radio Service ( frameworks/base/services/core/java/com/android/server/broadcastradio ) é o serviço do cliente para Broadcast Radio HAL. O Broadcast Radio Service coordena vários Radio Managers com Broadcast Radio HALs. O serviço de transmissão de rádio oferece suporte a HAL de linguagem de definição de interface HAL (HIDL) e HALs de rádio de transmissão de linguagem de definição de interface Android (AIDL) . O Serviço de Radiodifusão conecta-se 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 Rádio de Transmissão a criação de 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 nas 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 de HAL, como informações do programa atual, lista de programas, sinalizadores de configuração e parâmetros do fornecedor, os 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 transmissão de rádio e as diferenças entre as duas, consulte Interface HAL de transmissão de rádio .

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

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

Interface HAL de rádio de transmissão

O Broadcast radio HAL fornece estruturas de dados e interfaces no nível do hardware para implementar rádio broadcast, 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úncio, que pode ser registrado na transmissão de rádio HAL para receber anúncios. A interface possui os seguintes métodos:

IAnnouncementListener
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 de cliente HAL.

ITunerCallback
Descrição: Chamado pelo HAL quando uma operação de ajuste (ajuste, 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 o ajuste, a busca (em AIDL) ou a varredura (em HIDL) ou a etapa são bem-sucedidas.
HIDL 2.0 oneway onTuneFailed(Result result, ProgramSelector selector)
AIDL void onTuneFailed(in Result result, in ProgramSelector selector)
Descrição: Chamado quando o ajuste, a busca (em AIDL) ou a varredura (em HIDL) ou a etapa são bem-sucedidas.
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 de 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: Novo em 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 HIDL 2.0 HAL, use a interface ITunerSession para o sintonizador para chamar as operações. No entanto, no máximo um sintonizador está ativo por vez (desde que cada instância HAL de rádio de transmissão tenha apenas um chip sintonizador). ITunerSession foi removido das interfaces AIDL e suas interfaces movidas para IBroadcastRadio .

IBroadcastRadio
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. Em 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 listener de 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 deve 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 callback 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 em 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 ser 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 etapas.
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 pelo 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 de 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 para 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 tarefa demorada de atualizar a lista de programas para ocorrer mais tarde e retornar rapidamente um status ou resultado. O método primeiro cancela as solicitações de atualização pendentes e, em seguida, 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 que o retorno de chamada onCurrentProgramInfoChanged seja concluído, 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 fichário, o limite de dados para alguns métodos de interface que transmitem dados de tamanho potencialmente grande é esclarecido no AIDL HAL.

  • getImage requer que a imagem retornada tenha menos de 1 MB.
  • onProgramListUpdate requer que cada chunk seja menor que 500kiB. Listas de programas maiores devem ser divididas pela implementação HAL em vários blocos e enviadas por meio de vários retornos de chamada.

Alterações nas estruturas de dados AIDL HAL

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

  • Constant enum é removido em AIDL e definido como const int em IBroadcastRadio . Enquanto isso, ANTENNA_DISCONNECTED_TIMEOUT_MS foi renomeado para ANTENNA_STATE_CHANGE_TIMEOUT_MS . Um novo int const TUNER_TIMEOUT_MS é adicionado. Todas as operações de ajuste, 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 . Correspondentemente, fmDeemphasis e fmRds em ProgramInfo são declarados como int, um resultado de computação de bit dos respectivos sinalizadores. Enquanto isso, D50 e D75 são 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 computação de bit de sinalizadores. TUNED também foi renomeado para FLAG_TUNABLE , para melhor descrever 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.
  • Uma vez que o conceito de união é introduzido em 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 de 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 mídia e assistente de 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 de ProgramSelector para converter de e para URI. Recomenda-se que as implementações de rádio usem esta biblioteca para construir a árvore de navegação associada.

Alternador 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 para o aplicativo de rádio (o ícone e o menu suspenso usados ​​nos aplicativos de mídia e rádio de referência):

<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 que podem ser alternadas. Se uma interface do usuário diferente da fornecida for desejada, você poderá criar um widget personalizado para iniciar o AppSelectionFragment quando o switcher for exibido.

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

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

Especificações de controle detalhadas

A interface MediaSession (através de MediaSession.Callback ) fornece mecanismos de controle para o programa de rádio atualmente em execução:

  • onPlay , onStop . (Des)mudo reprodução de rádio.
  • onPause . Pausa com deslocamento de tempo (se suportado).
  • 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 para 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 de 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 da 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 tem um diretório de nível superior separado.
Estrutura da árvore MediaBrowserService
Figura 2. Estrutura da árvore MediaBrowserService

Cada elemento em cada uma dessas pastas (AM/FM/Programas) é um MediaItem com um URI que pode ser usado com MediaSession para ajustar. 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, "Reproduzir FM", "Reproduzir AM" e "Reproduzir rádio" são todas 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.

MediaSession

Como não há conceito de pausar um fluxo de 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 a silenciar o stream enquanto Reproduzir está associada à remoção do mudo.

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

A reprodução das ações mediaId e URI destina-se a sintonizar uma estação buscada na interface MediaBrowser. O mediaId é uma string arbitrária fornecida pelo aplicativo de rádio para impor um valor único (portanto, um determinado ID aponta para apenas um item) e estável (portanto, um determinado item tem o mesmo ID durante toda a sessão) com o qual identificar uma determinada estação . A URI será de um esquema bem definido. Resumindo, uma forma URIizada de ProgramSelector. Embora isso preserve o atributo de unicidade, não precisa ser estável, embora possa mudar quando a estação mudar 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 um nome de estação não exposto ao cliente por meio da interface do MediaBrowser.

Saltar 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 lida com essas ações.

Manipulação de erros

As ações TransportControls (Reproduzir, Parar e Avançar) 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. Caso a execução do comando Play não seja imediata, o estado do playback deve 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 no 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.

Adicionando e removendo favoritos

MediaSession tem 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 de ou para a 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 para um slot numérico (normalmente, 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 antes de serem removidos. Esta é apenas uma limitação do cliente MediaBrowser, como um aplicativo complementar. O aplicativo de rádio não é restrito da mesma forma. Esta parte é opcional quando um aplicativo não suporta Favoritos.

MediaBrowser

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 usa o mesmo espaço de canal, ele não é apresentado separadamente.

A lista de programas de rádio atualmente disponíveis é 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 reproduzível.

Para identificar as 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 campos de metadados personalizados específicos de rádio, pois todos os dados relevantes se encaixam 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 isso, 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 diferentes mediaIds (depende do aplicativo de rádio; a biblioteca de suporte terá diferentes). Os URIs (no formato ProgramSelector) diferem entre os canais brutos e os programas encontrados na maioria dos casos (exceto para FM sem RDS), mas são praticamente os mesmos entre os 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 executar diferentes ações nelas. 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 especiais de sintonia

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

    Para oferecer suporte a essas ações, alguns diretórios de nível superior têm 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 Reproduzir a partir do mediaId da banda FM.

    A determinação de qual programa sintonizar depende do aplicativo. Normalmente, é 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 a árvore de transmissão de rádio de serviço MediaBrowserService. 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 servem rádio (pode haver mais de um; por exemplo, separar AM/FM e satélite), use queryIntentServices .

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

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

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

    Se o aplicativo específico da fonte (por exemplo, um aplicativo de rádio) for instalado em um dispositivo sem esse suporte de fonte, ele ainda se anunciará como lidando com a intenção ACTION_PLAY_BROADCASTRADIO , mas sua árvore MediaBrowser não conterá tags específicas de rádio. Assim, um cliente que deseja 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 marca de tipo de pasta definida como BCRADIO_FOLDER_TYPE_BAND . Os títulos de seus MediaItem são strings localizadas que representam nomes de bandas. Na maioria dos casos, será o mesmo que a tradução para o 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 ter um destes valores predefinidos:

    • AM
    • FM
    • DAB

    Se a banda não estiver nesta lista, a etiqueta de nome da banda não deve ser definida. No entanto, se a banda estiver na lista, ela deve ter um tag definido. O rádio HD não enumera bandas separadas, pois usa o mesmo meio subjacente do AM/FM.

    Intenções gerais de jogo

    Cada aplicativo dedicado para reproduzir determinada fonte (como rádio ou CD) deve lidar com uma intenção geral de reprodução 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 tocado recentemente ou a faixa do CD. 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 de dados ópticos como CD/DVD, mas não CD-DA (pode ser um 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 embutido)

    Intents foram escolhidos para serem usados ​​para o comando de jogo geral, porque resolvem dois problemas de uma só vez: o próprio comando de jogo geral e a descoberta de serviço. O benefício adicional de ter essa intenção seria a possibilidade de executar essa açã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 dessa maneira (consulte Descoberta e conexão de serviço ).

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

    Seletor de programa

    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 exclusivo e estável para uma determinada estação de rádio que não muda, mas pode não ser suficiente para sintonizar essa estação. Por exemplo, o código RDS PI, que pode ser traduzido para o indicativo de chamada nos EUA.
    • Identificadores secundários. Identificadores adicionais úteis para sintonizar aquela estação (por exemplo, frequência), possivelmente incluindo identificadores de outras tecnologias de rádio. Por exemplo, uma estação DAB pode ter um fallback de transmissão analógica.

    Para permitir que ProgramSelector se encaixe na solução baseada em MediaBrowser ou MediaSession , defina um esquema de 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 de autoridade (host AKA) do program oferece algum espaço para extensão do esquema no futuro. As strings de tipo de identificador são precisamente especificadas 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 isso, não é opcional para o aplicativo servidor e os URIs ausentes são reservados para casos bem justificados.

    O design inicial usava dois pontos simples 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 atender a vários tipos de fontes. Nesses casos, é recomendável criar um MediaBrowserService separado para cada tipo de origem. Mesmo em uma configuração com várias fontes servidas/MediaBrowserServices, é altamente recomendável ter uma única MediaSession em um único aplicativo.

    CD de áudio

    Semelhante ao CD de áudio, pois o aplicativo que atende a esses discos exporia o MediaBrowser com uma única entrada navegável (ou mais, se o sistema tiver uma disqueteira de CD), que por sua vez conteria todas as faixas de um determinado CD. Se o sistema não tiver o 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 os lê todos), o MediaItem para o disco inteiro seria apenas PLAYABLE , não BROWSABLE mais PLAYABLE . Se não houver disco em um determinado slot, o item não será PLAYABLE nem BROWSABLE (mas cada slot deve sempre estar presente na árvore).

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

    Essas entradas seriam marcadas de maneira semelhante às pastas de rádio de transmissão; 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 sistema habilitado para CD-Text e disco compatível, 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 atende a 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 depois de receber a solicitação playFromMediaId .

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

    Cada entrada AUX MediaItem teria um campo extra EXTRA_AUX_PORT_NAME definido para o nome não localizado da porta sem a frase "AUX". For example, "AUX 1" would have be set to "1", "AUX front" to "front" and "AUX" to an empty string. In non-English locales, the name tag would remain the same English string. Unlikely as for EXTRA_BCRADIO_BAND_NAME_EN , the values are OEM-defined and not constrained to a predefined list.

    If the hardware can detect devices connected to the AUX port, the hardware should mark the MediaItem as PLAYABLE , only if input is connected. The hardware should still be enumerated (but not PLAYABLE ) if nothing was connected to this port. If the hardware has no such capability, the MediaItem must always be set to PLAYABLE .

    Extra fields

    Define the following fields:

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

    Client needs to review the top-level MediaItems for elements having the EXTRA_CD_DISK or EXTRA_AUX_PORT_NAME extra field set.

    Detailed examples

    The following examples address the MediaBrowser tree structure for source types that are part of this design.

    Broadcast radio MediaBrowserService (handles ACTION_PLAY_BROADCASTRADIO ):

    • Stations (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 without 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
    • Favorites (browsable, playable) EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_FAVORITES
      • BBC One (playable) URI: broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=101300
      • BBC Two (not playable)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 (handles ACTION_PLAY_AUDIOCD ):

    • Disc 1 (playable) EXTRA_CD_DISK=1
    • Disc 2 (browsable, playable) EXTRA_CD_DISK=2
      • Track 1 (playable) EXTRA_CD_TRACK=1
      • Track 2 (playable) EXTRA_CD_TRACK=2
    • My music CD (browsable, playable) EXTRA_CD_DISK=3
      • All By Myself (playable) EXTRA_CD_TRACK=1
      • Reise, Reise (playable) EXTRA_CD_TRACK=2
    • Empty slot 4 (not playable) EXTRA_CD_DISK=4

    AUX MediaBrowserService (handles ACTION_PLAY_AUX ):

    • AUX front (playable) EXTRA_AUX_PORT_NAME="front"
    • AUX rear (playable) EXTRA_AUX_PORT_NAME="rear"