AudioControl HAL 구현

Android 9에서는 차량 HAL의 이전 반복에서 사용된 AUDIO_* 속성을 지원 중단하고 명시적인 함수 호출과 유형이 지정된 매개변수 목록을 포함하는 전용 Audio Control HAL로 대체했습니다.

이 새로운 HAL은 구성과 볼륨 조절을 위해 차량의 오디오 엔진과 상호작용할 시작 지점을 제공하는 기본 인터페이스 객체로 IAudioControl을 노출합니다. 시스템은 이 객체의 인스턴스를 정확히 하나만 포함하며 이 인스턴스는 시작 시 CarAudioService에서 생성합니다. 이 객체는 전통적인 Android Audio HAL의 자동차용 확장 프로그램으로, 대부분의 구현에서 Audio HAL 인터페이스를 게시하는 프로세스와 동일한 프로세스에서 IAudioControl interfaces도 게시해야 합니다.

지원되는 인터페이스

AudioControl HAL은 다음 인터페이스를 지원합니다.

  • getBusforContext. 시작 시 컨텍스트마다 한 번 호출되어 ContextNumber에서 busAddress로의 매핑을 가져옵니다. 사용 예:
    getBusForContext(ContextNumber contextNumber)
        generates (uint32_t busNumber);
    
    이 인스턴스를 사용하면 차량이 프레임워크에 각 컨텍스트의 물리적 출력 스트림을 라우팅하는 지점을 알려줄 수 있습니다. 모든 컨텍스트는 유효한 버스 번호(0 - 버스 수-1)를 반환해야 합니다. 인식할 수 없는 contextNumber가 있으면 -1이 반환됩니다. 잘못된 busNumber가 반환되는 컨텍스트는 버스 0으로 라우팅됩니다.

    이 메커니즘을 통해 동일한 busNumber와 연결되는 모든 동시 사운드는 단일 스트림으로 Audio HAL에 전송되기 전에 Android AudioFlinger에서 믹싱됩니다. 이는 차량 HAL 속성 AUDIO_HW_VARIANTAUDIO_ROUTING_POLICY를 대체합니다.
  • setBalanceTowardRight. 차량 스피커의 좌우 밸런스 설정을 제어합니다. 사용 예:
    setBalanceTowardRight(float value);
    
    스피커 볼륨을 차량의 오른쪽(+) 또는 왼쪽(-)으로 이동합니다. 0.0은 중앙에, +1.0은 맨 오른쪽으로, -1.0은 맨 왼쪽으로 이동하는 것을 의미하며, -1과 1 사이 범위를 벗어난 값은 오류입니다.
  • setFadeTowardFront. 차량 스피커의 전후방 페이드 설정을 제어합니다. 사용 예:
    setFadeTowardFront(float value);
    
    스피커 볼륨을 차량의 정면(+) 또는 후면(-) 방향으로 이동합니다. 0.0은 중앙으로, +1.0은 맨 앞으로, -1.0은 맨 뒤로 이동하는 것을 의미하며, -1과 1 사이 범위를 벗어난 값은 오류입니다.

볼륨 구성

Android Automotive 구현은 소프트웨어 믹서 대신 하드웨어 앰프를 사용하여 볼륨을 제어해야 합니다. 부작용을 피하기 위해 device/generic/car/emulator/audio/overlay/frameworks/base/core/res/res/values/config.xml에서 config_useFixedVolume 플래그를 true로 설정합니다(필요한 경우 오버레이함).

<resources>
    <!-- Car uses hardware amplifier for volume. -->
    <bool name="config_useFixedVolume">true</bool>
</resources>

config_useFixedVolume 플래그가 설정되지 않았거나 false로 설정된 경우 애플리케이션에서 AudioManager.setStreamVolume()을 호출하고 소프트웨어 믹서의 스트림 유형별로 볼륨을 변경할 수 있습니다. 이는 다른 애플리케이션에 잠재적인 영향을 미치고 소프트웨어 믹서의 볼륨 감쇠가 하드웨어 앰프에서 수신될 때 신호에서 사용 가능한 중요 비트 수가 더 적기 때문에 바람직하지 않을 수 있습니다.

볼륨 그룹 구성

CarAudioServicepackages/services/Car/service/res/xml/car_volume_group.xml에 정의된 볼륨 그룹을 사용합니다. 이 파일을 재정의하여 필요에 따라 볼륨 그룹을 다시 정의할 수 있습니다. 그룹은 런타임 시 XML 파일에 정의된 순서에 따라 식별됩니다. ID의 범위는 0에서 N-1이며, 여기서 N은 볼륨 그룹의 수입니다. 예:

