라디오 구현

이 페이지에서는 하드웨어 및 소프트웨어 수준에서 라디오를 구현하는 방법을 설명합니다.

시스템 구성요소

방송 라디오 스택에는 다음과 같은 구성요소가 포함됩니다.

방송 라디오 아키텍처
그림 1. 방송 라디오 아키텍처

라디오 참조 앱

라디오 컨트롤을 구현하는 방법에 관한 자세한 내용은 라디오 컨트롤 구현을 참고하세요.

샘플 자바 라디오 앱(packages/apps/Car/Radio)은 참조 구현 역할을 합니다. 앱 서비스가 시작되면 라디오 관리자에게 라디오 튜너를 열도록 요청합니다. 그러면 앱은 라디오 튜너에 요청을 보낼 수 있습니다. 특정 라디오 채널이나 주파수에 맞추거나 사용할 수 있는 다음 라디오 채널을 찾아 조정하는 작업을 예로 들 수 있습니다. 앱은 라디오의 라디오 관리자 및 라디오 튜너로부터 현재 프로그램 정보, 라디오 프로그램 목록, 구성, 공급업체 정의 매개변수 등의 업데이트를 수신합니다. 참조 라디오 앱은 AM 및 FM 라디오만 지원합니다. OEM은 원하는 대로 라디오 앱을 수정하거나 교체할 수 있습니다.

라디오 관리자

앱이 라디오 관리자에 튜너를 열도록 요청하면 라디오 관리자(frameworks/base/core/java/android/hardware/radio/RadioManager.java)는 방송 라디오 서비스에 튜너 세션을 열도록 요청하고 세션을 라디오 튜너(frameworks/base/core/java/android/hardware/radio/RadioTuner.java)에 래핑하며 이는 앱에 반환됩니다. 라디오 튜너는 라디오 앱에서 호출되어 방송 라디오 서비스에 요청을 보낼 수 있는 API(미세 조정, 단계, 취소)를 정의합니다. 라디오 튜너에 정의된 콜백 메서드(RadioTuner.Callback)는 현재 프로그램 정보, 프로그램 목록, 공급업체 정의 매개변수와 같은 방송 라디오 HAL에 관한 업데이트를 방송 라디오 서비스에서 앱으로 전송합니다.

방송 라디오 서비스

방송 라디오 서비스(frameworks/base/services/core/java/com/android/server/broadcastradio)는 방송 라디오 HAL의 클라이언트 서비스입니다. 방송 라디오 서비스는 방송 라디오 HAL을 사용하여 여러 라디오 관리자를 조정합니다. 방송 라디오 서비스는 HAL 인터페이스 정의 언어(HIDL)Android 인터페이스 정의 언어(AIDL) 방송 라디오 HAL을 지원합니다. 방송 라디오 서비스는 AIDL HAL 서비스가 있는 경우 AIDL HAL에 연결됩니다. AIDL HAL 서비스가 없으면 HIDL HAL에 연결됩니다. 방송 라디오 서비스는 사용 가능한 각 HAL 인스턴스에 맞는 라디오 모듈(예: AM, FM, DAB)을 만듭니다.

각 라디오 관리자는 라디오 유형에 따라 상응하는 라디오 모듈에서 튜너 세션을 만들도록 방송 라디오 서비스에 요청할 수 있습니다. 각 튜너 세션은 미세 조정, 단계, 취소(HAL 인터페이스에 정의됨)와 같은 메서드를 호출하여 상응하는 방송 라디오 HAL 인스턴스에서 작업을 실행할 수 있습니다. 하나의 튜너 세션이 현재 프로그램 정보, 프로그램 목록, 구성 플래그, 공급업체 매개변수와 같은 HAL 업데이트에 관한 HAL 인스턴스에서 콜백을 수신하면 업데이트에 관한 콜백은 동일한 라디오 모듈에 연결된 모든 라디오 튜너로 전송됩니다.

방송 라디오 HAL

방송 라디오의 HIDL 및 AIDL 인터페이스와 이 둘의 차이점에 관한 자세한 내용은 방송 라디오 HAL 인터페이스를 참고하세요.

방송 라디오 하드웨어 추상화 계층

다음 섹션에서는 하드웨어 계층을 사용하여 방송 라디오를 구현하는 방법을 설명합니다.

방송 라디오 HAL 인터페이스

방송 라디오 HAL은 AM/FM 및 DAB 라디오와 같은 방송 라디오를 구현하기 위한 하드웨어 수준의 데이터 구조와 인터페이스를 제공합니다.

HIDL 2.0 및 AIDL 인터페이스

방송 라디오 HAL은 다음 섹션에 설명된 인터페이스를 사용합니다.

공지사항 리스너

IAnnouncementListener는 공지사항 리스너의 콜백 인터페이스로, 방송 라디오 HAL에 등록하여 공지사항을 수신할 수 있습니다. 인터페이스에는 다음과 같은 메서드가 있습니다.

IAnnouncementListener
설명: 공지사항 목록이 변경될 때마다 호출됩니다.
HIDL 2.0 oneway onListUpdated(vec<Announcement> announcements)
AIDL oneway void onListUpdated(in Announcement[] announcements)
핸들 닫기

ICloseHandle은 활성 인터페이스가 필요하지 않은 콜백을 삭제하는 일반적인 닫기 핸들입니다.

ICloseHandle
설명: 핸들을 닫습니다.
HIDL 2.0 close()
AIDL void close()

콜백 인터페이스

ITunerCallback은 방송 라디오 HAL에서 HAL 클라이언트 서비스에 업데이트를 전송하기 위해 호출하는 콜백 인터페이스입니다.

