Zaimplementuj radio

Na tej stronie wyjaśniono, jak wdrożyć radio na poziomie sprzętu i oprogramowania.

Elementy systemu

Stos radiowy rozgłoszeniowy zawiera następujące komponenty.

Architektura radia nadawczego
Rysunek 1. Architektura radia rozgłoszeniowego

Aplikacja referencyjna radia

Aby uzyskać szczegółowe informacje na temat implementacji sterowania radiowego, zobacz Implementacja sterowania radiowego .

Przykładowa aplikacja radiowa Java ( packages/apps/Car/Radio ) służy jako implementacja referencyjna. Po uruchomieniu usługa aplikacji żąda od Menedżera radia otwarcia tunera radiowego. Następnie aplikacja może wysyłać do tunera radiowego żądania, takie jak dostrojenie określonej stacji radiowej, częstotliwości lub wyszukanie następnej dostępnej stacji radiowej. Aplikacja otrzymuje aktualizacje od Menedżera radia i tunera radiowego w radiu, takie jak aktualne informacje o programach, listy programów radiowych, konfiguracje i parametry zdefiniowane przez dostawcę. Referencyjna aplikacja Radio obsługuje tylko radio AM i FM. Producenci OEM mogą modyfikować lub wymieniać aplikację Radio według potrzeb.

Menedżer Radia

Gdy aplikacja żąda od Radio Managera otwarcia tunera, Menedżer Radio ( frameworks/base/core/java/android/hardware/radio/RadioManager.java ) żąda od usługi Broadcast Radio Service otwarcia sesji tunera, a następnie zamyka sesję w Tuner radiowy ( frameworks/base/core/java/android/hardware/radio/RadioTuner.java ), który jest zwracany do aplikacji. Tuner radiowy definiuje interfejsy API (takie jak strojenie, krok i anulowanie), które można wywoływać z aplikacji radiowych i wysyłać żądania do usługi Broadcast Radio Service. Metody wywołania zwrotnego ( RadioTuner.Callback ) zdefiniowane w Radio Tuner wysyłają aktualizacje dotyczące audycji radiowych HAL, takie jak aktualne informacje o programach, listy programów i parametry zdefiniowane przez dostawcę, z usługi Broadcast Radio Service do aplikacji.

Usługa radiofonii i telewizji

Usługa Broadcast Radio ( frameworks/base/services/core/java/com/android/server/broadcastradio ) to usługa kliencka dla Broadcast Radio HAL. Usługa Broadcast Radio Service koordynuje wielu menedżerów radiowych za pomocą HAL Broadcast Radio. Usługa Broadcast Radio Service obsługuje język definicji interfejsu HAL (HIDL) i język definicji interfejsu Androida (AIDL) . Usługa radiofonii i telewizji łączy się z AIDL HAL, jeśli istnieje jakakolwiek usługa AIDL HAL; w przeciwnym razie usługa łączy się z HIDL HAL. Usługa transmisji radiowej tworzy moduł radiowy dla każdej dostępnej instancji HAL (takiej jak AM, FM i DAB).

Każdy menedżer radiowy może poprosić usługę rozgłoszeniową o utworzenie sesji tunera w odpowiednim module radiowym, w zależności od typu radia. Każda sesja tunera może wywoływać metody, takie jak strojenie, krok i anulowanie (zdefiniowane w interfejsach HAL), aby wykonywać operacje na odpowiedniej instancji HAL rozgłoszeniowego radia. Kiedy jedna sesja tunera odbierze wywołanie zwrotne z instancji HAL w sprawie aktualizacji HAL, takie jak informacje o bieżącym programie, lista programów, flagi konfiguracyjne i parametry dostawcy, wywołania zwrotne dotyczące aktualizacji są wysyłane do wszystkich tunerów radiowych połączonych z tym samym modułem radiowym.

Transmisja radiowa HAL

Aby dowiedzieć się więcej o interfejsach HIDL i AIDL radia rozgłoszeniowego oraz różnicach między nimi, zobacz Interfejs HAL radia rozgłoszeniowego .

Warstwa abstrakcji sprzętu radiowego rozgłoszeniowego

W poniższych sekcjach opisano sposób pracy z warstwą abstrakcji sprzętu (HAL) w celu zaimplementowania radia rozgłoszeniowego.

Interfejs radiowy HAL

Radio rozgłoszeniowe HAL zapewnia struktury danych i interfejsy na poziomie sprzętowym do realizacji radia rozgłoszeniowego, takiego jak radio AM/FM i DAB.

Interfejsy HIDL 2.0 i AIDL

Radio rozgłoszeniowe HAL wykorzystuje interfejsy opisane w kolejnych rozdziałach.

Słuchacz zapowiedzi

IAnnouncementListener to interfejs wywołania zwrotnego dla słuchacza zapowiedzi, który można zarejestrować w radiu HAL w celu odbioru ogłoszeń. Interfejs posiada następujące metody:

IOgłoszenieSłuchacz
Opis: Wywoływany za każdym razem, gdy zmieniała się lista ogłoszeń.
HIDL 2.0 oneway onListUpdated(vec<Announcement> announcements)
AIDL oneway void onListUpdated(in Announcement[] announcements)
Zamknij uchwyt

ICloseHandle to ogólny uchwyt zamykający służący do usuwania wywołania zwrotnego, które nie wymaga aktywnego interfejsu.

IZamknij uchwyt
Opis: Zamknij uchwyt.
HIDL 2.0 close()
AIDL void close()

Interfejs wywołania zwrotnego

ITunerCallback to interfejs wywołania zwrotnego wywoływany przez radio rozgłoszeniowe HAL w celu wysyłania aktualizacji do usługi klienta HAL.

