Implementare la radio

Questa pagina spiega come implementare la radio a livello hardware e software.

Componenti del sistema

Lo stack radiodiffusione include i seguenti componenti.

Architettura della radiodiffusione
Figura 1. Architettura della radiodiffusione

App di riferimento radiofonico

Per dettagli su come implementare il controllo radio, vedere Implementazione del controllo radio .

Un'app radio Java di esempio ( packages/apps/Car/Radio ) funge da implementazione di riferimento. All'avvio del servizio app, richiede a Radio Manager di aprire un sintonizzatore radio. Quindi, l'app può inviare richieste al sintonizzatore radio, ad esempio sintonizzarsi su una stazione radio o frequenza specifica o cercare la successiva stazione radio disponibile. L'app riceve aggiornamenti da Radio Manager e Radio Tuner in Radio, come informazioni sul programma corrente, elenchi di programmi radio, configurazioni e parametri definiti dal fornitore. L'app Radio di riferimento supporta solo la radio AM e FM. Gli OEM possono modificare o sostituire l'app Radio come desiderato.

Direttore radiofonico

Quando l'app richiede a Radio Manager di aprire un sintonizzatore, Radio Manager ( frameworks/base/core/java/android/hardware/radio/RadioManager.java ) richiede al servizio Broadcast Radio di aprire una sessione del sintonizzatore e quindi avvolge la sessione in un Radio Tuner ( frameworks/base/core/java/android/hardware/radio/RadioTuner.java ), che viene restituito all'app. Il sintonizzatore radio definisce le API (come tune, step e cancel) che possono essere chiamate dalle app radio e inviare richieste al servizio radiodiffusione. I metodi di richiamata ( RadioTuner.Callback ) definiti in Radio Tuner inviano aggiornamenti sull'HAL della radiodiffusione, ad esempio informazioni sul programma corrente, elenchi di programmi e parametri definiti dal fornitore, dal servizio radiodiffusione alle app.

Servizio radiodiffusione

Il servizio Broadcast Radio ( frameworks/base/services/core/java/com/android/server/broadcastradio ) è il servizio client per Broadcast Radio HAL. Il servizio Broadcast Radio coordina più gestori radio con HAL Broadcast Radio. Il servizio Broadcast Radio supporta gli HAL radiodiffusione HAL Interface Definition Language (HIDL) e Android Interface Definition Language (AIDL) . Il servizio Broadcast Radio si collega all'AIDL HAL quando esiste un servizio AIDL HAL; in caso contrario, il servizio si collega all'HIDL HAL. Il servizio radiodiffusione crea un modulo radio per ogni istanza HAL disponibile (ad esempio AM, FM e DAB).

Ogni Gestore Radio può richiedere al Servizio Radiodiffusione di creare una sessione sintonizzatore sul Modulo Radio corrispondente, in base alla tipologia di radio. Ciascuna sessione del sintonizzatore può richiamare metodi quali tune, step e cancel (definiti nelle interfacce HAL) per eseguire operazioni sull'istanza HAL della radiodiffusione corrispondente. Quando una sessione del sintonizzatore riceve una richiamata dall'istanza HAL su un aggiornamento dell'HAL, come informazioni sul programma corrente, elenco programmi, flag di configurazione e parametri del fornitore, le richiamate sull'aggiornamento vengono inviate a tutti i sintonizzatori radio collegati allo stesso modulo radio.

Trasmissione radiofonica HAL

Per ulteriori informazioni sulle interfacce HIDL e AIDL delle trasmissioni radiofoniche e sulle differenze tra le due, vedere Interfaccia HAL delle trasmissioni radiofoniche .

Livello di astrazione hardware radiodiffusione

Le sezioni seguenti descrivono come utilizzare il livello di astrazione hardware (HAL) per implementare la trasmissione radio.

Interfaccia HAL radiodiffusione

L'HAL Broadcast radio fornisce strutture dati e interfacce a livello hardware per implementare la radio broadcast, come la radio AM/FM e DAB.

Interfacce HIDL 2.0 e AIDL

L'HAL radiodiffusione utilizza le interfacce descritte nelle sezioni seguenti.

Ascoltatore di annunci

IAnnouncementListener è l'interfaccia di richiamata per l'ascoltatore di annunci, che può essere registrato sulla radio HAL per ricevere annunci. L'interfaccia dispone dei seguenti metodi:

IAnnuncioListener
Descrizione: Chiamato ogni volta che l'elenco degli annunci cambia.
HIDL 2.0 oneway onListUpdated(vec<Announcement> announcements)
AIDL oneway void onListUpdated(in Announcement[] announcements)
Chiudere la maniglia

ICloseHandle è l'handle di chiusura generico per rimuovere un callback che non necessita di un'interfaccia attiva.

IChiloseHandle
Descrizione: Chiudere la maniglia.
HIDL 2.0 close()
AIDL void close()

Interfaccia di richiamata

ITunerCallback è l'interfaccia di richiamata chiamata dall'HAL della radiodiffusione per inviare aggiornamenti al servizio client HAL.