ITunerCallback
설명: 미세 조정 작업(미세 조정, 탐색(AIDL) 또는 검색(HIDL), 단계 성공)이 비동기식으로 실패할 때 HAL에서 호출합니다.
HIDL 2.0 oneway onCurrentProgramInfoChanged(ProgramInfo info)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
설명: 미세 조정, 탐색(AIDL)이나 검색(HIDL) 또는 단계가 성공할 때 호출됩니다.
HIDL 2.0 oneway onTuneFailed(Result result, ProgramSelector selector)
AIDL void onTuneFailed(in Result result, in ProgramSelector selector)
설명: 미세 조정, 탐색(AIDL)이나 검색(HIDL) 또는 단계가 성공할 때 호출됩니다.
HIDL 2.0 oneway onCurrentProgramInfoChanged(ProgramInfo info)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
설명: 프로그램 목록이 업데이트되면 호출됩니다. 각 청크의 크기는 500KiB로 제한해야 합니다.
HIDL 2.0 oneway onProgramListUpdated(ProgramListChunk chunk)
AIDL oneway onProgramListUpdated(ProgramListChunk chunk)
설명: 안테나가 연결되거나 연결 해제될 때 호출됩니다.
HIDL 2.0 oneway onAntennaStateChange(bool connected)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
설명: HAL에서 공급업체별 매개변수 값이 내부적으로 업데이트될 때 호출됩니다(HAL 클라이언트에서 setParameters를 호출한 후에는 호출해서는 안 됨).
HIDL 2.0 oneway onParametersUpdated(vec<VendorKeyValue> parameters)
AIDL void onParametersUpdated(in VendorKeyValue[] parameters)
설명: AIDL의 새로운 기능입니다. HAL에서 구성 플래그가 내부적으로 업데이트될 때 호출됩니다(HAL 클라이언트에서 setConfigFlag를 호출한 후에는 호출해서는 안 됨).
HIDL 2.0 적용 불가능
AIDL void onConfigFlagUpdated(in ConfigFlag flag, in boolean value)

기본 방송 라디오 HAL 인터페이스

IBroadcastRadio는 방송 라디오 HAL의 기본 인터페이스입니다. HIDL 2.0 HAL에서는 튜너의 ITunerSession 인터페이스를 사용하여 작업을 호출합니다. 하지만 한 번에 최대 하나의 튜너가 활성 상태가 됩니다(각 방송 라디오 HAL 인스턴스에 튜너 칩이 하나만 있는 경우). ITunerSession은 AIDL 인터페이스에서 삭제되고 해당 인터페이스는 IBroadcastRadio로 이동했습니다.

IBroadcastRadio
설명: 모듈 및 기능에 관한 설명을 가져옵니다.
HIDL 2.0 getProperties() generates (Properties properties)
AIDL Properties getProperties()
설명: 현재 또는 가능한 AM/FM 지역 구성을 가져옵니다.
HIDL 2.0 getAmFmRegionConfig(bool full) generates (Result result, AmFmRegionConfig config)
AIDL AmFmRegionConfig getAmFmRegionConfig(bool full)
설명: 현재 DAB 지역 구성을 가져옵니다.
HIDL 2.0 getDabRegionConfig() generates (Result result, vec<DabTableEntry> config)
AIDL DabTableEntry[] getDabRegionConfig()
설명: 라디오 모듈 캐시에서 이미지를 가져옵니다. 바인더 트랜잭션 버퍼에 관한 엄격한 제한으로 인해 AIDL에서 이미지 크기는 1MB 미만이어야 합니다.
HIDL 2.0 getImage(uint32_t id) generates (vec<uint8_t> image)
AIDL byte[] getImage(in int id)
설명: 공지사항 리스너를 등록합니다.
HIDL 2.0 registerAnnouncementListener(vec<AnnouncementType> enabled,IAnnouncementListener listener) generates (Result result, ICloseHandle closeHandle)
AIDL ICloseHandle registerAnnouncementListener(in IAnnouncementListener listener, in AnnouncementType[] enabled)
설명:
  • HIDL HAL: 새 튜너 세션이 열리면 이전 세션은 종료해야 합니다.
  • AIDL HAL: 튜너 세션을 사용할 수 없으므로 튜너 콜백만 설정하면 됩니다. 튜너 콜백이 있으면 이전 콜백은 설정 해제해야 합니다.
HIDL 2.0 openSession(ITunerCallback callback)(Result result, ITunerSession session)을 생성합니다.
AIDL void setTunerCallback(in ITunerCallback callback)
설명:
  • HIDL HAL: 튜너 세션 종료는 성공해야 하며 한 번만 실행해야 합니다.
  • AIDL HAL: 튜너가 없으며 튜너 콜백만 설정 해제하면 됩니다.
HIDL 2.0 close()
AIDL unsetTunerCallback()
설명: 지정된 프로그램에 맞춥니다.
HIDL 2.0 tune(ProgramSelector program) generates (Result result)
AIDL void tune(in ProgramSelector program)
설명: 방송 중인 유효한 다음 프로그램을 찾습니다. AIDL의 혼동을 피하기 위해 scanseek로 이름을 바꿨습니다.
HIDL 2.0 scan(bool directionUp, bool skipSubChannel) generates (Result result)
AIDL void seek(in boolean directionUp, in boolean skipSubChannel)
설명: 다른 프로그램에서 사용할 수 없는 인접 채널로 이동합니다.
HIDL 2.0 step(bool directionUp) generates (Result result)
AIDL void step(in boolean directionUp)
설명: 대기 중인 미세 조정, 검색(HIDL)이나 탐색(AIDL) 또는 단계 작업을 취소합니다.
HIDL 2.0 cancel()
AIDL void cancel()
설명: 프로그램 목록에 필터를 적용하고 onProgramListUpdated 콜백을 통해 프로그램 목록 업데이트를 전송하기 시작합니다.
HIDL 2.0 startProgramListUpdates(ProgramFilter filter) generates (Result result)
AIDL void startProgramListUpdates(in ProgramFilter filter)
설명: 프로그램 목록 업데이트 전송을 중지합니다.
HIDL 2.0 stopProgramListUpdates()
AIDL void stopProgramListUpdates()
설명: 지정된 구성 플래그의 현재 설정을 가져옵니다.
HIDL 2.0 isConfigFlagSet(ConfigFlag flag) generates (Result result, bool value)
AIDL boolean isConfigFlagSet(in ConfigFlag flag)
설명: 지정된 구성 플래그를 설정합니다.
HIDL 2.0 setConfigFlag(ConfigFlag flag, bool value) generates (Result result)
AIDL void setConfigFlag(in ConfigFlag flag, boolean value)
설명: 공급업체별 매개변수 값을 설정합니다.
HIDL 2.0 setParameters(vec<VendorKeyValue> parameters)

