이 페이지에서는 하드웨어 및 소프트웨어 수준에서 라디오를 구현하는 방법을 설명합니다.
- 시스템 구성요소는 라디오 기술 스택을 보여주고 설명합니다.
- 방송 라디오 하드웨어 추상화 계층은 OEM이 하드웨어 수준에서 AM/FM 및 디지털 오디오 방송(DAB) 라디오와 같은 방송 라디오를 구현하도록 데이터 구조와 인터페이스를 제공합니다.
- 라디오 컨트롤 구현은
MediaSession
과MediaBrowse
에 기반하며 이를 통해 미디어와 음성 어시스턴트 앱에서 라디오를 제어할 수 있습니다. 아래에 제공된 콘텐츠 외에도 자동차용 미디어 앱 빌드를 참고하세요.
시스템 구성요소
방송 라디오 스택에는 다음과 같은 구성요소가 포함됩니다.
라디오 참조 앱
라디오 컨트롤을 구현하는 방법에 관한 자세한 내용은 라디오 컨트롤 구현을 참고하세요.
샘플 자바 라디오 앱(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 2.0 | openSession(ITunerCallback callback) 이 (Result result, ITunerSession session) 을 생성합니다. |
|
AIDL | void setTunerCallback(in ITunerCallback callback) |
|
설명:
|
||
HIDL 2.0 | close() |
|
AIDL | unsetTunerCallback() |
|
설명: 지정된 프로그램에 맞춥니다. | ||
HIDL 2.0 | tune(ProgramSelector program) generates (Result result) |
|
AIDL | void tune(in ProgramSelector program) |
|
설명: 방송 중인 유효한 다음 프로그램을 찾습니다. AIDL의 혼동을 피하기 위해 scan 을 seek 로 이름을 바꿨습니다.
|
||
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
또는status
가OK
이면 미세 조정 작업이 실패하거나(예: 시간 제한으로 인해) 완료될 때 튜너 콜백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_MS
는ANTENNA_STATE_CHANGE_TIMEOUT_MS
로 이름이 변경됩니다. 새 const intTUNER_TIMEOUT_MS
가 추가되었습니다. 모든 미세 조정, 탐색, 단계 작업은 이 시간 내에 완료되어야 합니다.- enum
RDS
및Deemphasis
가 AIDL에서 삭제되고AmFmRegionConfig
에서 const int로 정의됩니다. 이에 따라ProgramInfo
의fmDeemphasis
및fmRds
가 모두 각 플래그의 비트 계산 결과인 int로 선언됩니다. 한편D50
및D75
는 각각DEEMPHASIS_D50
및DEEMPHASIS_D75
로 이름이 변경됩니다. - enum
ProgramInfoFlags
가 AIDL에서 삭제되고ProgramInfo
에서 const int로 정의되며 접두사FLAG_
가 추가됩니다. 이에 따라ProgramInfo
의infoFlags
는 플래그의 비트 계산 결과인 int로 선언됩니다. 또한TUNED
는FLAG_TUNABLE
로 이름이 변경되어 채널에 맞출 수 있다는 정의를 더 잘 설명합니다. AmFmBandRange
에서scanSpacing
이seekSpacing
으로 이름이 변경됩니다.scan
이 AIDL에서seek
로 이름이 변경되었기 때문입니다.- union 개념이 AIDL에 도입되었으므로 HIDL HAL에 정의된
MetadataKey
및Metadata
가 더 이상 사용되지 않습니다. AIDL unionMetadata
는 AIDL HAL에 정의되어 있습니다. 이전에MetadataKey
에 있던 각 enum 값은 이제 해당 정의에 따라 문자열 또는 int 유형이 있는Metadata
의 필드입니다.
라디오 컨트롤 구현
라디오 컨트롤 구현은 MediaSession
과 MediaBrowse
에 기반하며 이를 통해 미디어와 음성 어시스턴트 앱에서 라디오를 제어할 수 있습니다. 자세한 내용은 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 등). 모든 대역에는 별도의 최상위 디렉터리가 있습니다.
이러한 각 폴더(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 서비스는 페이지로 나누기를 지원해야 합니다.
EXTRA_PAGE
EXTRA_PAGE_SIZE
subscribe()
의 추가 매개변수
참고: 페이지로 나누기는 옵션 처리 없이
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에 연결하려면 지정된 서비스 구성요소 및
connect
의MediaBrowser
인스턴스를 만듭니다. 연결한 후에는 MediaSession 핸들을getSessionToken
을 통해 가져올 수 있습니다.라디오 앱은 서비스의
onGetRoot
구현에서 연결이 허용되는 클라이언트 패키지를 제한할 수 있습니다. 라디오 앱에서는 시스템 앱이 허용 없이 연결할 수 있도록 해야 합니다. 허용 목록에 관한 자세한 내용은 어시스턴트 앱 패키지 및 서명 수락을 참고하세요.소스별 앱(예: 라디오 앱)이 이러한 소스 지원이 없는 기기에 설치되면 여전히
ACTION_PLAY_BROADCASTRADIO
인텐트를 처리하는 것으로 자체를 알리지만 MediaBrowser 트리에는 라디오별 태그가 포함되지 않습니다. 따라서 지정된 소스를 기기에서 사용할 수 있는지 확인하려는 클라이언트는 다음을 실행해야 합니다.- 라디오 서비스를 탐색합니다(
ACTION_PLAY_BROADCASTRADIO
의 경우resolveService
호출). MediaBrowser
를 만든 후 연결합니다.- 추가
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-Textandroid.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
mediaId
로MediaBrowserService
에서 채널을 충분히 선택할 수 있지만 mediaId는 세션에 바인딩되고 제공자 간에 일관되지 않습니다. 때에 따라 클라이언트는 세션과 기기 간에 mediaId를 유지하기 위해 절대 주파수와 같은 절대 포인터가 필요할 수 있습니다.디지털 라디오 방송 시대에 기본 주파수만으로는 특정 채널에 맞추기에 충분하지 않습니다. 따라서
ProgramSelector
를 사용하여 아날로그나 디지털 채널에 맞춥니다.ProgramSelector
는 두 부분으로 구성됩니다.- 기본 식별자. 특정 라디오 채널의 변경할 수 없는 안정적인 고유 식별자이지만 이 채널에 맞추기에는 충분하지 않을 수도 있습니다. 미국에서 호출 부호로 변환될 수 있는 RDS PI 코드를 예로 들 수 있습니다.
- 보조 식별자. 다른 라디오 기술의 식별자를 비롯하여 채널에 맞추는 데 유용한 추가 식별자(예: 주파수)입니다. 아날로그 방송 대체가 있을 수 있는 DAB 채널을 예로 들 수 있습니다.
ProgramSelector
가MediaBrowser
또는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은
playFromMediaId
와playFromUri
를 모두 지원해야 합니다. 그러나 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
입니다. 지정된 슬롯에 디스크가 없다면 항목은PLAYABLE
도BROWSABLE
도 아니지만 각 슬롯은 항상 트리에 있어야 합니다.이러한 항목은 방송 라디오 폴더와 비슷한 방식으로 표시되는데, MediaDescription API에 정의된 추가 필드를 포함하고 있을 수 있습니다.
EXTRA_CD_TRACK
: 오디오 CD의 모든MediaItem
의 경우 1기반 트랙 번호EXTRA_CD_DISK
: 1기반 디스크 번호
CD-Text 지원 시스템 및 호환 디스크의 경우 최상위 MediaItem에는 디스크 제목이 있습니다. 마찬가지로 트랙의 MediaItems에는 트랙 제목이 있습니다.
보조 입력
보조 입력을 제공하는 앱은 AUX in 포트를 나타내는 단일 항목(또는 여러 포트가 있는 경우 더 많음)이 있는 MediaBrowser 트리를 노출합니다. 각 MediaSession은 mediaId를 가져와서
playFromMediaId
요청을 받은 후 그 소스로 전환합니다.각 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
- BBC One(재생 가능) URI:
- 즐겨찾기(탐색 가능, 재생 가능)
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
- BBC One(재생 가능) URI:
- 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
- 530 AM(재생 가능) URI:
- 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
- 87.7 FM(재생 가능) URI:
- 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
- 트랙 1(재생 가능)
- 내 음악 CD(탐색 가능, 재생 가능)
EXTRA_CD_DISK=3
- All By Myself(재생 가능)
EXTRA_CD_TRACK=1
- Reise, Reise(재생 가능)
EXTRA_CD_TRACK=2
- All By Myself(재생 가능)
- 빈 슬롯 4(재생 불가)
EXTRA_CD_DISK=4
AUX MediaBrowserService(
ACTION_PLAY_AUX
처리)- AUX front(재생 가능)
EXTRA_AUX_PORT_NAME="front"
- AUX rear(재생 가능)
EXTRA_AUX_PORT_NAME="rear"