ITunerCallback
Descrizione: chiamato dall'HAL quando un'operazione di ottimizzazione (ottimizzazione, ricerca (in AIDL) o scansione (in HIDL) e passaggio ha esito positivo) non riesce in modo asincrono.
HIDL 2.0 oneway onCurrentProgramInfoChanged(ProgramInfo info)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
Descrizione: chiamato quando la sintonizzazione, la ricerca (in AIDL) o la scansione (in HIDL) o il passaggio hanno esito positivo.
HIDL 2.0 oneway onTuneFailed(Result result, ProgramSelector selector)
AIDL void onTuneFailed(in Result result, in ProgramSelector selector)
Descrizione: chiamato quando la sintonizzazione, la ricerca (in AIDL) o la scansione (in HIDL) o il passaggio hanno esito positivo.
HIDL 2.0 oneway onCurrentProgramInfoChanged(ProgramInfo info)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
Descrizione: Chiamato quando viene aggiornata la lista dei programmi; la dimensione di ciascun blocco dovrebbe essere limitata a 500 kiB.
HIDL 2.0 oneway onProgramListUpdated(ProgramListChunk chunk)
AIDL oneway onProgramListUpdated(ProgramListChunk chunk)
Descrizione: Chiamato quando l'antenna è collegata o disconnessa.
HIDL 2.0 oneway onAntennaStateChange(bool connected)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
Descrizione: chiamato quando i valori dei parametri specifici del fornitore vengono aggiornati internamente nell'HAL (non deve essere richiamato dopo aver chiamato setParameters dal client HAL).
HIDL 2.0 oneway onParametersUpdated(vec<VendorKeyValue> parameters)
AIDL void onParametersUpdated(in VendorKeyValue[] parameters)
Descrizione: Novità in AIDL. Chiamato quando il flag di configurazione viene aggiornato internamente nell'HAL (non deve essere richiamato dopo aver chiamato setConfigFlag dal client HAL).
HIDL 2.0 Non applicabile.
AIDL void onConfigFlagUpdated(in ConfigFlag flag, in boolean value)

Interfaccia HAL radiodiffusione primaria

IBroadcastRadio è l'interfaccia principale per la trasmissione radiofonica HAL. Nell'HAL HIDL 2.0, utilizzare l'interfaccia ITunerSession del sintonizzatore per chiamare le operazioni. Tuttavia, è attivo al massimo un sintonizzatore alla volta (a condizione che ciascuna istanza HAL di trasmissione radio abbia solo un chip di sintonizzazione). ITunerSession è stato rimosso dalle interfacce AIDL e le sue interfacce sono state spostate su IBroadcastRadio .

IBroadcastRadio
Descrizione: ottieni la descrizione di un modulo e delle sue funzionalità.
HIDL 2.0 getProperties() generates (Properties properties)
AIDL Properties getProperties()
Descrizione: recupera la configurazione attuale o possibile della regione AM/FM.
HIDL 2.0 getAmFmRegionConfig(bool full) generates (Result result, AmFmRegionConfig config)
AIDL AmFmRegionConfig getAmFmRegionConfig(bool full)
Descrizione: recupera la configurazione corrente della regione DAB.
HIDL 2.0 getDabRegionConfig() generates (Result result, vec<DabTableEntry> config)
AIDL DabTableEntry[] getDabRegionConfig()
Descrizione: Ottiene un'immagine dalla cache del modulo radio. In AIDL, la dimensione dell'immagine deve essere inferiore a 1 MB a causa di un limite rigido sul buffer delle transazioni del raccoglitore.
HIDL 2.0 getImage(uint32_t id) generates (vec<uint8_t> image)
AIDL byte[] getImage(in int id)
Descrizione: Registra l'ascoltatore dell'annuncio.
HIDL 2.0 registerAnnouncementListener(vec<AnnouncementType> enabled,IAnnouncementListener listener) generates (Result result, ICloseHandle closeHandle)
AIDL ICloseHandle registerAnnouncementListener(in IAnnouncementListener listener, in AnnouncementType[] enabled)
Descrizione:
  • HIDL HAL: quando viene aperta una nuova sessione del sintonizzatore, la vecchia sessione deve essere terminata.
  • AIDL HAL: poiché non è disponibile alcuna sessione del sintonizzatore, è necessario impostare solo la richiamata del sintonizzatore. Se esiste, la vecchia richiamata dovrebbe essere disattivata.
HIDL 2.0 openSession(ITunerCallback callback) genera (Result result, ITunerSession session)
AIDL void setTunerCallback(in ITunerCallback callback)
Descrizione:
  • HIDL HAL: la chiusura di una sessione del sintonizzatore non deve fallire e deve essere eseguita una sola volta.
  • AIDL HAL: non è presente alcun sintonizzatore e solo la richiamata del sintonizzatore deve essere disattivata.
HIDL 2.0 close()
AIDL unsetTunerCallback()
Descrizione: Si sintonizza su un programma specifico.
HIDL 2.0 tune(ProgramSelector program) generates (Result result)
AIDL void tune(in ProgramSelector program)
Descrizione: Cerca il prossimo programma valido in onda . Per evitare confusione in AIDL, scan viene rinominata in seek .
HIDL 2.0 scan(bool directionUp, bool skipSubChannel) generates (Result result)
AIDL void seek(in boolean directionUp, in boolean skipSubChannel)
Descrizione: Passa al canale adiacente, che non può essere occupato da nessun programma.
HIDL 2.0 step(bool directionUp) generates (Result result)
AIDL void step(in boolean directionUp)
Descrizione: annulla le operazioni di sintonizzazione, scansione (in HIDL) o ricerca (in AIDL) o passaggi in sospeso.
HIDL 2.0 cancel()
AIDL void cancel()
Descrizione: applica un filtro all'elenco dei programmi e inizia a inviare gli aggiornamenti dell'elenco dei programmi tramite la richiamata onProgramListUpdated .
HIDL 2.0 startProgramListUpdates(ProgramFilter filter) generates (Result result)
AIDL void startProgramListUpdates(in ProgramFilter filter)
Descrizione: interrompe l'invio degli aggiornamenti dell'elenco dei programmi.
HIDL 2.0 stopProgramListUpdates()
AIDL void stopProgramListUpdates()
Descrizione: recupera l'impostazione corrente di un determinato flag di configurazione.
HIDL 2.0 isConfigFlagSet(ConfigFlag flag) generates (Result result, bool value)
AIDL boolean isConfigFlagSet(in ConfigFlag flag)
Descrizione: imposta il flag di configurazione specificato.
HIDL 2.0 setConfigFlag(ConfigFlag flag, bool value) generates (Result result)
AIDL void setConfigFlag(in ConfigFlag flag, boolean value)
Descrizione: imposta i valori dei parametri specifici del fornitore.
HIDL 2.0 setParameters(vec<VendorKeyValue> parameters)