<volumeGroups xmlns:car="http://schemas.android.com/apk/res-auto">
    <group>
        <context car:context="music"/>
        <context car:context="call_ring"/>
        <context car:context="notification"/>
        <context car:context="system_sound"/>
    </group>
    <group>
        <context car:context="navigation"/>
        <context car:context="voice_command"/>
    </group>
    <group>
        <context car:context="call"/>
    </group>
    <group>
        <context car:context="alarm"/>
    </group>
</volumeGroups>

이 구성에서 사용하는 속성은 packages/services/Car/service/res/values/attrs.xml에 정의되어 있습니다.

볼륨 키 이벤트 처리

Android에서는 KEYCODE_VOLUME_UP, KEYCODE_VOLUME_DOWNKEYCODE_VOLUME_MUTE를 비롯하여 볼륨 제어를 위한 여러 개의 키 코드를 정의합니다. 기본적으로 Android에서는 볼륨 키 이벤트를 애플리케이션으로 라우팅합니다. Automotive 구현은 이러한 키 이벤트를 CarAudioService로 강제 라우팅해야 하며, 그런 다음 setGroupVolume 또는 setMasterMute를 적절하게 호출할 수 있습니다.

이 동작을 강제하려면 device/generic/car/emulator/car/overlay/frameworks/base/core/res/res/values/config.xml에서 config_handleVolumeKeysInWindowManager 플래그를 true로 설정합니다.

<resources>
    <bool name="config_handleVolumeKeysInWindowManager">true</bool>
</resources>

CarAudioManager API

CarAudioManagerCarAudioService를 사용하여 차량 오디오 시스템을 구성하고 제어합니다. 관리자는 시스템 대부분의 앱에서 보이지 않지만, 차량 관련 구성요소(예: 볼륨 컨트롤러)는 CarAudioManager API를 사용하여 시스템과 상호작용할 수 있습니다.

다음 섹션에서는 Android 9에 적용된 CarAudioManager API의 변경사항을 설명합니다.

지원 중단된 API

Android 9에서는 기존 AudioManager getDeviceList API를 통해 기기 열거를 처리하므로 다음 차량 관련 함수를 지원 중단하고 삭제했습니다.

  • String[] getSupportedExternalSourceTypes()
  • String[] getSupportedRadioTypes()

Android 9에서는 AudioAttributes.AttributeUsage 또는 볼륨 그룹 기반 진입점을 사용하여 볼륨을 처리하므로 streamType을 사용하는 다음 API는 삭제되었습니다.

  • void setStreamVolume(int streamType, int index, int flags)
  • int getStreamMaxVolume(int streamType)
  • int getStreamMinVolume(int streamType)
  • void setVolumeController(IVolumeController controller)

새로운 API

Android 9에는 앰프 하드웨어 제어를 위해 신규 API가 추가됩니다(명시적으로 볼륨 그룹에 기반함).

  • int getVolumeGroupIdForUsage(@AudioAttributes.AttributeUsage int usage)
  • int getVolumeGroupCount()
  • int getGroupVolume(int groupId)
  • int getGroupMaxVolume(int groupId)
  • int getGroupMinVolume(int groupId)

또한 Android 9에서는 시스템 GUI에서 사용할 수 있도록 새로운 시스템 API를 제공합니다.

  • void setGroupVolume(int groupId, int index, int flags)
  • void registerVolumeChangeObserver(@NonNull ContentObserver observer)
  • void unregisterVolumeChangeObserver(@NonNull ContentObserver observer)
  • void registerVolumeCallback(@NonNull IBinder binder)
  • void unregisterVolumeCallback(@NonNull IBinder binder)
  • void setFadeToFront(float value)
  • Void setBalanceToRight(float value)

마지막으로, Android 9에는 외부 소스 관리를 위한 새로운 API가 추가됩니다. 이러한 API는 주로 미디어 유형에 따라 외부 소스에서 출력 버스로 오디오 라우팅을 지원하기 위한 용도입니다. 또한 이러한 API를 사용하여 타사 애플리케이션에서 외부 기기에 액세스할 수도 있습니다.

  • String[] getExternalSources(): AUX_LINE, FM_TUNER, TV_TUNERBUS_INPUT 유형의 시스템에서 사용할 수 있는 오디오 포트를 식별하는 주소 배열을 반환합니다.
  • CarPatchHandle createAudioPatch(String sourceAddress, int carUsage): 제공된 carUsage와 연결된 출력 BUS로 소스 주소를 라우팅합니다.
  • int releaseAudioPatch(CarPatchHandle patch): 제공된 패치를 삭제합니다. CarPatchHandle의 생성자가 예기치 않게 종료되면 AudioPolicyService::removeNotificationClient()에서 이를 자동으로 처리합니다.