다음을 생성합니다.

(vec<VendorKeyValue> results)
AIDL VendorKeyValue[] setParameters(in VendorKeyValue[] parameters)
설명: 공급업체별 매개변수 값을 가져옵니다.
HIDL 2.0 getParameters(vec<string> keys) generates (vec<VendorKeyValue> parameters)
AIDL VendorKeyValue[] getParameters(in String[] keys)

인터페이스 설명

비동기 동작

각 미세 조정 작업(예: 미세 조정, 검색(HIDL)이나 탐색(AIDL), 단계)은 시간이 오래 걸릴 수 있고 스레드는 오랫동안 차단되어서는 안 되므로 작업에서는 시간이 오래 걸리는 작업을 나중에 발생하도록 예약하고 상태나 결과를 빠르게 반환해야 합니다. 구체적으로 각 작업은 다음과 같아야 합니다.

  • 대기 중인 모든 미세 조정 작업을 취소합니다.
  • 메서드 입력과 튜너의 상태에 따라 작업을 처리할 수 있는지 확인합니다.
  • 미세 조정 작업을 예약하고 Result(HIDL) 또는 status(AIDL)를 즉시 반환합니다. Result 또는 statusOK이면 미세 조정 작업이 실패하거나(예: 시간 제한으로 인해) 완료될 때 튜너 콜백 tuneFailed 또는 currentProgramInfoChanged를 호출해야 합니다.

마찬가지로 startProgramListUpdates는 프로그램 목록을 업데이트하는 시간이 오래 걸리는 작업을 나중에 발생하도록 하고 상태나 결과를 신속하게 반환하도록 예약합니다. 이 메서드는 먼저 대기 중인 업데이트 요청을 취소한 후 업데이트 작업을 예약하고 결과를 빠르게 반환합니다.

경합 상태

미세 조정 작업(예: 미세 조정, 검색(HIDL)이나 탐색(AIDL), 단계)의 비동기 동작으로 인해 작업 취소와 미세 조정 작업 사이에 경합 상태가 발생합니다. HAL이 미세 조정 작업을 완료한 후 그리고 콜백이 완료되기 전에 cancel을 호출하면 cancel을 무시할 수 있고 콜백을 완료하여 HAL 클라이언트에서 수신해야 합니다.

마찬가지로 HAL이 프로그램 목록 업데이트를 완료한 후 그리고 onCurrentProgramInfoChanged 콜백이 완료되기 전에 stopProgramListUpdates를 호출하면 stopProgramListUpdates를 무시할 수 있고 콜백을 완료해야 합니다.

데이터 크기 한도

바인더 트랜잭션 버퍼에는 엄격한 한도가 있으므로 잠재적으로 크기가 큰 데이터를 전달하는 일부 인터페이스 메서드의 데이터 한도는 AIDL HAL에 명확하게 설명되어 있습니다.

  • getImage는 반환되는 이미지 크기가 1MB 미만이어야 합니다.
  • onProgramListUpdate는 각 chunk의 크기가 500KiB 미만이어야 합니다. 더 큰 프로그램 목록은 HAL 구현에 의해 여러 청크로 분할되고 여러 콜백을 통해 전송되어야 합니다.

AIDL HAL 데이터 구조 변경사항

인터페이스 변경사항 외에도 이러한 변경사항은 AIDL을 활용하는 방송 라디오 AIDL HAL에 정의된 데이터 구조에 적용되었습니다.

  • Constant enum은 AIDL에서 삭제되고 IBroadcastRadio에서 const int로 정의됩니다. 한편 ANTENNA_DISCONNECTED_TIMEOUT_MSANTENNA_STATE_CHANGE_TIMEOUT_MS로 이름이 변경됩니다. 새 const int TUNER_TIMEOUT_MS가 추가되었습니다. 모든 미세 조정, 탐색, 단계 작업은 이 시간 내에 완료되어야 합니다.
  • enum RDSDeemphasis가 AIDL에서 삭제되고 AmFmRegionConfig에서 const int로 정의됩니다. 이에 따라 ProgramInfofmDeemphasisfmRds가 모두 각 플래그의 비트 계산 결과인 int로 선언됩니다. 한편 D50D75는 각각 DEEMPHASIS_D50DEEMPHASIS_D75로 이름이 변경됩니다.
  • enum ProgramInfoFlags가 AIDL에서 삭제되고 ProgramInfo에서 const int로 정의되며 접두사 FLAG_가 추가됩니다. 이에 따라 ProgramInfoinfoFlags는 플래그의 비트 계산 결과인 int로 선언됩니다. 또한 TUNEDFLAG_TUNABLE로 이름이 변경되어 채널에 맞출 수 있다는 정의를 더 잘 설명합니다.
  • AmFmBandRange에서 scanSpacingseekSpacing으로 이름이 변경됩니다. scan이 AIDL에서 seek로 이름이 변경되었기 때문입니다.
  • union 개념이 AIDL에 도입되었으므로 HIDL HAL에 정의된 MetadataKeyMetadata가 더 이상 사용되지 않습니다. AIDL union Metadata는 AIDL HAL에 정의되어 있습니다. 이전에 MetadataKey에 있던 각 enum 값은 이제 해당 정의에 따라 문자열 또는 int 유형이 있는 Metadata의 필드입니다.

라디오 컨트롤 구현

라디오 컨트롤 구현은 MediaSessionMediaBrowse에 기반하며 이를 통해 미디어와 음성 어시스턴트 앱에서 라디오를 제어할 수 있습니다. 자세한 내용은 developer.android.com에서 차량용 미디어 앱 빌드를 참고하세요.