genera ,

(vec<VendorKeyValue> results)
AIDL VendorKeyValue[] setParameters(in VendorKeyValue[] parameters)
Descrizione: recupera i valori dei parametri specifici del fornitore.
HIDL 2.0 getParameters(vec<string> keys) generates (vec<VendorKeyValue> parameters)
AIDL VendorKeyValue[] getParameters(in String[] keys)

Chiarimenti sull'interfaccia

Comportamento asincrono

Poiché ciascuna operazione di ottimizzazione (ad esempio, ottimizzazione, scansione (in HIDL) o ricerca (in AIDL) e passaggio) potrebbe richiedere molto tempo e il thread non deve essere bloccato per un lungo periodo, l'operazione dovrebbe pianificare operazioni che richiedono molto tempo verificarsi in seguito e restituire rapidamente uno stato o un risultato. Nel dettaglio ogni operazione dovrà:

  • Annulla tutte le operazioni di ottimizzazione in sospeso.
  • Verificare se l'operazione può essere elaborata in base agli input del metodo e allo stato del sintonizzatore.
  • Pianificare l'attività di ottimizzazione e quindi restituire immediatamente il Result (in HIDL) o status (in AIDL). Se il Result o status è OK , è necessario richiamare la richiamata del sintonizzatore tuneFailed o currentProgramInfoChanged quando l'attività di ottimizzazione non è riuscita (ad esempio a causa di un timeout) o è stata completata.

Allo stesso modo, startProgramListUpdates pianifica anche l'attività dispendiosa in termini di tempo di aggiornamento dell'elenco dei programmi in modo che avvenga in un secondo momento e restituisca rapidamente uno stato o un risultato. Il metodo annulla innanzitutto le richieste di aggiornamento in sospeso, quindi pianifica l'attività di aggiornamento e restituisce rapidamente il risultato.

Condizione di gara

A causa del comportamento asincrono delle operazioni di ottimizzazione (ad esempio, ottimizzazione, scansione (in HIDL) o ricerca (in AIDL) e passaggio), esiste una condizione di competizione tra l'annullamento dell'operazione e le operazioni di ottimizzazione. Se cancel viene chiamato dopo che l'HAL ha completato un'operazione di ottimizzazione e prima del completamento della richiamata, l'annullamento può essere ignorato e la richiamata dovrebbe essere completata ed essere ricevuta dal client HAL.

Allo stesso modo, se stopProgramListUpdates viene chiamato dopo che l'HAL ha completato un aggiornamento dell'elenco di programmi e prima del completamento della richiamata onCurrentProgramInfoChanged , stopProgramListUpdates può essere ignorato e la richiamata dovrebbe essere completata.

Limite della dimensione dei dati

Poiché esiste un limite rigido sul buffer delle transazioni del raccoglitore, il limite dei dati per alcuni metodi di interfaccia che passano dati di dimensioni potenzialmente grandi viene chiarito nell'AIDL HAL.

  • getImage richiede che l'immagine restituita sia inferiore a 1 MB.
  • onProgramListUpdate richiede che ogni chunk sia inferiore a 500 kiB. Gli elenchi di programmi più grandi devono essere suddivisi dall'implementazione HAL in più blocchi e inviati tramite più callback.

Modifiche nelle strutture dati dell'HAL AIDL

Oltre ai cambiamenti nelle interfacce, questi cambiamenti sono stati applicati alle strutture dati definite nella trasmissione radiofonica AIDL HAL, che sfrutta AIDL.

  • L'enumerazione Constant viene rimossa in AIDL e definita come const int in IBroadcastRadio . Nel frattempo, ANTENNA_DISCONNECTED_TIMEOUT_MS viene rinominato in ANTENNA_STATE_CHANGE_TIMEOUT_MS . Viene aggiunto un nuovo const int TUNER_TIMEOUT_MS . Tutte le operazioni di sintonizzazione, ricerca e step devono essere completate entro questo tempo.
  • Enum RDS e Deemphasis vengono rimossi in AIDL e definiti come const int in AmFmRegionConfig . Di conseguenza, sia fmDeemphasis che fmRds in ProgramInfo sono dichiarati come int, un risultato di calcolo in bit dei rispettivi flag. Nel frattempo, D50 e D75 vengono rinominati DEEMPHASIS_D50 e DEEMPHASIS_D75 , rispettivamente.
  • Gli Enum ProgramInfoFlags vengono rimossi in AIDL e definiti come const int in ProgramInfo con l'aggiunta del prefisso FLAG_ . Di conseguenza, infoFlags in ProgramInfo è dichiarato come int, un risultato di calcolo dei bit dei flag. Anche TUNED è stato rinominato FLAG_TUNABLE , per descrivere meglio la definizione su cui la stazione può essere sintonizzata.
  • In AmFmBandRange , scanSpacing viene rinominato in seekSpacing , poiché scan viene rinominata in seek in AIDL.
  • Da quando il concetto di unione è stato introdotto in AIDL, MetadataKey e Metadata definiti in HIDL HAL non vengono più utilizzati. I Metadata dell'unione AIDL sono definiti nell'HAL AIDL. Ogni valore enum precedentemente in MetadataKey è ora un campo in Metadata con tipo di stringa o int, a seconda delle relative definizioni.