오디오 패치 만들기

두 오디오 포트(믹스 포트 또는 기기 포트) 사이에 오디오 패치를 만들 수 있습니다. 일반적으로 믹스 포트에서 기기 포트로의 오디오 패치는 재생용이고 반대 방향은 캡처용입니다.

예를 들어 FM_TUNER 소스의 오디오 샘플을 미디어 싱크로 직접 라우팅하는 오디오 패치는 소프트웨어 믹서를 우회합니다. 그런 다음 하드웨어 믹서를 사용하여 Android 및 FM_TUNER의 오디오 샘플을 싱크에 믹싱해야 합니다. FM_TUNER 소스에서 미디어 싱크로 직접 오디오 패치를 만드는 경우 다음을 따르세요.

  • 볼륨 조절은 미디어 싱크에 적용되며 Android 및 FM_TUNER 오디오, 둘 다에 영향을 미쳐야 합니다.
  • 사용자가 간단한 앱 전환을 통해 Android와 FM_TUNER 오디오 간에 전환할 수 있어야 합니다. 명시적으로 미디어 소스를 선택하지 않아도 됩니다.

또한 Automotive 구현은 두 개의 기기 포트 사이에 오디오 패치를 만들어야 할 수도 있습니다. 이렇게 하려면, 먼저 audio_policy_configuration.xml에서 기기 포트와 가능한 경로를 선언하고 mixports를 이러한 기기 포트에 연결해야 합니다.

구성 예

device/generic/car/emulator/audio/audio_policy_configuration.xml을 참고하세요.

<audioPolicyConfiguration>
    <modules>
        <module name="primary" halVersion="3.0">
            <attachedDevices>
                <item>bus0_media_out</item>
                <item>bus1_audio_patch_test_in</item>
            </attachedDevices>
            <mixPorts>
                <mixPort name="mixport_bus0_media_out" role="source"
                        flags="AUDIO_OUTPUT_FLAG_PRIMARY">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                            samplingRates="48000"
                            channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                </mixPort>
                <mixPort name="mixport_audio_patch_in" role="sink">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                           samplingRates="48000"
                           channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
                </mixPort>
            </mixPorts>
            <devicePorts>
                <devicePort tagName="bus0_media_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
                        address="bus0_media_out">
                    <profile balance="" format="AUDIO_FORMAT_PCM_16_BIT"
                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                    <gains>
                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
                                minValueMB="-8400" maxValueMB="4000" defaultValueMB="0" stepValueMB="100"/>
                    </gains>
                </devicePort>
                <devicePort tagName="bus1_audio_patch_test_in" type="AUDIO_DEVICE_IN_BUS" role="source"
                        address="bus1_audio_patch_test_in">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
                    <gains>
                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
                                minValueMB="-8400" maxValueMB="4000" defaultValueMB="0" stepValueMB="100"/>
                    </gains>
                </devicePort>
            </devicePorts>
            <routes>
                <route type="mix" sink="bus0_media_out" sources="mixport_bus0_media_out,bus1_audio_patch_test_in"/>
                <route type="mix" sink="mixport_audio_patch_in" sources="bus1_audio_patch_test_in"/>
            </routes>
        </module>
    </modules>
</audioPolicyConfiguration>

오디오 드라이버 API

getExternalSources()를 사용하여 사용할 수 있는 소스 목록(주소로 식별)을 검색한 다음 이러한 소스와 싱크 포트 사이에 오디오 사용에 따른 오디오 패치를 만들 수 있습니다. 이에 대응하는 오디오 HAL의 진입점이 IDevice.hal에 표시됩니다.

Interface IDevice {
...
/
* Creates an audio patch between several source and sink ports.  The handle
* is allocated by the HAL and must be unique for this audio HAL module.
*
* @param sources patch sources.
* @param sinks patch sinks.
* @return retval operation completion status.
* @return patch created patch handle.
*/
createAudioPatch(vec<AudioPortConfig> sources, vec<AudioPortConfig> sinks)
       generates (Result retval, AudioPatchHandle patch);


* Release an audio patch.
*
* @param patch patch handle.
* @return retval operation completion status.
*/
releaseAudioPatch(AudioPatchHandle patch) generates (Result retval);
...
}