미디어 탐색 트리 구현은 packages/apps/Car/libs의 car-broadcastradio-support 라이브러리에 제공됩니다. 이 라이브러리에는 URI로 변환하거나 URI에서 변환할 수 있는 확장된 ProgramSelector도 포함되어 있습니다. 라디오 구현 시 이 라이브러리를 사용하여 연결된 탐색 트리를 빌드하는 것이 좋습니다.

미디어 소스 전환

라디오와 미디어에 표시되는 다른 앱 사이에 원활하게 전환하기 위해 car-media-common 라이브러리에는 라디오 앱에 통합되어야 하는 클래스가 포함되어 있습니다. MediaAppSelectorWidget은 라디오 앱의 XML에 포함될 수 있습니다(참조 미디어와 라디오 앱에서 사용되는 아이콘과 드롭다운).

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

이 위젯은 전환할 수 있는 미디어 소스 목록을 표시하는 AppSelectionFragment를 실행합니다. 제공된 것 이외의 UI가 필요한 경우 맞춤 위젯을 만들어 전환이 표시되어야 할 때 AppSelectionFragment를 실행할 수 있습니다.

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

샘플 구현은 packages/apps/Car/Radio에 있는 참조 라디오 앱 구현에 제공됩니다.

세부 제어 사양

MediaSession(MediaSession.Callback을 통한) 인터페이스는 현재 재생 중인 라디오 프로그램의 제어 메커니즘을 제공합니다.

  • onPlay, onStop. 라디오 재생 음소거(음소거 해제)
  • onPause. 시간 이동 시 일시중지(지원되는 경우)
  • onPlayFromMediaId. 최상위 폴더의 모든 콘텐츠 재생. 예: "FM 재생해 줘" 또는 "라디오 재생해 줘"
  • onPlayFromUri. 특정 주파수 재생. 예: "88.5 FM 틀어 줘"
  • onSkipToNext, onSkipToPrevious. 다음 또는 이전 채널에 맞춤
  • onSetRating. 즐겨찾기에 추가 또는 즐겨찾기에서 삭제

MediaBrowser는 최상위 디렉터리의 세 가지 유형에 관해 조정 가능한 MediaItem을 노출합니다.

  • (선택사항) 프로그램(채널). 이 모드는 일반적으로 듀얼 튜너 라디오에서 사용하여 사용자 위치에서 제공되는 모든 조정 가능한 라디오 채널을 나타냅니다.
  • 즐겨찾기. 즐겨찾기 목록에 추가된 라디오 프로그램 중 일부를 사용할 수 없습니다(수신 범위 밖).
  • 대역 채널. 현재 지역에서 실제로 가능한 모든 채널(87.9, 88.1, 88.3, 88.5, 88.7, 88.9, 89.1 등). 모든 대역에는 별도의 최상위 디렉터리가 있습니다.
MediaBrowserService 트리 구조
그림 2. MediaBrowserService 트리 구조

이러한 각 폴더(AM/FM/프로그램)의 각 요소는 MediaSession과 함께 조정에 사용할 수 있는 URI가 포함된 MediaItem입니다. 각 최상위 폴더(AM/FM/Programs)는 MediaSession과 함께 재생을 트리거하는 데 사용할 수 있고 OEM의 재량권에 달려 있는 mediaId가 포함된 MediaItem입니다. 예를 들어 "FM 재생해 줘", "AM 재생해 줘", "라디오 재생해 줘"는 모두 mediaId를 사용하여 OEM 라디오 앱으로 전송하는 구체적이지 않은 라디오 쿼리입니다. 일반적인 요청과 mediaId에 따라 재생할 항목은 라디오 앱이 결정합니다.

MediaSession

방송 스트림을 일시중지하는 개념이 없으므로 재생, 일시중지, 중지 작업이 항상 라디오에 적용되지는 않습니다. 라디오에서 중지 작업은 스트림 음소거와 관련이 있고 재생은 음소거 삭제와 관련이 있습니다.

일부 라디오 튜너(또는 앱)는 콘텐츠를 캐시하고 나중에 재생하여 방송 스트림 일시중지를 시뮬레이션하는 기능을 제공합니다. 이러한 경우에는 onPause를 사용합니다.

mediaId 및 URI 작업에서 재생은 MediaBrowser 인터페이스에서 가져온 채널에 맞추기 위한 것입니다. mediaId는 라디오 앱에서 제공하는 임의 문자열로, 고유하고(주어진 ID가 항목 하나만 가리킴) 안정적인(주어진 항목의 ID가 전체 세션에 걸쳐 동일함) 값을 부과하여 주어진 채널을 식별합니다. URI는 잘 정의된 스키마입니다. 즉, URI 기반 ProgramSelector 형식입니다. 이를 통해 uniquity 속성이 보존됩니다. 이 속성은 안정적일 필요는 없지만 채널이 다른 주파수로 이동할 때 변경될 수 있습니다.

의도적으로 onPlayFromSearch는 사용되지 않습니다. MediaBrowser 트리에서 검색결과를 선택하는 것은 클라이언트(호환 앱)의 책임입니다. 이러한 책임을 라디오 앱으로 옮기면 복잡성이 증가하고 문자열 쿼리가 표시되어야 하는 방식에 관한 공식적인 계약이 필요하며 결과적으로 다양한 하드웨어 플랫폼에서 고르지 못한 사용자 환경이 발생합니다.

참고: 라디오 앱에는 MediaBrowser 인터페이스를 통해 클라이언트에 노출되지 않은 채널 이름을 검색하는 데 유용할 수 있는 추가 정보가 포함되어 있지 않습니다.

다음 또는 이전 채널로 건너뛰기는 현재 컨텍스트에 따라 다릅니다.

  • 앱이 즐겨찾기 목록의 채널에 맞춰지면 앱은 즐겨찾기 목록에서 다음 채널로 이동할 수 있습니다.
  • 프로그램 목록에서 채널을 청취하면 채널 번호에 따라 정렬된 사용 가능한 다음 채널에 맞출 수 있습니다.
  • 임의 채널을 청취하면 방송 신호가 없더라도 실제 다음 채널에 맞출 수 있습니다.