Implementazione del radiocomando

L'implementazione del controllo radio si basa su MediaSession e MediaBrowse , che consentono alle app multimediali e di assistente vocale di controllare la radio. Per ulteriori informazioni, consulta Creare app multimediali per auto su Developer.android.com.

Un'implementazione dell'albero di navigazione multimediale è fornita nella libreria car-broadcastradio-support in packages/apps/Car/libs . Questa libreria contiene anche estensioni di ProgramSelector per la conversione da e verso URI. Si consiglia che le implementazioni radio utilizzino questa libreria per creare l'albero di navigazione associato.

Commutatore di sorgenti multimediali

Per garantire una transizione fluida tra la radio e le altre app visualizzate nei media, la libreria car-media-common contiene classi che dovrebbero essere integrate nell'app radio. MediaAppSelectorWidget può essere incluso nell'XML per l'app radio (l'icona e il menu a discesa utilizzati nelle app multimediali e radio di riferimento):

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

Questo widget avvia AppSelectionFragment , che visualizza un elenco di fonti multimediali a cui è possibile passare. Se si desidera un'interfaccia utente diversa da quella fornita, è possibile creare un widget personalizzato per avviare AppSelectionFragment quando deve essere visualizzato lo switcher.

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

Un'implementazione di esempio viene fornita nell'implementazione dell'app radio di riferimento, disponibile in packages/apps/Car/Radio .

Specifiche di controllo dettagliate

L'interfaccia MediaSession (tramite MediaSession.Callback ) fornisce meccanismi di controllo per il programma radiofonico attualmente in riproduzione:

  • onPlay , onStop . (Dis)disattiva la riproduzione della radio.
  • onPause . Pausa spostata nel tempo (se supportata).
  • onPlayFromMediaId . Riproduci qualsiasi contenuto da una cartella di livello superiore. Ad esempio, "Riproduci FM" o "Riproduci radio".
  • onPlayFromUri . Riproduci una frequenza specifica. Ad esempio, "Riproduci 88.5 FM".
  • onSkipToNext , onSkipToPrevious . Sintonizzarsi su una stazione successiva o precedente.
  • onSetRating . Aggiungi o rimuovi ai o dai Preferiti.

MediaBrowser espone un MediaItem sintonizzabile su tre tipi di directory di primo livello:

  • ( Opzionale ) Programmi (stazioni). Questa modalità viene generalmente utilizzata dalle radio con doppio sintonizzatore per indicare tutte le stazioni radio sintonizzabili disponibili nella posizione dell'utente.
  • Preferiti. Programmi radiofonici aggiunti all'elenco dei preferiti, alcuni potrebbero non essere disponibili (fuori portata di ricezione).
  • Canali della banda. Tutti i canali fisicamente possibili nella regione corrente (87.9, 88.1, 88.3, ​​88.5, 88.7, 88.9, 89.1 e così via). Ogni banda ha una directory di primo livello separata.
Struttura ad albero MediaBrowserService
Figura 2. Struttura ad albero MediaBrowserService

Ogni elemento in ciascuna di queste cartelle (AM/FM/Programmi) è un MediaItem con un URI che può essere utilizzato con MediaSession per ottimizzare. Ogni cartella di primo livello (AM/FM/Programmi) è un MediaItem con un mediaId che può essere utilizzato con MediaSession per attivare la riproduzione e dipende dalla discrezione dell'OEM. Ad esempio, "Riproduci FM", "Riproduci AM" e "Riproduci radio" sono tutte query radio non specifiche che utilizzano un mediaId per l'invio all'app radio OEM. Spetta all'app radio determinare cosa riprodurre dalla richiesta generica e dal mediaId.

MediaSession

Dato che non esiste il concetto di mettere in pausa un flusso di trasmissione, le azioni Riproduci, Pausa e Interrompi non si applicano sempre alla radio. Con la radio, l'azione Interrompi è associata alla disattivazione dell'audio dello streaming, mentre la Riproduzione è associata alla rimozione della disattivazione.

Alcuni sintonizzatori radio (o app) offrono la possibilità di simulare una pausa nel flusso di trasmissione memorizzando nella cache il contenuto e quindi riproducendolo in un secondo momento. In questi casi, utilizzare onPause .

La riproduzione dalle azioni mediaId e URI ha lo scopo di sintonizzarsi su una stazione recuperata dall'interfaccia MediaBrowser. Il mediaId è una stringa arbitraria fornita dall'app radio per imporre un valore univoco (quindi un dato ID punta a un solo elemento) e stabile (quindi un dato elemento ha lo stesso ID per tutta la sessione) con cui identificare una determinata stazione . L'URI avrà uno schema ben definito. In breve, una forma URI di ProgramSelector. Sebbene ciò preservi l'attributo di unicità, non è necessario che sia stabile, sebbene possa cambiare quando la stazione si sposta su una frequenza diversa.

In base alla progettazione, onPlayFromSearch non viene utilizzato. È responsabilità del client (app complementare) selezionare un risultato di ricerca dall'albero MediaBrowser. Trasferire tale responsabilità all'app radio aumenterebbe la complessità, richiederebbe contratti formali su come dovrebbero apparire le query sulle stringhe e comporterebbe un'esperienza utente non uniforme su diverse piattaforme hardware.