Oddzwonienie ITuner
Opis: wywoływane przez warstwę HAL, gdy operacja dostrajania (dostrajanie, wyszukiwanie (w formacie AIDL) lub skanowanie (w języku HIDL) i wykonanie kroku) kończy się asynchronicznym niepowodzeniem.
HIDL 2.0 oneway onCurrentProgramInfoChanged(ProgramInfo info)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
Opis: Wywoływany, gdy dostrajanie, wyszukiwanie (w AIDL) lub skanowanie (w HIDL) lub wykonanie kroku się powiedzie.
HIDL 2.0 oneway onTuneFailed(Result result, ProgramSelector selector)
AIDL void onTuneFailed(in Result result, in ProgramSelector selector)
Opis: Wywoływany, gdy dostrajanie, wyszukiwanie (w AIDL) lub skanowanie (w HIDL) lub wykonanie kroku się powiedzie.
HIDL 2.0 oneway onCurrentProgramInfoChanged(ProgramInfo info)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
Opis: Wywoływany, gdy lista programów jest aktualizowana; Rozmiar każdej porcji powinien być ograniczony do 500 kiB.
HIDL 2.0 oneway onProgramListUpdated(ProgramListChunk chunk)
AIDL oneway onProgramListUpdated(ProgramListChunk chunk)
Opis: Wywoływany, gdy antena jest podłączona lub odłączona.
HIDL 2.0 oneway onAntennaStateChange(bool connected)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
Opis: Wywoływany, gdy wartości parametrów specyficznych dla dostawcy są aktualizowane wewnętrznie w warstwie HAL (nie należy wywoływać po wywołaniu setParameters przez klienta HAL).
HIDL 2.0 oneway onParametersUpdated(vec<VendorKeyValue> parameters)
AIDL void onParametersUpdated(in VendorKeyValue[] parameters)
Opis: Nowość w AIDL. Wywoływana, gdy flaga konfiguracji jest aktualizowana wewnętrznie w warstwie HAL (nie powinna być wywoływana po wywołaniu setConfigFlag przez klienta HAL).
HIDL 2.0 Nie dotyczy.
AIDL void onConfigFlagUpdated(in ConfigFlag flag, in boolean value)

Podstawowy interfejs radiowy HAL

IBroadcastRadio jest podstawowym interfejsem dla radia HAL. W HIDL 2.0 HAL użyj interfejsu ITunerSession do tunera, aby wywołać operacje. Jednak w danym momencie aktywny jest maksymalnie jeden tuner (pod warunkiem, że każda instancja HAL radia ma tylko jeden chip tunera). ITunerSession został usunięty z interfejsów AIDL, a jego interfejsy przeniesiono do IBroadcastRadio .

IBroadcastRadio
Opis: Uzyskaj opis modułu i jego możliwości.
HIDL 2.0 getProperties() generates (Properties properties)
AIDL Properties getProperties()
Opis: Pobiera bieżącą lub możliwą konfigurację regionu AM/FM.
HIDL 2.0 getAmFmRegionConfig(bool full) generates (Result result, AmFmRegionConfig config)
AIDL AmFmRegionConfig getAmFmRegionConfig(bool full)
Opis: Pobiera bieżącą konfigurację regionu DAB.
HIDL 2.0 getDabRegionConfig() generates (Result result, vec<DabTableEntry> config)
AIDL DabTableEntry[] getDabRegionConfig()
Opis: Pobiera obraz z pamięci podręcznej modułu radiowego. W AIDL rozmiar obrazu musi być mniejszy niż 1 MB ze względu na sztywne ograniczenie bufora transakcji segregatora.
HIDL 2.0 getImage(uint32_t id) generates (vec<uint8_t> image)
AIDL byte[] getImage(in int id)
Opis: Rejestruje słuchacza zapowiedzi.
HIDL 2.0 registerAnnouncementListener(vec<AnnouncementType> enabled,IAnnouncementListener listener) generates (Result result, ICloseHandle closeHandle)
AIDL ICloseHandle registerAnnouncementListener(in IAnnouncementListener listener, in AnnouncementType[] enabled)
Opis:
  • HIDL HAL: Po otwarciu nowej sesji tunera stara sesja musi zostać zakończona.
  • AIDL HAL: Ponieważ nie jest dostępna żadna sesja tunera, należy ustawić jedynie wywołanie zwrotne tunera. Jeśli istnieje, stare wywołanie zwrotne powinno zostać rozbrojone.
HIDL 2.0 openSession(ITunerCallback callback) generuje (Result result, ITunerSession session)
AIDL void setTunerCallback(in ITunerCallback callback)
Opis:
  • HIDL HAL: Zamknięcie sesji tunera nie może zakończyć się niepowodzeniem i może zostać wydane tylko raz.
  • AIDL HAL: Nie ma tunera i należy jedynie wyłączyć wywołanie zwrotne tunera.
HIDL 2.0 close()
AIDL unsetTunerCallback()
Opis: Dostraja się do określonego programu.
HIDL 2.0 tune(ProgramSelector program) generates (Result result)
AIDL void tune(in ProgramSelector program)
Opis: Poszukuje następnego ważnego programu na antenie . Aby uniknąć nieporozumień w AIDL, scan została zmieniona na seek .
HIDL 2.0 scan(bool directionUp, bool skipSubChannel) generates (Result result)
AIDL void seek(in boolean directionUp, in boolean skipSubChannel)
Opis: Kroki do sąsiedniego kanału, który nie może być zajęty przez żaden program.
HIDL 2.0 step(bool directionUp) generates (Result result)
AIDL void step(in boolean directionUp)
Opis: Anuluje oczekujące operacje dostrajania, skanowania (w formacie HIDL), wyszukiwania (w formacie AIDL) lub operacji krokowych.
HIDL 2.0 cancel()
AIDL void cancel()
Opis: Stosuje filtr do listy programów i rozpoczyna wysyłanie aktualizacji listy programów poprzez wywołanie zwrotne onProgramListUpdated .
HIDL 2.0 startProgramListUpdates(ProgramFilter filter) generates (Result result)
AIDL void startProgramListUpdates(in ProgramFilter filter)
Opis: Zatrzymuje wysyłanie aktualizacji listy programów.
HIDL 2.0 stopProgramListUpdates()
AIDL void stopProgramListUpdates()
Opis: Pobiera bieżące ustawienie danej flagi konfiguracyjnej.
HIDL 2.0 isConfigFlagSet(ConfigFlag flag) generates (Result result, bool value)
AIDL boolean isConfigFlagSet(in ConfigFlag flag)
Opis: Ustawia daną flagę konfiguracyjną.
HIDL 2.0 setConfigFlag(ConfigFlag flag, bool value) generates (Result result)
AIDL void setConfigFlag(in ConfigFlag flag, boolean value)
Opis: Ustawia wartości parametrów specyficzne dla dostawcy.
HIDL 2.0 setParameters(vec<VendorKeyValue> parameters)