라디오 앱은 이러한 작업을 처리합니다.

오류 처리

TransportControls 작업(재생, 중지, 다음)은 작업 성공 여부에 관한 피드백을 제공하지 않습니다. 오류를 나타낼 유일한 방법은 오류 메시지와 함께 MediaSession 상태를 STATE_ERROR로 설정하는 것입니다.

라디오 앱은 이러한 작업을 처리하여 실행하거나 오류 상태를 설정해야 합니다. 재생 명령어를 즉시 실행하지 않으면 이 명령어가 실행되는 동안 재생 상태가 STATE_CONNECTING(직접 조정의 경우) 또는 STATE_SKIPPING_TO_PREVIOUS/ NEXT로 변경되어야 합니다.

클라이언트는 PlaybackState를 주시하고 세션이 현재 프로그램을 요청되었거나 오류 상태로 전환된 것으로 변경했는지 확인해야 합니다. STATE_CONNECTING은 30초를 초과할 수 없습니다. 그러나 주어진 AM/FM 주파수에 직접 맞추면 훨씬 더 빠르게 실행됩니다.

즐겨찾기 추가 및 삭제

MediaSession에는 즐겨찾기를 제어하는 데 사용할 수 있는 평가 지원 기능이 있습니다. RATING_HEART 유형 평가로 호출된 onSetRating은 현재 맞춘 채널을 즐겨찾기 목록에 추가하거나 목록에서 삭제합니다.

기존 미리 설정과 달리 이 모델은 저장된 각 즐겨찾기가 숫자 슬롯(일반적으로 1~6)에 할당되었을 때 정렬되지 않은 무한한 즐겨찾기 목록을 가정합니다. 따라서 미리 설정 기반 시스템은 onSetRating 작업과 호환되지 않습니다.

MediaSession API의 제한사항은 현재 맞춰진 채널만 추가하거나 삭제할 수 있다는 점입니다. 예를 들어 항목을 먼저 선택해야 삭제할 수 있습니다. 이는 호환 앱과 같은 MediaBrowser 클라이언트의 제한사항일 뿐입니다. 라디오 앱은 이와 유사하게 제한되지 않습니다. 이 부분은 앱에서 즐겨찾기를 지원하지 않을 때 선택사항입니다.

MediaBrowser

지정된 라디오 기술에 적합한 임의 채널에 맞출 때 어떤 주파수나 실제 채널 이름이 주어진 지역에 유효한지 표시하기 위해 각 대역에 유효한 모든 채널(주파수)이 나열됩니다. 미국 지역에서는 87.8~108.0MHz 범위(0.2MHz 간격 사용)의 FM 채널 101개와 530~1700kHz 범위(10kHz 간격 사용)의 AM 채널 117개에 이릅니다. HD 라디오는 동일한 채널 공간을 사용하므로 별도로 표시되지 않습니다.

현재 사용 가능한 라디오 프로그램 목록은 직접 오디오 방송(DAB) 앙상블로 그룹화와 같은 표시 체계를 허용하지 않는다는 점에서 단조롭습니다.

즐겨찾기 목록의 항목은 조정이 불가능할 수 있습니다. 특정 프로그램의 범위를 벗어난 경우를 예로 들 수 있습니다. 라디오 앱은 항목이 미리 맞춰질 수 있는지 감지하거나 감지하지 못할 수 있습니다. 이 경우 항목을 재생 가능으로 표시하지 않을 수 있습니다.