볼륨 설정 UI 구성

Android 9에서는 볼륨 그룹 구성에서 볼륨 설정 UI를 분리합니다(볼륨 그룹 구성에서 설명하는 것처럼 오버레이 가능). 이렇게 분리한 덕분에 향후 볼륨 그룹 구성이 변경되어도 UI를 변경할 필요가 없습니다.

자동차 설정 UI에서 packages/apps/Car/Settings/res/xml/car_volume_items.xml 파일에는 정의된 각 AudioAttributes.USAGE와 연결된 UI 요소(제목 및 아이콘 리소스)가 포함됩니다. 이 파일은 각 VolumeGroup에 포함된 사용 중 첫 번째로 인식된 사용과 연결된 리소스를 사용하여 정의된 VolumeGroups의 적절한 렌더링을 제공합니다.

예를 들어 다음 예에서는 VolumeGroup을 voice_communicationvoice_communication_signalling을 모두 포함하는 것으로 정의합니다. 자동차 설정 UI의 기본 구현은 voice_communication이 파일에서 첫 번째 일치하는 사용이므로 이와 연결된 리소스를 사용하여 VolumeGroup을 렌더링합니다.

<carVolumeItems xmlns:car="http://schemas.android.com/apk/res-auto">
    <item car:usage="voice_communication"
          car:title="@*android:string/volume_call"
          car:icon="@*android:drawable/ic_audio_ring_notif"/>
    <item car:usage="voice_communication_signalling"
          car:title="@*android:string/volume_call"
          car:icon="@*android:drawable/ic_audio_ring_notif"/>
    <item car:usage="media"
          car:title="@*android:string/volume_music"
          car:icon="@*android:drawable/ic_audio_media"/>
    <item car:usage="game"
          car:title="@*android:string/volume_music"
          car:icon="@*android:drawable/ic_audio_media"/>
    <item car:usage="alarm"
          car:title="@*android:string/volume_alarm"
          car:icon="@*android:drawable/ic_audio_alarm"/>
    <item car:usage="assistance_navigation_guidance"
          car:title="@string/navi_volume_title"
          car:icon="@drawable/ic_audio_navi"/>
    <item car:usage="notification_ringtone"
          car:title="@*android:string/volume_ringtone"
          car:icon="@*android:drawable/ic_audio_ring_notif"/>
    <item car:usage="assistant"
          car:title="@*android:string/volume_unknown"
          car:icon="@*android:drawable/ic_audio_vol"/>
    <item car:usage="notification"
          car:title="@*android:string/volume_notification"
          car:icon="@*android:drawable/ic_audio_ring_notif"/>
    <item car:usage="notification_communication_request"
          car:title="@*android:string/volume_notification"
          car:icon="@*android:drawable/ic_audio_ring_notif"/>
    <item car:usage="notification_communication_instant"
          car:title="@*android:string/volume_notification"
          car:icon="@*android:drawable/ic_audio_ring_notif"/>
    <item car:usage="notification_communication_delayed"
          car:title="@*android:string/volume_notification"
          car:icon="@*android:drawable/ic_audio_ring_notif"/>
    <item car:usage="notification_event"
          car:title="@*android:string/volume_notification"
          car:icon="@*android:drawable/ic_audio_ring_notif"/>
    <item car:usage="assistance_accessibility"
          car:title="@*android:string/volume_notification"
          car:icon="@*android:drawable/ic_audio_ring_notif"/>
    <item car:usage="assistance_sonification"
          car:title="@*android:string/volume_unknown"
          car:icon="@*android:drawable/ic_audio_vol"/>
    <item car:usage="unknown"
          car:title="@*android:string/volume_unknown"
          car:icon="@*android:drawable/ic_audio_vol"/>
</carVolumeItems>

위의 구성에 사용된 속성과 값은 packages/apps/Car/Settings/res/values/attrs.xml에 선언됩니다. 볼륨 설정 UI는 다음 VolumeGroup 기반의 CarAudioManager API를 사용합니다.

  • getVolumeGroupCount(): 그려야 하는 컨트롤 수를 알 수 있습니다.
  • getGroupMinVolume()getGroupMaxVolume(): 하한 및 상한을 가져옵니다.
  • getGroupVolume(): 현재 볼륨을 가져옵니다.
  • registerVolumeChangeObserver(): 볼륨 변경 시 알림을 받습니다.