generuje ,

(vec<VendorKeyValue> results)
AIDL VendorKeyValue[] setParameters(in VendorKeyValue[] parameters)
Opis: pobiera wartości parametrów specyficzne dla dostawcy.
HIDL 2.0 getParameters(vec<string> keys) generates (vec<VendorKeyValue> parameters)
AIDL VendorKeyValue[] getParameters(in String[] keys)

Wyjaśnienia interfejsu

Zachowanie asynchroniczne

Ponieważ każda operacja dostrajania (na przykład strojenie, skanowanie (w HIDL) lub wyszukiwanie (w AIDL) i krok) może być czasochłonne, a wątek nie powinien być blokowany przez długi czas, operacja powinna zaplanować czasochłonne operacje wystąpić później i szybko zwrócić status lub wynik. Szczegółowo każda operacja powinna:

  • Anuluj wszystkie oczekujące operacje strojenia.
  • Sprawdź, czy operacja może zostać przetworzona na podstawie danych wejściowych metody i stanu tunera.
  • Zaplanuj zadanie dostrajania, a następnie natychmiast zwróć Result (w formacie HIDL) lub status (w formacie AIDL). Jeśli Result lub status to OK , wywołanie zwrotne tunera tuneFailed lub currentProgramInfoChanged musi zostać wywołane, gdy zadanie strojenia nie powiodło się (na przykład z powodu przekroczenia limitu czasu) lub zostało zakończone.

Podobnie startProgramListUpdates planuje również czasochłonne zadanie aktualizacji listy programów, które ma nastąpić później i szybko zwrócić status lub wynik. Metoda najpierw anuluje oczekujące żądania aktualizacji, a następnie planuje zadanie aktualizacji i szybko zwraca wynik.

Warunki wyścigu

Ze względu na asynchroniczne zachowanie operacji dostrajania (na przykład strojenie, skanowanie (w formacie HIDL) lub wyszukiwanie (w formacie AIDL) i krok) istnieje sytuacja wyścigu między anulowaniem operacji a operacjami dostrajania. Jeśli cancel zostanie wywołane po zakończeniu operacji dostrajania warstwy HAL i przed zakończeniem wywołania zwrotnego, anulowanie można zignorować, a wywołanie zwrotne powinno zostać zakończone i odebrane przez klienta HAL.

Podobnie, jeśli stopProgramListUpdates zostanie wywołane po zakończeniu aktualizacji listy programów przez warstwę HAL i przed zakończeniem wywołania zwrotnego onCurrentProgramInfoChanged , stopProgramListUpdates można zignorować i wywołanie zwrotne powinno zostać zakończone.

Limit rozmiaru danych

Ponieważ istnieje sztywny limit bufora transakcji spoiwa, limit danych dla niektórych metod interfejsu przekazujących dane o potencjalnie dużym rozmiarze jest wyjaśniony w AIDL HAL.

  • getImage wymaga zwrócenia obrazu o rozmiarze mniejszym niż 1 MB.
  • onProgramListUpdate wymaga, aby każdy chunk był mniejszy niż 500 kiB. Większe listy programów muszą zostać podzielone przez implementację HAL na wiele części i przesłane za pośrednictwem wielu wywołań zwrotnych.

Zmiany w strukturach danych AIDL HAL

Oprócz zmian w interfejsach, zmiany te zostały zastosowane w strukturach danych zdefiniowanych w radiu AIDL HAL, które wykorzystuje AIDL.

  • Constant wyliczenie jest usuwane w AIDL i definiowane jako const int w IBroadcastRadio . Tymczasem nazwa ANTENNA_DISCONNECTED_TIMEOUT_MS została zmieniona na ANTENNA_STATE_CHANGE_TIMEOUT_MS . Dodano nową stałą int TUNER_TIMEOUT_MS . Wszystkie operacje dostrajania, wyszukiwania i wykonywania kroków muszą zostać zakończone w tym czasie.
  • Enum RDS i Deemphasis są usuwane w AIDL i definiowane jako const int w AmFmRegionConfig . Odpowiednio, zarówno fmDeemphasis jak i fmRds w ProgramInfo są zadeklarowane jako int, bitowy wynik obliczeń odpowiednich flag. Tymczasem nazwy D50 i D75 zostały odpowiednio zmienione na DEEMPHASIS_D50 i DEEMPHASIS_D75 .
  • Enum ProgramInfoFlags są usuwane w AIDL i definiowane jako const int w ProgramInfo z dodanym przedrostkiem FLAG_ . Odpowiednio infoFlags w ProgramInfo jest zadeklarowany jako int, bitowy wynik obliczeń flag. Nazwa TUNED została również zmieniona na FLAG_TUNABLE , aby lepiej opisać definicję stacji, do której można dostroić stację.
  • W AmFmBandRange scanSpacing została zmieniona na seekSpacing , ponieważ scan została zmieniona na seek w AIDL.
  • Ponieważ koncepcja unii została wprowadzona w AIDL, MetadataKey i Metadata zdefiniowane w HIDL HAL nie są już używane. Metadata unii AIDL są zdefiniowane w AIDL HAL. Każda wartość wyliczeniowa znajdująca się wcześniej w MetadataKey jest teraz polem w Metadata o typie string lub int, w zależności od ich definicji.

Implementacja sterowania radiowego

Implementacja sterowania radiem opiera się na MediaSession i MediaBrowse , które umożliwiają aplikacjom multimediów i asystentów głosowych sterowanie radiem. Aby uzyskać więcej informacji, zobacz Tworzenie aplikacji multimedialnych dla samochodów w witrynie developer.android.com.