최상위 폴더를 식별하기 위해 블루투스에서 사용하는 동일한 메커니즘이 적용됩니다. 즉, MediaDescription 객체의 Extras 번들에는 블루투스에서 EXTRA_BT_FOLDER_TYPE으로 그런 것처럼 튜너별 필드가 포함되어 있습니다. 방송 라디오의 경우에는 공개 API에서 다음과 같은 새 필드를 정의하게 됩니다.

  • EXTRA_BCRADIO_FOLDER_TYPE = "android.media.extra.EXTRA_BCRADIO_FOLDER_TYPE". 다음 값 중 하나:
    • BCRADIO_FOLDER_TYPE_PROGRAMS = 1. 현재 사용 가능한 프로그램
    • BCRADIO_FOLDER_TYPE_FAVORITES = 2. 즐겨찾기
    • BCRADIO_FOLDER_TYPE_BAND = 3. 특정 대역의 모든 실제 채널

    라디오별 맞춤 메타데이터 필드를 정의하지 않아도 됩니다. 모든 관련 데이터가 기존 MediaBrowser.MediaItem 체계에 적합하기 때문입니다.

    • 프로그램 이름(RDS PS, DAB 서비스 이름). MediaDescription.getTitle.
    • FM 주파수. URI(ProgramSelector 참고) 또는 MediaDescription.getTitle(항목이 BROADCASTRADIO_FOLDER_TYPE_BAND 폴더에 있는 경우)
    • 라디오 고유 식별자(RDS PI, DAB SId). ProgramSelector로 파싱된 MediaDescription.getMediaUri

    일반적으로 현재 프로그램이나 즐겨찾기 목록에서 항목의 FM 주파수를 가져오지 않아도 됩니다. 클라이언트가 미디어 ID에서 작동해야 하기 때문입니다. 그러나 표시 목적 등으로 가져와야 한다면 URI에 표시되고 ProgramSelector로 파싱될 수 있습니다. 하지만 현재 세션 내에서 항목을 선택하는 데 URI를 사용하지 않는 것이 좋습니다. 자세한 내용은 ProgramSelector를 참고하세요.

    성능 또는 바인더 관련 문제를 방지하기 위해 MediaBrowser 서비스는 페이지로 나누기를 지원해야 합니다.

    참고: 페이지로 나누기는 옵션 처리 없이 onLoadChildren() 변형에서 기본적으로 구현됩니다.

    모든 목록 유형(원시 채널, 찾은 프로그램, 즐겨찾기)의 관련 항목은 mediaId가 다를 수 있습니다(라디오 앱에 따라 달라짐, 지원 라이브러리마다 다름). URI(ProgramSelector 형식)는 대부분의 경우 원시 채널과 찾은 프로그램 간에 다르지만(RDS가 없는 FM은 예외) 찾은 프로그램과 즐겨찾기 간에는 대부분 동일합니다(AF를 업데이트한 경우는 예외).

    여러 목록 유형에서 항목 mediaId가 다양하면 항목에 여러 작업을 실행할 수 있습니다. 최근 선택한 MediaItem의 폴더에 따라 onSkipToNext에서 즐겨찾기 목록 또는 모든 프로그램 목록을 순회할 수 있습니다(MediaSession 참고).

    특별 미세 조정 작업

    프로그램 목록을 통해 사용자는 특정 채널에 맞출 수 있지만 "FM에 맞춰 줘" 등과 같은 일반적인 요청은 할 수 없습니다. 이와 같이 요청하면 최근에 청취한 FM 대역의 채널에 맞춰질 수 있습니다.

    이러한 작업을 지원하기 위해 일부 최상위 디렉터리에는 FLAG_PLAYABLE 플래그가 설정되어 있습니다. 폴더의 경우 FLAG_BROWSABLE도 설정되어 있습니다.

    작업 맞춤 대상 실행 방법
    라디오 재생 모든 라디오 채널 startService(ACTION_PLAY_BROADCASTRADIO)

    또는

    playFromMediaId(MediaBrowser.getRoot())
    FM 재생 모든 FM 채널 FM 대역의 mediaId에서 재생

    어떤 프로그램에 맞출지는 앱에 따라 다릅니다. 이는 일반적으로 지정된 목록에서 가장 최근에 맞춰진 채널입니다. ACTION_PLAY_BROADCASTRADIO에 관한 자세한 내용은 일반 재생 인텐트를 참고하세요.

    탐색 및 서비스 연결

    PackageManager는 방송 라디오 트리를 제공하는 MediaBrowserService를 직접 찾을 수 있습니다. 이렇게 하려면 ACTION_PLAY_BROADCASTRADIO 인텐트(일반 재생 인텐트 참고)와 MATCH_SYSTEM_ONLY 플래그를 사용하여 resolveService를 호출합니다. 라디오를 제공하는 모든 서비스를 찾으려면(서비스가 두 개 이상 있을 수 있음. 예: 개별 AM/FM과 위성) queryIntentServices를 사용하세요.

    확인된 서비스는 android.media.browse.MediaBrowserService 바인드 인텐트도 처리합니다. GTS로 확인됩니다.

    선택한 MediaBrowserService에 연결하려면 지정된 서비스 구성요소 및 connectMediaBrowser 인스턴스를 만듭니다. 연결한 후에는 MediaSession 핸들을 getSessionToken을 통해 가져올 수 있습니다.

    라디오 앱은 서비스의 onGetRoot 구현에서 연결이 허용되는 클라이언트 패키지를 제한할 수 있습니다. 라디오 앱에서는 시스템 앱이 허용 없이 연결할 수 있도록 해야 합니다. 허용 목록에 관한 자세한 내용은 어시스턴트 앱 패키지 및 서명 수락을 참고하세요.

    소스별 앱(예: 라디오 앱)이 이러한 소스 지원이 없는 기기에 설치되면 여전히 ACTION_PLAY_BROADCASTRADIO 인텐트를 처리하는 것으로 자체를 알리지만 MediaBrowser 트리에는 라디오별 태그가 포함되지 않습니다. 따라서 지정된 소스를 기기에서 사용할 수 있는지 확인하려는 클라이언트는 다음을 실행해야 합니다.

    1. 라디오 서비스를 탐색합니다(ACTION_PLAY_BROADCASTRADIO의 경우 resolveService 호출).
    2. MediaBrowser를 만든 후 연결합니다.
    3. 추가 EXTRA_BCRADIO_FOLDER_TYPE을 사용하여 MediaItem의 존재를 확인합니다.

    참고: 대부분의 경우 클라이언트는 사용 가능한 모든 MediaBrowser 트리를 검사하여 지정된 기기에서 사용 가능한 모든 소스를 감지해야 합니다.

    대역 이름

    대역 목록은 폴더 유형 태그가 BCRADIO_FOLDER_TYPE_BAND로 설정된 일련의 최상위 디렉터리로 표현됩니다. MediaItem의 제목은 대역 이름을 나타내는 현지화된 문자열입니다. 대부분의 경우 영어 번역과 동일하지만 클라이언트는 이러한 가정에 의존할 수 없습니다.

    특정 대역을 찾는 데 안정적인 메커니즘을 제공하기 위해 대역 폴더에는 추가 태그 EXTRA_BCRADIO_BAND_NAME_EN이 추가됩니다. 현지화되지 않은 대역 이름으로, 다음과 같은 사전 정의된 값 중 하나만 사용할 수 있습니다.

    • AM
    • FM
    • DAB

    대역이 이 목록에 없으면 대역 이름 태그를 설정하면 안 됩니다. 그러나 대역이 목록에 있으면 태그 집합이 있어야 합니다. HD 라디오는 별도의 대역을 열거하지 않습니다. AM/FM과 동일한 기본 매체를 사용하기 때문입니다.

    일반 재생 인텐트

    특정 소스(예: 라디오나 CD) 재생 전용의 각 앱은 일반 재생 인텐트를 처리하여 비활성 상태(예: 부팅 후)에서 콘텐츠 재생을 시작할 수 있어야 합니다. 재생할 콘텐츠를 선택하는 방법은 앱에 따라 다르지만 일반적으로 최근에 재생한 라디오 프로그램이나 CD 트랙입니다. 각 오디오 소스마다 별도의 인텐트가 정의되어 있습니다.

    • android.car.intent.action.PLAY_BROADCASTRADIO
    • android.car.intent.action.PLAY_AUDIOCD: CD-DA 또는 CD-Text
    • android.car.intent.action.PLAY_DATADISC: CD/DVD와 같은 광학 데이터 디스크. CD-DA는 아님(혼합 모드 CD일 수 있음)
    • android.car.intent.action.PLAY_AUX: AUX 포트를 지정하지 않음
    • android.car.intent.action.PLAY_BLUETOOTH
    • android.car.intent.action.PLAY_USB: USB 기기를 지정하지 않음
    • android.car.intent.action.PLAY_LOCAL: 로컬 미디어 저장소(내장 플래시)

    인텐트는 일반 재생 명령어에 사용되도록 선택되었습니다. 일반 재생 명령어 자체와 서비스 검색이라는 두 가지 문제를 한 번에 해결하기 때문입니다. 이러한 인텐트가 있으면 MediaBrowser 세션을 열지 않고도 이러한 간단한 작업을 실행할 수 있다는 추가 장점이 있습니다.

    서비스 검색은 실제로 이러한 인텐트로 해결되는 더 중요한 문제입니다. 서비스 검색 절차는 이와 같은 방식으로 간단 명료합니다(검색 및 서비스 연결 참고).

    클라이언트 구현을 더 쉽게 하기 위해 이러한 재생 명령어(라디오 앱에서도 구현되어야 함)를 실행하는 대체 방법이 있습니다. mediaId로 사용되는 루트 노드의 rootId로 playFromMediaId를 실행하는 것입니다. 루트 노드는 재생할 수 없지만 그 rootId는 mediaId로 사용할 수 있는 임의 문자열입니다. 하지만 클라이언트는 이러한 미묘한 차이를 알 필요가 없습니다.

    ProgramSelector

    mediaIdMediaBrowserService에서 채널을 충분히 선택할 수 있지만 mediaId는 세션에 바인딩되고 제공자 간에 일관되지 않습니다. 때에 따라 클라이언트는 세션과 기기 간에 mediaId를 유지하기 위해 절대 주파수와 같은 절대 포인터가 필요할 수 있습니다.

    디지털 라디오 방송 시대에 기본 주파수만으로는 특정 채널에 맞추기에 충분하지 않습니다. 따라서 ProgramSelector를 사용하여 아날로그나 디지털 채널에 맞춥니다. ProgramSelector는 두 부분으로 구성됩니다.

    • 기본 식별자. 특정 라디오 채널의 변경할 수 없는 안정적인 고유 식별자이지만 이 채널에 맞추기에는 충분하지 않을 수도 있습니다. 미국에서 호출 부호로 변환될 수 있는 RDS PI 코드를 예로 들 수 있습니다.
    • 보조 식별자. 다른 라디오 기술의 식별자를 비롯하여 채널에 맞추는 데 유용한 추가 식별자(예: 주파수)입니다. 아날로그 방송 대체가 있을 수 있는 DAB 채널을 예로 들 수 있습니다.

    ProgramSelectorMediaBrowser 또는 MediaSession 기반 솔루션에 적합하도록 하려면 이를 직렬화하도록 URI 스키마를 정의합니다. 스키마는 다음과 같이 정의됩니다.

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

    이 예에서 보조 식별자 부분(물음표(?) 뒤)은 선택사항이며 mediaId로 사용할 안정적인 식별자를 제공하기 위해 삭제할 수 있습니다. 예:

    • 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

    program의 권한 부분(AKA 호스트)은 향후 체계 확장을 위한 공간을 제공합니다. 식별자 유형 문자열은 IdentifierType의 HAL 2.x 정의에 있는 이름으로 정확하게 지정되며 값 형식은 십진수 또는 16진수(0x 접두사 포함)입니다.

    모든 공급업체별 식별자는 VENDOR_ 접두사로 표시됩니다. 예를 들어 VENDOR_START의 경우 VENDOR_0, VENDOR_START + 1의 경우 VENDOR_1입니다. 이러한 URI는 URI가 생성된 무선 하드웨어에만 적용되며 다른 OEM에서 만든 기기 간에 전송할 수 없습니다.

    이러한 URI는 최상위 라디오 폴더 아래의 각 MediaItem에 할당되어야 합니다. 또한 MediaSession은 playFromMediaIdplayFromUri를 모두 지원해야 합니다. 그러나 URI는 주로 라디오 메타데이터 추출(예: FM 주파수)과 영구 스토리지에 사용됩니다. URI를 모든 미디어 항목에 사용할 수 있다는 보장은 없습니다(예: 프레임워크에서 기본 ID 유형을 아직 지원하지 않는 경우). 반면 미디어 ID는 항상 작동합니다. 클라이언트가 URI를 사용하여 현재 MediaBrowser 세션의 항목을 선택하는 것은 권장되지 않습니다. 대신 playFromMediaId를 사용하세요. 하지만 제공 앱에는 선택사항이 아니며 누락된 URI는 적절한 경우를 위해 예약됩니다.

    초기 설계에서는 체계 부분 뒤에 :// 시퀀스 대신 단일 콜론을 사용했습니다. 그러나 전자는 계층적 절대 URI 참조용으로 android.net.Uri에서 지원되지 않습니다.

    기타 소스 유형

    다른 오디오 소스는 유사하게 처리할 수 있습니다. 예를 들어 보조 입력과 오디오 CD 플레이어가 있습니다.

    단일 앱은 여러 유형의 소스를 제공할 수 있습니다. 이러한 경우에는 소스 유형마다 별도의 MediaBrowserService를 만드는 것이 좋습니다. 제공된 여러 소스/MediaBrowserServices가 있는 설정에서도 단일 앱 내에 단일 MediaSession이 있는 것이 좋습니다.

    오디오 CD

    이러한 디스크를 제공하는 앱이 탐색 가능한 단일 항목(또는 시스템에 CD 체인저가 있으면 더 많음)이 있는 MediaBrowser를 노출한다는 점에서 오디오 CD와 유사합니다. 시스템이 모든 CD의 트랙에 관해 알지 못하면(예: 모든 디스크가 카트리지에 한 번에 삽입되어 모두를 읽지 않은 경우) 전체 디스크의 MediaItem은 BROWSABLE + PLAYABLE이 아닌 PLAYABLE입니다. 지정된 슬롯에 디스크가 없다면 항목은 PLAYABLEBROWSABLE도 아니지만 각 슬롯은 항상 트리에 있어야 합니다.

    오디오 CD 트리 구조
    그림 3. 오디오 CD 트리 구조

    이러한 항목은 방송 라디오 폴더와 비슷한 방식으로 표시되는데, MediaDescription API에 정의된 추가 필드를 포함하고 있을 수 있습니다.

    • EXTRA_CD_TRACK: 오디오 CD의 모든 MediaItem의 경우 1기반 트랙 번호
    • EXTRA_CD_DISK: 1기반 디스크 번호

    CD-Text 지원 시스템 및 호환 디스크의 경우 최상위 MediaItem에는 디스크 제목이 있습니다. 마찬가지로 트랙의 MediaItems에는 트랙 제목이 있습니다.

    보조 입력

    보조 입력을 제공하는 앱은 AUX in 포트를 나타내는 단일 항목(또는 여러 포트가 있는 경우 더 많음)이 있는 MediaBrowser 트리를 노출합니다. 각 MediaSession은 mediaId를 가져와서 playFromMediaId 요청을 받은 후 그 소스로 전환합니다.

    AUX 트리 구조
    그림 4. AUX 트리 구조

    각 AUX MediaItem 항목에는 'AUX' 구문 없이 포트의 현지화되지 않은 이름으로 설정된 추가 필드 EXTRA_AUX_PORT_NAME이 있습니다. 예를 들어 'AUX 1'은 '1'로, 'AUX front'는 'front'로, 'AUX'는 빈 문자열로 설정됩니다. 영어가 아닌 언어의 경우 이름 태그는 영어 문자열과 동일하게 유지됩니다. EXTRA_BCRADIO_BAND_NAME_EN과는 달리 값은 OEM에서 정의하며 사전 정의된 목록으로 제한되지 않습니다.

    하드웨어가 AUX 포트에 연결된 기기를 감지할 수 있다면 입력이 연결된 경우에만 하드웨어가 MediaItem을 PLAYABLE로 표시해야 합니다. 이 포트에 아무것도 연결되지 않았다면 하드웨어는 여전히 열거(PLAYABLE은 아님)되어야 합니다. 하드웨어에 이러한 기능이 없으면 MediaItem은 항상 PLAYABLE로 설정되어야 합니다.

    추가 필드

    다음 필드를 정의합니다.

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

    클라이언트는 최상위 MediaItem에서 요소에 EXTRA_CD_DISK 또는 EXTRA_AUX_PORT_NAME 추가 필드가 설정되어 있는지 검토해야 합니다.

    자세한 예

    다음 예에서는 이 설계의 일부인 소스 유형의 MediaBrowser 트리 구조를 다룹니다.

    방송 라디오 MediaBrowserService(ACTION_PLAY_BROADCASTRADIO 처리)

    • 채널(탐색 가능)EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_PROGRAMS
      • BBC One(재생 가능) URI: broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=90500
      • ABC 88.1(재생 가능) URI: broadcastradio://program/RDS_PI/5678?AMFM_FREQUENCY=88100
      • ABC 88.1 HD1(재생 가능) URI: broadcastradio://program/HD_STATION_ID_EXT/158241DEADBEEF?AMFM_FREQUENCY=88100&RDS_PI=5678
      • ABC 88.1 HD2(재생 가능) URI: broadcastradio://program/HD_STATION_ID_EXT/158242DEADBEFE
      • 90.5 FM(재생 가능) - RDS가 없는 FM URI: broadcastradio://program/AMFM_FREQUENCY/90500
      • 620 AM(재생 가능) URI: broadcastradio://program/AMFM_FREQUENCY/620
      • BBC One(재생 가능) URI: broadcastradio://program/DAB_SID_EXT/1E24102?RDS_PI=1234
    • 즐겨찾기(탐색 가능, 재생 가능) EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_FAVORITES
      • BBC One(재생 가능) URI: broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=101300
      • BBC Two(재생 불가) URI: broadcastradio://program/RDS_PI/1300?AMFM_FREQUENCY=102100
    • AM(탐색 가능, 재생 가능) EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_BANDEXTRA_BCRADIO_BAND_NAME_EN="AM"
      • 530 AM(재생 가능) URI: broadcastradio://program/AMFM_FREQUENCY/530
      • 540 AM(재생 가능) URI: broadcastradio://program/AMFM_FREQUENCY/540
      • 550 AM(재생 가능) URI: broadcastradio://program/AMFM_FREQUENCY/550
    • FM(탐색 가능, 재생 가능) EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_BANDEXTRA_BCRADIO_BAND_NAME_EN="FM"
      • 87.7 FM(재생 가능) URI: broadcastradio://program/AMFM_FREQUENCY/87700
      • 87.9 FM(재생 가능) URI: broadcastradio://program/AMFM_FREQUENCY/87900
      • 88.1 FM(재생 가능) URI: broadcastradio://program/AMFM_FREQUENCY/88100
    • DAB(재생 가능): EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_BANDEXTRA_BCRADIO_BAND_NAME_EN="DAB"

    오디오 CD MediaBrowserService(ACTION_PLAY_AUDIOCD 처리):

    • 디스크 1(재생 가능) EXTRA_CD_DISK=1
    • 디스크 2(탐색 가능, 재생 가능) EXTRA_CD_DISK=2
      • 트랙 1(재생 가능) EXTRA_CD_TRACK=1
      • 트랙 2(재생 가능) EXTRA_CD_TRACK=2
    • 내 음악 CD(탐색 가능, 재생 가능) EXTRA_CD_DISK=3
      • All By Myself(재생 가능) EXTRA_CD_TRACK=1
      • Reise, Reise(재생 가능) EXTRA_CD_TRACK=2
    • 빈 슬롯 4(재생 불가) EXTRA_CD_DISK=4

    AUX MediaBrowserService(ACTION_PLAY_AUX 처리)

    • AUX front(재생 가능) EXTRA_AUX_PORT_NAME="front"
    • AUX rear(재생 가능) EXTRA_AUX_PORT_NAME="rear"