Nota: l'app radio non contiene informazioni aggiuntive utili per cercare il nome di una stazione non esposta al client tramite l'interfaccia MediaBrowser.

Il passaggio alla stazione successiva o precedente dipende dal contesto attuale:

  • Quando un'app è sintonizzata su una stazione dall'elenco dei Preferiti, l'app può passare alla stazione successiva dall'elenco dei Preferiti.
  • L'ascolto di una stazione dall'elenco dei programmi può comportare la sintonizzazione sulla successiva stazione disponibile, ordinata in base al numero del canale.
  • L'ascolto di un canale arbitrario potrebbe comportare la sintonizzazione sul canale fisico successivo, anche in assenza di segnale di trasmissione.

L'app radio gestisce queste azioni.

Gestione degli errori

Le azioni TransportControls (Riproduci, Interrompi e Successivo) non forniscono feedback sulla riuscita o meno dell'azione. L'unico modo per indicare un errore è impostare lo stato MediaSession su STATE_ERROR con un messaggio di errore.

L'app radio deve gestire tali azioni ed eseguirle o impostare uno stato di errore. Se l'esecuzione del comando Play non è immediata, lo stato di riproduzione deve essere modificato in STATE_CONNECTING (in caso di sintonizzazione diretta) o STATE_SKIPPING_TO_PREVIOUS o NEXT mentre il comando viene eseguito.

Il client deve osservare PlaybackState e verificare che la sessione abbia modificato il programma corrente in quanto richiesto o inserito nello stato di errore. STATE_CONNECTING non deve superare i 30 s. Tuttavia, una sintonizzazione diretta su una determinata frequenza AM/FM dovrebbe funzionare molto più velocemente.

Aggiungi e rimuovi i preferiti

MediaSession dispone del supporto per la classificazione, che può essere utilizzato per controllare i Preferiti. onSetRating chiamato con una valutazione di tipo RATING_HEART aggiunge o rimuove la stazione attualmente sintonizzata da o verso l'elenco dei preferiti.

Contrariamente alle preimpostazioni legacy, questo modello presuppone un elenco di Preferiti non ordinato e illimitato, in cui ciascun preferito salvato è stato assegnato a uno slot numerico (tipicamente da 1 a 6). Di conseguenza, i sistemi basati su preimpostazioni sarebbero incompatibili con il funzionamento onSetRating .

La limitazione dell'API MediaSession è che è possibile aggiungere o rimuovere solo la stazione attualmente sintonizzata. Ad esempio, gli elementi devono essere selezionati prima di poter essere rimossi. Questa è solo una limitazione del client MediaBrowser, come un'app complementare. L'app radio non ha le stesse limitazioni. Questa parte è facoltativa quando un'app non supporta i Preferiti.

MediaBrowser

Per esprimere quali frequenze o nomi di canali fisici (quando la sintonizzazione su un canale arbitrario è adatto per una determinata tecnologia radio) sono validi per una determinata regione, tutti i canali validi (frequenze) sono elencati per ciascuna banda. Nella regione degli Stati Uniti, ciò equivale a 101 canali FM nell'intervallo da 87,8 a 108,0 MHz (utilizzando una spaziatura di 0,2 MHz) e 117 canali AM nell'intervallo da 530 a 1700 kHz (utilizzando una spaziatura di 10 kHz). Poiché la radio HD utilizza lo stesso spazio di canali, non viene presentata separatamente.

L'elenco dei programmi radiofonici attualmente disponibili è piatto in quanto non consente schemi di visualizzazione come il raggruppamento per ensemble di trasmissione audio diretta (DAB).

Le voci nell'elenco dei preferiti potrebbero non essere sintonizzabili. Ad esempio se un determinato programma è fuori portata. L'app radio potrebbe rilevare o meno se la voce può essere sintonizzata in anticipo. In tal caso, potrebbe non contrassegnare la voce come riproducibile.