Implementacja drzewa przeglądania multimediów jest dostępna w bibliotece car-broadcastradio-support w packages/apps/Car/libs . Ta biblioteka zawiera również rozszerzenia ProgramSelector umożliwiające konwersję do i z URI. Zaleca się, aby implementacje radiowe korzystały z tej biblioteki do budowania powiązanego drzewa przeglądania.

Przełącznik źródła multimediów

Aby zapewnić płynne przejście między radiem a innymi aplikacjami wyświetlanymi w mediach, biblioteka car-media-common zawiera klasy, które należy zintegrować z aplikacją radia. MediaAppSelectorWidget można uwzględnić w pliku XML aplikacji radiowej (ikona i menu rozwijane używane w referencyjnych aplikacjach multimedialnych i radiowych):

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

Ten widżet uruchamia element AppSelectionFragment , który wyświetla listę źródeł multimediów, na które można się przełączyć. Jeśli wymagany jest interfejs użytkownika inny niż podany, możesz utworzyć niestandardowy widżet, aby uruchomić AppSelectionFragment , gdy powinien zostać wyświetlony przełącznik.

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

Przykładowa implementacja jest dostępna w referencyjnej implementacji aplikacji radiowej, znajdującej się w packages/apps/Car/Radio .

Szczegółowe specyfikacje sterowania

Interfejs MediaSession (poprzez MediaSession.Callback ) udostępnia mechanizmy sterujące aktualnie odtwarzaną audycją radiową:

  • onPlay , onStop . (Nie)wyciszenie odtwarzania radia.
  • onPause . Pauza przesunięta w czasie (jeśli jest obsługiwana).
  • onPlayFromMediaId . Odtwarzaj dowolną zawartość z folderu najwyższego poziomu. Na przykład „Odtwórz FM” lub „Odtwórz radio”.
  • onPlayFromUri . Odtwórz określoną częstotliwość. Na przykład „Odtwórz 88,5 FM”.
  • onSkipToNext , onSkipToPrevious . Dostrój następną lub poprzednią stację.
  • onSetRating . Dodaj lub usuń do lub z Ulubionych.

MediaBrowser udostępnia przestrajalny element MediaItem w trzech typach katalogów najwyższego poziomu:

  • ( Opcjonalnie ) Programy (stacje). Ten tryb jest zwykle używany w radiotelefonach z dwoma tunerami do wskazywania wszystkich dostępnych dostrojonych stacji radiowych w lokalizacji użytkownika.
  • Ulubione. Programy radiowe dodane do listy Ulubione, niektóre mogą być niedostępne (poza zasięgiem odbioru).
  • Kanały pasma. Wszystkie fizycznie możliwe kanały w bieżącym regionie (87,9, 88,1, 88,3, 88,5, 88,7, 88,9, 89,1 itd.). Każdy zespół ma oddzielny katalog najwyższego poziomu.
Struktura drzewa MediaBrowserService
Rysunek 2. Struktura drzewa MediaBrowserService

Każdy element w każdym z tych folderów (AM/FM/Programy) jest elementem MediaItem z identyfikatorem URI, którego można używać z MediaSession do dostrajania. Każdy folder najwyższego poziomu (AM/FM/Programy) to element MediaItem z identyfikatorem mediaId, którego można używać z programem MediaSession w celu uruchomienia odtwarzania i zależy to od uznania producenta OEM. Na przykład „Play FM”, „Play AM” i „Play Radio” to niespecyficzne zapytania radiowe, które używają identyfikatora mediaId do wysyłania do aplikacji radiowej OEM. To aplikacja radiowa decyduje, co odtwarzać na podstawie ogólnego żądania i identyfikatora mediaId.

Sesja medialna

Ponieważ nie istnieje koncepcja wstrzymywania strumienia transmisji, akcje Odtwórz, Wstrzymaj i Zatrzymaj nie zawsze mają zastosowanie do radia. W przypadku radia akcja Stop jest powiązana z wyciszeniem strumienia, a Odtwórz z wyłączeniem wyciszenia.

Niektóre tunery radiowe (lub aplikacje) umożliwiają symulowanie pauzy w transmisji poprzez buforowanie zawartości i późniejsze jej odtwarzanie. W takich przypadkach użyj onPause .

Odtwarzanie z akcji mediaId i URI ma na celu dostrojenie się do stacji pobranej z interfejsu MediaBrowser. MediaId to dowolny ciąg znaków dostarczany przez aplikację radia w celu narzucenia unikalnej (aby dany identyfikator wskazywał tylko jeden element) i stabilnej (a więc dany element miał ten sam identyfikator przez całą sesję) wartości umożliwiającej identyfikację danej stacji . Identyfikator URI będzie miał dobrze zdefiniowany schemat. Krótko mówiąc, forma ProgramSelector z URI. Chociaż zachowuje to atrybut wyjątkowości, nie musi być stabilny, chociaż może się zmienić, gdy stacja przejdzie na inną częstotliwość.

Z założenia onPlayFromSearch nie jest używana. Obowiązkiem klienta (aplikacji towarzyszącej) jest wybranie wyniku wyszukiwania z drzewa MediaBrowser. Przeniesienie tej odpowiedzialności na aplikację radiową zwiększyłoby złożoność, wymagałoby formalnych umów dotyczących wyglądu zapytań łańcuchowych i skutkowałoby nierównym doświadczeniem użytkownika na różnych platformach sprzętowych.

Uwaga: aplikacja radiowa nie zawiera dodatkowych informacji, które byłyby przydatne do wyszukiwania nazwy stacji, która nie jest widoczna dla klienta za pośrednictwem interfejsu MediaBrowser.

Przejście do następnej lub poprzedniej stacji zależy od bieżącego kontekstu:

  • Gdy aplikacja dostroi się do stacji z listy Ulubione, może przejść do następnej stacji z listy Ulubione.
  • Słuchanie stacji z Listy Programów może skutkować dostrojeniem kolejnej dostępnej stacji, posortowanej według numeru kanału.
  • Słuchanie dowolnego kanału może skutkować dostrojeniem się do następnego kanału fizycznego, nawet jeśli nie jest nadawany sygnał.

Aplikacja radiowa obsługuje te działania.

Obsługa błędów

Akcje TransportControls (Odtwórz, Zatrzymaj i Dalej) nie dostarczają informacji zwrotnej na temat tego, czy akcja zakończyła się pomyślnie, czy nie. Jedynym sposobem wskazania błędu jest ustawienie stanu MediaSession na STATE_ERROR z komunikatem o błędzie.

Aplikacja radiowa musi obsłużyć te działania i albo je wykonać, albo ustawić stan błędu. Jeżeli wykonanie polecenia Play nie jest natychmiastowe, w trakcie wykonywania polecenia należy zmienić stan odtwarzania na STATE_CONNECTING (w przypadku bezpośredniego dostrojenia) lub STATE_SKIPPING_TO_PREVIOUS lub NEXT .

Klient powinien obserwować PlaybackState i sprawdzić, czy sesja zmieniła bieżący program na żądany lub wprowadziła stan błędu. STATE_CONNECTING nie może przekraczać 30 sekund. Jednakże bezpośrednie dostrojenie do danej częstotliwości AM/FM powinno działać znacznie szybciej.

Dodawaj i usuwaj ulubione

MediaSession obsługuje oceny, których można używać do kontrolowania Ulubionych. onSetRating wywołany z oceną typu RATING_HEART dodaje lub usuwa aktualnie dostrojoną stację do lub z listy Ulubione.

W przeciwieństwie do starszych ustawień wstępnych, model ten zakłada nieuporządkowaną i nieograniczoną listę ulubionych, gdzie każdy zapisany ulubiony jest przypisany do przedziału numerycznego (zwykle od 1 do 6). W rezultacie systemy oparte na ustawieniach wstępnych byłyby niekompatybilne z operacją onSetRating .

Ograniczeniem interfejsu API MediaSession jest to, że można dodać lub usunąć tylko aktualnie dostrojoną stację. Na przykład elementy muszą zostać najpierw wybrane, zanim będzie można je usunąć. Jest to jedynie ograniczenie klienta MediaBrowser, np. aplikacji towarzyszącej. Aplikacja radiowa nie podlega podobnym ograniczeniom. Ta część jest opcjonalna, jeśli aplikacja nie obsługuje Ulubionych.

Przeglądarka multimediów

Aby wyrazić, które częstotliwości lub nazwy kanałów fizycznych (gdy dostrojenie dowolnego kanału jest odpowiednie dla danej technologii radiowej) obowiązują w danym regionie, dla każdego pasma wyszczególnione są wszystkie obowiązujące kanały (częstotliwości). W regionie USA oznacza to 101 kanałów FM z zakresu od 87,8 do 108,0 MHz (przy odstępie 0,2 MHz) i 117 kanałów AM w zakresie od 530 do 1700 kHz (przy odstępie 10 kHz). Ponieważ radio HD wykorzystuje tę samą przestrzeń kanałową, nie jest prezentowane osobno.

Lista aktualnie dostępnych programów radiowych jest płaska, ponieważ nie pozwala na stosowanie takich schematów wyświetlania, jak grupowanie według zespołu bezpośredniej transmisji audio (DAB).

Wpisy na liście ulubionych mogą nie podlegać strojeniu. Na przykład, jeśli dany program jest poza zakresem. Aplikacja radiowa może, ale nie musi, wykryć, czy wpis można wcześniej dostroić. Jeśli tak, może nie oznaczać wpisu jako nadającego się do odtworzenia.