Per identificare le cartelle di livello superiore viene applicato lo stesso meccanismo utilizzato da Bluetooth. Cioè, un pacchetto Extra dell'oggetto MediaDescription contiene un campo specifico del sintonizzatore proprio come fa Bluetooth con EXTRA_BT_FOLDER_TYPE . Nel caso delle trasmissioni radiofoniche ciò porta a definire nelle API pubbliche i seguenti nuovi campi:

  • EXTRA_BCRADIO_FOLDER_TYPE = "android.media.extra.EXTRA_BCRADIO_FOLDER_TYPE" . Uno dei seguenti valori:
    • BCRADIO_FOLDER_TYPE_PROGRAMS = 1 . Programmi attualmente disponibili.
    • BCRADIO_FOLDER_TYPE_FAVORITES = 2 . Preferiti.
    • BCRADIO_FOLDER_TYPE_BAND = 3 . Tutti i canali fisici per una determinata banda.

    Non è necessario definire campi di metadati personalizzati specifici della radio, poiché tutti i dati rilevanti rientrano nello schema MediaBrowser.MediaItem esistente:

    • Nome del programma (RDS PS, nome del servizio DAB). MediaDescription.getTitle .
    • Frequenza FM. URI (vedi ProgramSelector ) o MediaDescription.getTitle (se una voce si trova nella cartella BROADCASTRADIO_FOLDER_TYPE_BAND ).
    • Identificatori radio specifici (RDS PI, DAB SId). MediaDescription.getMediaUri analizzato in ProgramSelector.

    In genere, non è necessario recuperare la frequenza FM per una voce nel programma corrente o nell'elenco dei preferiti (poiché il client dovrebbe operare su ID multimediali). Tuttavia, se tale necessità dovesse verificarsi (ad esempio, per scopi di visualizzazione), è presente nell'URI e può essere analizzata in ProgramSelector . Detto questo, non è consigliabile utilizzare l'URI per selezionare elementi all'interno della sessione corrente. Per i dettagli, vedere ProgramSelector .

    Per evitare problemi di prestazioni o relativi al raccoglitore, il servizio MediaBrowser deve supportare l'impaginazione:

    Nota: per impostazione predefinita, l'impaginazione è implementata nella variante onLoadChildren() senza gestione delle opzioni.

    Le voci correlate da tutti i tipi di elenchi (canali grezzi, programmi trovati e preferiti) possono avere mediaId diversi (dipende dall'app radio; la libreria di supporto li avrà diversi). Gli URI (nella forma ProgramSelector) differiscono tra i canali grezzi e i programmi trovati nella maggior parte dei casi (eccetto FM senza RDS), ma sono per lo più gli stessi tra i programmi trovati e i preferiti (tranne, ad esempio, quando AF è stato aggiornato).

    Avere mediaId diversi per voci provenienti da diversi tipi di elenchi rende possibile intraprendere azioni diverse su di essi. È possibile scorrere l'elenco dei Preferiti o l'elenco Tutti i programmi su onSkipToNext , a seconda della cartella del MediaItem recentemente selezionato (vedere MediaSession ).

    Azioni di sintonizzazione speciali

    L'elenco dei programmi consente agli utenti di sintonizzarsi su una stazione specifica, ma non consente agli utenti di effettuare richieste generali come "Sintonizzati su FM", che potrebbero comportare la sintonizzazione su una stazione ascoltata di recente sulla banda FM.

    Per supportare tali azioni, alcune directory di livello superiore hanno il flag FLAG_PLAYABLE impostato (insieme a FLAG_BROWSABLE per le cartelle).

    Azione Sintonizzati su Come emettere
    Ascolta la radio Qualsiasi canale radiofonico startService(ACTION_PLAY_BROADCASTRADIO)

    O

    playFromMediaId(MediaBrowser. getRoot() )
    Ascolta FM Qualsiasi canale FM Riproduci dal mediaId della banda FM.

    La decisione su quale programma sintonizzarsi spetta all'app. Solitamente si tratta del canale sintonizzato più recentemente dall'elenco fornito. Per dettagli su ACTION_PLAY_BROADCASTRADIO , vedere Intenti di gioco generali .

    Individuazione e connessione al servizio

    PackageManager può trovare direttamente l'albero delle radiodiffusioni che serve MediaBrowserService. Per fare ciò, resolveService con l'intento ACTION_PLAY_BROADCASTRADIO (vedi Intenti di gioco generali ) e il flag MATCH_SYSTEM_ONLY . Per trovare tutti i servizi che servono la radio (potrebbero essercene più di uno; ad esempio, AM/FM e satellite separati), utilizzare queryIntentServices .

    Il servizio risolto gestisce anche l'intento di associazione android.media.browse.MediaBrowserService . Questo viene verificato con GTS.

    Per connetterti al MediaBrowserService selezionato, crea un'istanza MediaBrowser per un determinato componente del servizio e connect . Dopo aver stabilito la connessione, è possibile ottenere un handle per MediaSession tramite getSessionToken .

    L'app Radio può limitare i pacchetti client autorizzati a connettersi in un'implementazione onGetRoot del proprio servizio. L'app dovrebbe consentire alle app di sistema di connettersi senza inserirle nella whitelist. Per maggiori dettagli sull'inserimento nella whitelist, vedi Accettare il pacchetto e la firma dell'app dell'Assistente .

    Se l'app specifica della sorgente (ad esempio, un'app radio) è installata su un dispositivo senza tale supporto della sorgente, si pubblicizzerebbe comunque come in grado di gestire l'intento ACTION_PLAY_BROADCASTRADIO , ma il suo albero MediaBrowser non conterrebbe tag specifici della radio. Pertanto, un cliente che voglia verificare se una determinata fonte è disponibile su un dispositivo, deve:

    1. Scopri il servizio radio ( resolveService per ACTION_PLAY_BROADCASTRADIO ).
    2. Crea MediaBrowser e poi connettiti ad esso.
    3. Determina la presenza di MediaItem con EXTRA_BCRADIO_FOLDER_TYPE extra.

    Nota: nella maggior parte dei casi, il client deve eseguire la scansione di tutti gli alberi MediaBrowser disponibili per rilevare tutte le origini disponibili per un determinato dispositivo.

    Nomi delle bande

    L'elenco delle bande è rappresentato da un insieme di directory di livello superiore con un tag del tipo di cartella impostato su BCRADIO_FOLDER_TYPE_BAND . I titoli dei loro MediaItem sono stringhe localizzate che rappresentano i nomi delle band. Nella maggior parte dei casi sarà uguale alla traduzione in inglese, ma il cliente non può dipendere da questo presupposto.

    Per fornire un meccanismo stabile per la ricerca di determinate band, è stato aggiunto un tag aggiuntivo per le cartelle delle band, EXTRA_BCRADIO_BAND_NAME_EN . Questo è un nome non localizzato della band e può assumere solo uno di questi valori predefiniti:

    • AM
    • FM
    • DAB

    Se la band non è presente in questo elenco, il tag del nome della band non deve essere impostato. Tuttavia, se la band è nell'elenco, deve avere un tag impostato. La radio HD non elenca bande separate poiché utilizza lo stesso mezzo sottostante di AM/FM.

    Intenzioni di gioco generali

    Ogni app dedicata alla riproduzione di una determinata sorgente (come radio o CD) deve gestire un intento di riproduzione generale per avviare la riproduzione di alcuni contenuti possibilmente dallo stato inattivo (ad esempio dopo l'avvio). Spetta all'app selezionare il contenuto da riprodurre, ma di solito si tratta del programma radiofonico o della traccia del CD riprodotto di recente. Esiste un intento separato definito per ciascuna sorgente audio:

    • android.car.intent.action.PLAY_BROADCASTRADIO
    • android.car.intent.action.PLAY_AUDIOCD : CD-DA o CD-Text
    • android.car.intent.action.PLAY_DATADISC : disco dati ottico come CD/DVD, ma non CD-DA (potrebbe essere un CD in modalità mista)
    • android.car.intent.action.PLAY_AUX : senza specificare quale porta AUX
    • android.car.intent.action.PLAY_BLUETOOTH
    • android.car.intent.action.PLAY_USB : senza specificare quale dispositivo USB
    • android.car.intent.action.PLAY_LOCAL : archiviazione multimediale locale (flash integrato)

    Gli intenti sono stati scelti per essere utilizzati per il comando di gioco generale, perché risolvono due problemi contemporaneamente: il comando di gioco generale stesso e il rilevamento del servizio. Un ulteriore vantaggio di avere tale intento sarebbe la possibilità di eseguire un'azione così semplice senza aprire la sessione di MediaBrowser.

    L'individuazione dei servizi è in realtà il problema più importante risolto con questi intenti. La procedura per l'individuazione del servizio è semplice e inequivocabile in questo modo (vedi Individuazione e connessione al servizio ).

    Per rendere più semplici alcune implementazioni client, esiste un modo alternativo di emettere tale comando Play (che deve essere implementato anche dall'app radio): emettere playFromMediaId con il rootId del nodo root (utilizzato come mediaId). Sebbene il nodo radice non sia concepito per essere riproducibile, il suo rootId è una stringa arbitraria che può essere resa utilizzabile come mediaId. Tuttavia, i clienti non sono tenuti a comprendere questa sfumatura.

    Selettore di programma

    Sebbene mediaId sia sufficiente per selezionare un canale da MediaBrowserService , diventa vincolato a una sessione e non è coerente tra i provider. In alcuni casi il client potrebbe aver bisogno di un puntatore assoluto (come una frequenza assoluta) per mantenerlo tra sessioni e dispositivi.

    Nell'era delle trasmissioni radiofoniche digitali, una semplice frequenza non è sufficiente per sintonizzarsi su una determinata stazione. Pertanto, utilizzare ProgramSelector per sintonizzarsi su un canale analogico o digitale. ProgramSelector è composto da due parti:

    • Identificatore primario. Un identificatore univoco e stabile per una determinata stazione radio che non cambia ma potrebbe non essere sufficiente per sintonizzarsi su quella stazione. Ad esempio, il codice RDS PI, che può essere tradotto nell'identificativo di chiamata negli Stati Uniti.
    • Identificatori secondari. Identificatori aggiuntivi utili per sintonizzarsi su quella stazione (ad esempio, frequenza), eventualmente includendo identificatori di altre tecnologie radio. Ad esempio, una stazione DAB potrebbe avere un fallback di trasmissione analogica.

    Per consentire ProgramSelector di adattarsi alla soluzione basata su MediaBrowser o MediaSession , definire uno schema URI per serializzarlo. Lo schema è definito come segue:

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

    In questo esempio, la parte secondaria degli identificatori (dopo il punto interrogativo ( ? )) è facoltativa e può essere rimossa per fornire un identificatore stabile da utilizzare come mediaId . Per esempio:

    • 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 del program relativa all'autorità (AKA host) offre spazio per l'estensione del programma in futuro. Le stringhe del tipo di identificatore sono specificate con precisione come i relativi nomi nella definizione HAL 2.x di IdentifierType e il formato del valore è un numero decimale o esadecimale (con prefisso 0x ).

    Tutti gli identificatori specifici del fornitore sono rappresentati dal prefisso VENDOR_ . Ad esempio, VENDOR_0 per VENDOR_START e VENDOR_1 per VENDOR_START più 1. Tali URI sono specifici dell'hardware radio su cui sono stati generati e non possono essere trasferiti tra dispositivi realizzati da OEM diversi.

    Questi URI devono essere assegnati a ciascun MediaItem nelle cartelle radio di livello superiore. Inoltre, MediaSession deve supportare sia playFromMediaId che playFromUri . Tuttavia, l'URI è destinato principalmente all'estrazione di metadati radio (come la frequenza FM) e all'archiviazione persistente. Non vi è alcuna garanzia che l'URI sarà disponibile per tutti gli elementi multimediali (ad esempio, quando il tipo di ID primario non è ancora supportato dal framework). D'altra parte, Media ID funziona sempre. Non è consigliabile che i client utilizzino l'URI per selezionare elementi dalla sessione MediaBrowser corrente. Utilizzare invece playFromMediaId . Detto questo, non è facoltativo per l'app di servizio e gli URI mancanti sono riservati a casi ben giustificati.

    Il progetto iniziale utilizzava singoli due punti invece della sequenza :// dopo la parte dello schema. Tuttavia, il primo non è supportato da android.net.Uri per i riferimenti URI gerarchici assoluti.

    Altri tipi di fonti

    Altre sorgenti audio possono essere gestite in modo simile. Ad esempio, l'ingresso ausiliario e il lettore CD audio.

    Una singola app può servire più tipi di fonti. In questi casi, è consigliabile creare un MediaBrowserService separato per ogni tipo di origine. Anche in una configurazione con più origini servite/MediaBrowserServices, è fortemente consigliabile avere una singola MediaSession all'interno di una singola app.

    CD audio

    Simile al CD audio in quanto l'app che serve tali dischi esporrebbe MediaBrowser con una singola voce esplorabile (o più, se il sistema dispone di un cambia CD), che a sua volta conterrebbe tutte le tracce di un determinato CD. Se il sistema non conosce le tracce su ogni CD (ad esempio, quando tutti i dischi vengono inseriti in una cartuccia contemporaneamente e non li legge tutti), allora MediaItem per l'intero disco sarebbe solo PLAYABLE , non BROWSABLE più PLAYABLE . Se in un determinato slot non fosse presente alcun disco, l'oggetto non sarebbe né PLAYABLEBROWSABLE (ma ogni slot deve essere sempre presente nell'albero).

    Struttura ad albero del CD audio
    Figura 3. Struttura ad albero del CD audio

    Queste voci verrebbero contrassegnate in modo simile alle cartelle delle trasmissioni radiofoniche; conterrebbero campi extra aggiuntivi definiti nell'API MediaDescription:

    • EXTRA_CD_TRACK : per ogni MediaItem su CD audio, numero di traccia in base 1.
    • EXTRA_CD_DISK : numero del disco in base 1.

    Per i sistemi abilitati per CD-Text e i dischi compatibili, il MediaItem di livello superiore avrà un titolo del disco. Allo stesso modo, i MediaItem per le tracce avrebbero un titolo della traccia.

    Ingresso ausiliario

    L'app che fornisce l'input ausiliario espone un albero MediaBrowser con una singola voce (o più, quando esistono più porte) che rappresenta la porta di ingresso AUX. La rispettiva MediaSession prende il suo mediaId e passa a quella fonte dopo aver ricevuto la richiesta playFromMediaId .

    Struttura ad albero AUX
    Figura 4. Struttura ad albero AUX

    Ogni voce AUX MediaItem avrebbe un campo aggiuntivo EXTRA_AUX_PORT_NAME impostato sul nome non localizzato della porta senza la frase "AUX". Ad esempio, "AUX 1" dovrebbe essere impostato su "1", "AUX front" su "front" e "AUX" su una stringa vuota. Nelle versioni locali non inglesi, il tag name rimarrebbe la stessa stringa inglese. È improbabile che per EXTRA_BCRADIO_BAND_NAME_EN , i valori sono definiti da OEM e non vincolati a un elenco predefinito.

    Se l'hardware è in grado di rilevare i dispositivi collegati alla porta AUX, l'hardware dovrebbe contrassegnare il MediaItem come PLAYABLE , solo se l'input è collegato. L'hardware dovrebbe essere ancora elencato (ma non PLAYABLE ) se nulla era collegato a questa porta. Se l'hardware non ha tali capacità, il MediaItem deve essere sempre impostato su PLAYABLE .

    Campi extra

    Definire i seguenti campi:

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

    Il client deve rivedere i mediaitem di livello superiore per elementi con il set di campo extra EXTRA_CD_DISK o EXTRA_AUX_PORT_NAME .

    Esempi dettagliati

    I seguenti esempi affrontano la struttura dell'albero di Mediabrowser per i tipi di origine che fanno parte di questo design.

    Broadcast Radio MediaBrowsService (gestisce ACTION_PLAY_BROADCASTRADIO ):

    • Stazioni (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 (giocabile) - FM senza RDSuri: broadcastradio://program/AMFM_FREQUENCY/90500
      • 620 am (giocabile) URI: broadcastradio://program/AMFM_FREQUENCY/620
      • BBC One (Playable) Uri: broadcastradio://program/DAB_SID_EXT/1E24102?RDS_PI=1234
    • Preferiti (browsable, giocabile) EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_FAVORITES
      • BBC One (Playable) Uri: broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=101300
      • BBC Two (non giocabile) 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 (giocabile) URI: broadcastradio://program/AMFM_FREQUENCY/530
      • 540 am (giocabile) URI: broadcastradio://program/AMFM_FREQUENCY/540
      • 550 am (giocabile) 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 (giocabile) URI: broadcastradio://program/AMFM_FREQUENCY/87700
      • 87.9 FM (giocabile) URI: broadcastradio://program/AMFM_FREQUENCY/87900
      • 88.1 FM (giocabile) URI: broadcastradio://program/AMFM_FREQUENCY/88100
    • Dab (playable): EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_BANDEXTRA_BCRADIO_BAND_NAME_EN="DAB"

    Audio CD MediaBrowsService (gestisce ACTION_PLAY_AUDIOCD ):

    • Disco 1 (giocabile) EXTRA_CD_DISK=1
    • Disco 2 (sfollabile, giocabile) EXTRA_CD_DISK=2
      • Traccia 1 (playable) EXTRA_CD_TRACK=1
      • Traccia 2 (playable) EXTRA_CD_TRACK=2
    • My Music CD (browsable, playable) EXTRA_CD_DISK=3
      • Tutto da me (giocabile) EXTRA_CD_TRACK=1
      • Reise, Reise (Playable) EXTRA_CD_TRACK=2
    • Slot vuoto 4 (non giocabile) EXTRA_CD_DISK=4

    Aux MediaBrowsService (gestisce ACTION_PLAY_AUX ):

    • Aux Front (Playable) EXTRA_AUX_PORT_NAME="front"
    • Aux posteriore (giocabile) EXTRA_AUX_PORT_NAME="rear"