Do identyfikacji folderów najwyższego poziomu stosowany jest ten sam mechanizm, który wykorzystuje Bluetooth. Oznacza to, że pakiet Extras obiektu MediaDescription zawiera pole specyficzne dla tunera, tak jak robi to Bluetooth z EXTRA_BT_FOLDER_TYPE . W przypadku radia rozgłoszeniowego prowadzi to do zdefiniowania w publicznym API następujących nowych pól:

  • EXTRA_BCRADIO_FOLDER_TYPE = "android.media.extra.EXTRA_BCRADIO_FOLDER_TYPE" . Jedna z następujących wartości:
    • BCRADIO_FOLDER_TYPE_PROGRAMS = 1 . Aktualnie dostępne programy.
    • BCRADIO_FOLDER_TYPE_FAVORITES = 2 . Ulubione.
    • BCRADIO_FOLDER_TYPE_BAND = 3 . Wszystkie kanały fizyczne dla danego pasma.

    Nie ma potrzeby definiowania żadnych niestandardowych pól metadanych specyficznych dla radia, ponieważ wszystkie istotne dane pasują do istniejącego schematu MediaBrowser.MediaItem :

    • Nazwa programu (RDS PS, nazwa usługi DAB). MediaDescription.getTitle .
    • Częstotliwość FM. URI (patrz ProgramSelector ) lub MediaDescription.getTitle (jeśli wpis znajduje się w folderze BROADCASTRADIO_FOLDER_TYPE_BAND ).
    • Identyfikatory specyficzne dla radia (RDS PI, DAB SId). MediaDescription.getMediaUri przeanalizowano do ProgramSelector.

    Zwykle nie ma potrzeby pobierania częstotliwości FM dla wpisu w bieżącym programie lub liście Ulubione (ponieważ klient powinien operować na identyfikatorach mediów). Jeśli jednak zaistnieje taka potrzeba (na przykład do celów wyświetlania), jest ona obecna w identyfikatorze URI i można ją przeanalizować do ProgramSelector . To powiedziawszy, nie zaleca się używania identyfikatora URI do wybierania elementów w bieżącej sesji. Aby uzyskać szczegółowe informacje, zobacz ProgramSelector .

    Aby uniknąć problemów z wydajnością lub segregatorem, usługa MediaBrowser musi obsługiwać paginację:

    Uwaga: Domyślnie paginacja jest domyślnie zaimplementowana w wariancie onLoadChildren() bez obsługi opcji.

    Powiązane wpisy ze wszystkich typów list (surowe kanały, znalezione programy i ulubione) mogą mieć różne mediaId (zależy to od aplikacji radia; biblioteka wsparcia będzie miała je inne). Identyfikatory URI (w formie ProgramSelector) różnią się między nieprzetworzonymi kanałami i programami znalezionymi w większości przypadków (z wyjątkiem FM bez RDS), ale w większości są takie same między znalezionymi programami i ulubionymi (z wyjątkiem na przykład aktualizacji AF).

    Posiadanie różnych mediaId dla wpisów z różnych typów list umożliwia podejmowanie na nich różnych działań. Możesz przeglądać listę Ulubione lub listę Wszystkie programy w onSkipToNext , w zależności od folderu ostatnio wybranego MediaItem (zobacz MediaSession ).

    Specjalne działania dostrojone

    Lista programów umożliwia użytkownikom dostrojenie się do określonej stacji, ale nie pozwala użytkownikom na wprowadzanie ogólnych poleceń, takich jak „Dostrój do FM”, które mogłyby skutkować dostrojeniem do ostatnio słuchanej stacji w paśmie FM.

    Aby wspierać takie akcje, niektóre katalogi najwyższego poziomu mają ustawioną flagę FLAG_PLAYABLE (wraz z FLAG_BROWSABLE dla folderów).

    Działanie Dopasowuje się do Jak wydać
    Włącz radio Dowolny kanał radiowy startService(ACTION_PLAY_BROADCASTRADIO)

    Lub

    playFromMediaId(MediaBrowser. getRoot() )
    Odtwórz FM Dowolny kanał FM Odtwórz z mediaId pasma FM.

    Wybór programu do dostrojenia zależy od aplikacji. Jest to zazwyczaj ostatnio dostrojony kanał z podanej listy. Aby uzyskać szczegółowe informacje na temat ACTION_PLAY_BROADCASTRADIO , zobacz Ogólne zamiary gry .

    Wykrywanie i połączenie serwisowe

    PackageManager może bezpośrednio znaleźć drzewo radiowe transmisji MediaBrowserService obsługującej. Aby to zrobić, wywołaj resolveService z intencją ACTION_PLAY_BROADCASTRADIO (zobacz Ogólne intencje gry ) i flagą MATCH_SYSTEM_ONLY . Aby znaleźć wszystkie usługi obsługujące radio (może być ich więcej niż jedna, na przykład oddzielne AM/FM i satelita), użyj queryIntentServices .

    Rozwiązana usługa obsługuje również intencję powiązania android.media.browse.MediaBrowserService . Jest to weryfikowane w GTS.

    Aby połączyć się z wybraną usługą MediaBrowserService utwórz instancję MediaBrowser dla danego komponentu usługi i connect . Po nawiązaniu połączenia uchwyt do MediaSession można uzyskać poprzez getSessionToken .

    Aplikacja Radio może ograniczać pakiety klienckie, które mogą łączyć się w ramach implementacji usługi onGetRoot . Aplikacja powinna umożliwiać aplikacjom systemowym łączenie się bez umieszczania na białej liście. Szczegółowe informacje na temat umieszczania na białej liście znajdziesz w artykule Akceptowanie pakietu aplikacji Asystent i podpisu .

    Jeśli aplikacja specyficzna dla źródła (na przykład aplikacja radiowa) jest zainstalowana na urządzeniu bez obsługi takiego źródła, nadal reklamuje się jako obsługująca intencję ACTION_PLAY_BROADCASTRADIO , ale jej drzewo MediaBrowser nie będzie zawierać znaczników specyficznych dla radia. Zatem klient chcący sprawdzić czy dane źródło jest dostępne na urządzeniu musi:

    1. Odkryj usługę radiową (zadzwoń do resolveService w celu uzyskania ACTION_PLAY_BROADCASTRADIO ).
    2. Utwórz MediaBrowser , a następnie połącz się z nim.
    3. Określ obecność MediaItem za pomocą dodatku EXTRA_BCRADIO_FOLDER_TYPE .

    Uwaga: W większości przypadków klient musi przeskanować wszystkie dostępne drzewa MediaBrowser, aby wykryć wszystkie dostępne źródła dla danego urządzenia.

    Nazwy zespołów

    Lista pasm jest reprezentowana przez zestaw katalogów najwyższego poziomu ze znacznikiem typu folderu ustawionym na BCRADIO_FOLDER_TYPE_BAND . Ich tytuły MediaItem to zlokalizowane ciągi znaków reprezentujące nazwy zespołów. W większości przypadków będzie to to samo, co tłumaczenie na język angielski, ale klient nie może polegać na tym założeniu.

    Aby zapewnić stabilny mechanizm wyszukiwania określonych pasm, dodano dodatkowy znacznik dla folderów zespołów, EXTRA_BCRADIO_BAND_NAME_EN . Jest to niezlokalizowana nazwa pasma i może przyjmować tylko jedną z następujących predefiniowanych wartości:

    • AM
    • FM
    • DAB

    Jeśli zespołu nie ma na tej liście, nie należy ustawiać plakietki z nazwą zespołu. Jeśli jednak zespół znajduje się na liście, musi mieć ustawiony tag. Radio HD nie wymienia oddzielnych pasm, ponieważ wykorzystuje to samo podstawowe medium co AM/FM.

    Ogólne zamiary gry

    Każda aplikacja dedykowana do odtwarzania danego źródła (takiego jak radio lub płyta CD) musi obsługiwać ogólny zamiar odtwarzania , aby rozpocząć odtwarzanie niektórych treści ewentualnie ze stanu nieaktywnego (na przykład po uruchomieniu). Wybór treści do odtwarzania zależy od aplikacji, ale zazwyczaj jest to ostatnio odtwarzany program radiowy lub utwór z płyty CD. Dla każdego źródła dźwięku zdefiniowano odrębną intencję:

    • android.car.intent.action.PLAY_BROADCASTRADIO
    • android.car.intent.action.PLAY_AUDIOCD : CD-DA lub CD-Text
    • android.car.intent.action.PLAY_DATADISC : Optyczny dysk z danymi, taki jak CD/DVD, ale nie CD-DA (może to być CD w trybie mieszanym)
    • android.car.intent.action.PLAY_AUX : Bez określania, który port AUX
    • android.car.intent.action.PLAY_BLUETOOTH
    • android.car.intent.action.PLAY_USB : Bez określenia, które urządzenie USB
    • android.car.intent.action.PLAY_LOCAL : Lokalna pamięć multimediów (wbudowana pamięć flash)

    Intencje zostały wybrane do wykorzystania w ogólnym poleceniu odtwarzania, ponieważ rozwiązują jednocześnie dwa problemy: samo ogólne polecenie odtwarzania i wykrywanie usług. Dodatkową korzyścią z posiadania takiego zamiaru byłaby możliwość wykonania tak prostej akcji bez otwierania sesji MediaBrowser.

    Wykrywanie usług jest w rzeczywistości ważniejszym problemem rozwiązanym za pomocą tych zamiarów. Procedura wykrywania usług jest w ten sposób łatwa i jednoznaczna (patrz Wykrywanie i podłączanie usług ).

    Aby ułatwić implementację niektórych klientów, istnieje alternatywny sposób wydania takiego polecenia Play (które również musi zostać zaimplementowane przez aplikację radiową): wydanie playFromMediaId z rootId węzła głównego (używanego jako mediaId). Chociaż węzeł główny nie jest przeznaczony do odtwarzania, jego rootId jest dowolnym ciągiem znaków, który można wykorzystać jako mediaId. Jednak klienci nie są zobowiązani do zrozumienia tego niuansu.

    Selektor programu

    Chociaż mediaId wystarczy, aby wybrać kanał z MediaBrowserService , zostaje on powiązany z sesją i nie jest spójny między dostawcami. W niektórych przypadkach klient może potrzebować wskaźnika bezwzględnego (takiego jak częstotliwość bezwzględna), aby utrzymać go między sesjami i urządzeniami.

    W dobie cyfrowych audycji radiowych sama częstotliwość nie wystarczy, aby dostroić się do konkretnej stacji. Dlatego użyj ProgramSelector , aby dostroić się do kanału analogowego lub cyfrowego. ProgramSelector składa się z dwóch części:

    • Podstawowy identyfikator. Unikalny i stabilny identyfikator danej stacji radiowej, który nie zmienia się, ale może nie wystarczyć do dostrojenia się do tej stacji. Na przykład kod RDS PI, który w USA można przetłumaczyć na znak wywoławczy.
    • Identyfikatory wtórne. Dodatkowe identyfikatory przydatne do dostrojenia się do tej stacji (na przykład częstotliwość), ewentualnie obejmujące identyfikatory z innych technologii radiowych. Na przykład stacja DAB może mieć rezerwowe nadawanie analogowe.

    Aby umożliwić dopasowanie ProgramSelector do rozwiązania opartego na MediaBrowser lub MediaSession , zdefiniuj schemat URI w celu jego serializacji. Schemat jest zdefiniowany w następujący sposób:

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

    W tym przykładzie część dotycząca dodatkowych identyfikatorów (po znaku zapytania ( ? )) jest opcjonalna i można ją usunąć, aby zapewnić stabilny identyfikator do użycia jako mediaId . Na przykład:

    • 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

    Część organizacyjna program (znana również jako gospodarz) zapewnia pewne miejsce na rozszerzenie programu w przyszłości. Ciągi typu identyfikatora są precyzyjnie określone jako ich nazwy w definicji IdentifierType HAL 2.x, a format wartości to liczba dziesiętna lub szesnastkowa (z przedrostkiem 0x ).

    Wszystkie identyfikatory specyficzne dla dostawcy są reprezentowane przez przedrostek VENDOR_ . Na przykład VENDOR_0 dla VENDOR_START i VENDOR_1 dla VENDOR_START plus 1. Takie identyfikatory URI są specyficzne dla sprzętu radiowego, na którym zostały wygenerowane i nie można ich przenosić między urządzeniami wyprodukowanymi przez różnych producentów OEM.

    Te identyfikatory URI muszą być przypisane do każdego elementu MediaItem w folderach radiowych najwyższego poziomu. Ponadto MediaSession musi obsługiwać zarówno playFromMediaId , jak i playFromUri . Jednak identyfikator URI jest przeznaczony przede wszystkim do ekstrakcji metadanych radiowych (takich jak częstotliwość FM) i trwałego przechowywania. Nie ma gwarancji, że identyfikator URI będzie dostępny dla wszystkich elementów multimedialnych (na przykład, gdy podstawowy typ identyfikatora nie jest jeszcze obsługiwany przez platformę). Z drugiej strony identyfikator multimediów zawsze działa. Nie zaleca się , aby klienci używali identyfikatora URI do wybierania elementów z bieżącej sesji MediaBrowser. Zamiast tego użyj playFromMediaId . To powiedziawszy, nie jest to opcjonalne w przypadku aplikacji obsługującej, a brakujące identyfikatory URI są zarezerwowane dla dobrze uzasadnionych przypadków.

    W pierwotnym projekcie zastosowano pojedynczy dwukropek zamiast sekwencji :// po części schematu. Jednak ten pierwszy nie jest obsługiwany przez android.net.Uri w przypadku bezwzględnych hierarchicznych odwołań do identyfikatora URI.

    Inne typy źródeł

    Inne źródła dźwięku można obsługiwać w podobny sposób. Na przykład wejście pomocnicze i odtwarzacz Audio CD.

    Pojedyncza aplikacja może obsługiwać wiele typów źródeł. W takich przypadkach zaleca się utworzenie osobnej usługi MediaBrowserService dla każdego typu źródła. Nawet w konfiguracji z wieloma obsługiwanymi źródłami/usługami MediaBrowserService zdecydowanie zaleca się posiadanie jednej sesji MediaSession w jednej aplikacji.

    Płyta audio CD

    Podobnie jak w przypadku Audio CD, aplikacja obsługująca takie dyski udostępniałaby MediaBrowser z jednym wpisem do przeglądania (lub większą liczbą, jeśli system ma zmieniarkę CD), który z kolei zawierałby wszystkie ścieżki z danej płyty CD. Jeśli system nie ma wiedzy o utworach na każdej płycie CD (np. gdy wszystkie dyski są włożone do kasety na raz i nie czyta ich wszystkich), to MediaItem dla całego dysku będzie po prostu PLAYABLE , a nie BROWSABLE i PLAYABLE . Jeśli w danym slocie nie ma dysku, przedmiot nie będzie PLAYABLE ani BROWSABLE (ale każde miejsce musi zawsze znajdować się w drzewie).

    Struktura drzewa audio CD
    Rysunek 3. Struktura drzewa audio CD

    Wpisy te byłyby oznaczone w podobny sposób, jak foldery audycji radiowych; zawierałyby dodatkowe dodatkowe pola zdefiniowane w API MediaDescription:

    • EXTRA_CD_TRACK : Dla każdego MediaItem na płycie audio CD, numer ścieżki oparty na 1.
    • EXTRA_CD_DISK : numer dysku oparty na 1.

    W przypadku systemu obsługującego CD-Text i kompatybilnego dysku, element MediaItem najwyższego poziomu będzie miał tytuł dysku. Podobnie element MediaItems dla utworów będzie miał tytuł utworu.

    Wejście pomocnicze

    Aplikacja obsługująca wejście pomocnicze udostępnia drzewo MediaBrowser z jednym wpisem (lub większą liczbą, jeśli istnieje wiele portów) reprezentującym port wejścia AUX. Odpowiednia MediaSession pobiera swój mediaId i przełącza się na to źródło po otrzymaniu żądania playFromMediaId .

    Struktura drzewa AUX
    Rysunek 4. Struktura drzewa AUX

    Każdy wpis AUX MediaItem miałby dodatkowe pole EXTRA_AUX_PORT_NAME ustawione na niezlokalizowaną nazwę portu bez frazy „AUX”. Na przykład „AUX 1” zostałoby ustawione na „1”, „AUX front” na „front”, a „AUX” na pusty ciąg znaków. W lokalizacjach innych niż angielski znacznik nazwy pozostanie tym samym ciągiem znaków w języku angielskim. Mało prawdopodobne, jak dla EXTRA_BCRADIO_BAND_NAME_EN , wartości są zdefiniowane OEM i nie są ograniczone do predefiniowanej listy.

    Jeśli sprzęt może wykryć urządzenia podłączone do portu Aux, sprzęt powinien oznaczać MediaItem jako PLAYABLE , tylko wtedy, gdy wejście jest podłączone. Sprzęt powinien być nadal wyliczony (ale nie można PLAYABLE ), jeśli nic nie było podłączone do tego portu. Jeśli sprzęt nie ma takiej możliwości, MediaItem musi być zawsze ustawiony na PLAYABLE .

    Dodatkowe pola

    Zdefiniuj następujące pola:

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

    Klient musi przejrzeć MediaItems na najwyższym poziomie dla elementów posiadających zestaw EXTRA_CD_DISK lub EXTRA_AUX_PORT_NAME .

    Szczegółowe przykłady

    Poniższe przykłady odnoszą się do struktury drzewa MediaBrowser dla typów źródeł, które są częścią tego projektu.

    Broadcast Radio MediaBrowserservice (obsługuje ACTION_PLAY_BROADCASTRADIO ):

    • Stacje (przeglądanie) EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_PROGRAMS
      • BBC One (odtwarzane) URI: broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=90500
      • ABC 88.1 (grywalne) URI: broadcastradio://program/RDS_PI/5678?AMFM_FREQUENCY=88100
      • ABC 88.1 HD1 (grywalne) URI: broadcastradio://program/HD_STATION_ID_EXT/158241DEADBEEF?AMFM_FREQUENCY=88100&RDS_PI=5678
      • ABC 88.1 HD2 (grywalny) URI: broadcastradio://program/HD_STATION_ID_EXT/158242DEADBEFE
      • 90,5 FM (grywalny) - FM bez RDSURI: broadcastradio://program/AMFM_FREQUENCY/90500
      • 620 AM (grywalny) URI: broadcastradio://program/AMFM_FREQUENCY/620
      • BBC One (odtwarzane) URI: broadcastradio://program/DAB_SID_EXT/1E24102?RDS_PI=1234
    • Ulubione (przeglądane, playabalne) EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_FAVORITES
      • BBC One (odtwarzane) URI: broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=101300
      • BBC dwa (nie grane) 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 (grywalny) URI: broadcastradio://program/AMFM_FREQUENCY/530
      • 540 AM (grywalny) URI: broadcastradio://program/AMFM_FREQUENCY/540
      • 550 AM (grywalny) 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 (grywalne) URI: broadcastradio://program/AMFM_FREQUENCY/87700
      • 87,9 FM (grywalne) URI: broadcastradio://program/AMFM_FREQUENCY/87900
      • 88.1 FM (grywalne) URI: broadcastradio://program/AMFM_FREQUENCY/88100
    • DAB (playabalne): EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_BANDEXTRA_BCRADIO_BAND_NAME_EN="DAB"

    Audio CD MediaBrowSerservice (obsługuje ACTION_PLAY_AUDIOCD ):

    • Disc 1 (odtwarzany) EXTRA_CD_DISK=1
    • Disc 2 (przeglądalny, playabalny) EXTRA_CD_DISK=2
      • Ścieżka 1 (gra) EXTRA_CD_TRACK=1
      • Track 2 (odtwarzalny) EXTRA_CD_TRACK=2
    • Moja muzyczna cd (przeglądacza, odtwarzana) EXTRA_CD_DISK=3
      • Wszystko przez siebie (grywalne) EXTRA_CD_TRACK=1
      • Reise, Reise (playable) EXTRA_CD_TRACK=2
    • Pusty gniazdo 4 (nie do gry) EXTRA_CD_DISK=4

    Aux MediaBrowsERService (obsługuje ACTION_PLAY_AUX ):

    • Aux Front (playable) EXTRA_AUX_PORT_NAME="front"
    • Aux z tyłu (gra) EXTRA_AUX_PORT_NAME="rear"