Android 14의 새로운 자동차 OEM 플러그인 서비스를 사용하면 일부 자동차 구성요소를 구성할 수 있습니다. 특히 오디오의 경우 새로운 플러그인 서비스 세 가지가 도입되었으며 이를 통해 OEM은 AAOS 기기에서 오디오 관리를 유연하게 구성할 수 있습니다.
- 오디오 포커스 제어
- 오디오 볼륨 및 음소거 제어
- 오디오 볼륨 낮추기 제어
자동차 플러그인 서비스 아키텍처
아래 그림은 자동차 서비스 및 OEM 자동차 서비스와의 관계를 대략적으로 보여줍니다. 앱 프로세스 및 자동차 서비스 프로세스와 마찬가지로 OEM 자동차 서비스 프로세스도 자체 프로세스 공간을 차지합니다.
자동차 서비스는 config_oemCarService
에 정의된 구성요소를 찾아 OEM 자동차 서비스를 시작합니다. 구성이 비어 있으면 OEM 서비스는 존재하지 않으며 시작되는 서비스도 없습니다. 구성요소는 OemCarService를 확장합니다.
자동차 오디오 서비스는 자동차 오디오 OEM 서비스를 획득하는 API를 덮어써야 합니다.
public final class OemCarServiceImp extends OemCarService {
@Override
public OemCarAudioFocusService getOemAudioFocusService();
@Override
public OemCarAudioDuckingService getOemAudioDuckingService();
@Override
public OemCarAudioVolumeService getOemAudioVolumeService();
}
예를 보려면 packages/services/Car/tests/OemCarServiceTestApp
에 정의된 참조 테스트 앱을 확인하세요.
서비스가 자동차 서비스에서 시작되더라도 자동차 오디오 서비스에서 사용할 수 있는 권한을 자동으로 상속받지는 않습니다. 따라서 OEM 서비스에 필요한 권한은 적절한 메커니즘을 사용하여 획득해야 합니다. 예를 보려면 packages/services/Car/data/etc/com.android.car.oemcarservice.testapp.xml
을 참고하세요.
OEM 서비스 아키텍처가 적용된 자동차 오디오 서비스
AAOS에서 자동차 오디오 서비스는 다음 작업을 관리합니다.
- 오디오 라우팅
- 오디오 포커스
- 오디오 볼륨 낮추기
- 볼륨 및 음소거
Android 14 전에는 이 동작이 매우 제한된 사례이긴 하지만 대부분 정적이었고 설정을 통해서만 수정할 수 있었습니다. Android 14에서는 자동차 오디오 서비스가 다음을 관리하는 OEM 정의 구성요소와 통신할 수 있는 메커니즘을 도입했습니다.
- 오디오 포커스
- 오디오 볼륨 낮추기
- 볼륨 및 음소거
아래 그림은 자동차 오디오 서비스와 자동차 OEM 서비스의 간소화된 아키텍처를 보여줍니다. 자동차 오디오 서비스는 자동차 OEM 오디오 서비스에서 호출하여 오디오 동작을 관리할 수 있는 다양한 후크를 정의합니다. 후자는 해당 OEM 자동차 오디오 서비스 구성요소가 정의된 경우에만 발생합니다. 그렇지 않은 경우에는 자동차 오디오 서비스에서 기본 동작을 사용합니다.
자동차 오디오 서비스와 자동차 OEM 오디오 서비스가 항상 동기화되도록 하기 위해 각 호출에서 자동차 오디오 서비스는 현재 오디오 스택 상태의 필수 부분을 자동차 OEM 오디오 서비스에 전달합니다. 예를 들어 자동차 오디오 서비스가 오디오 포커스를 평가하라는 요청을 가로채면 스택의 현재 상태를 자동차 OEM 오디오 서비스에 전달합니다. 현재 상태에는 현재 포커스 보유자와 현재 포커스 손실자가 포함되어 있습니다. 포커스 손실자는 여전히 스택의 일부이지만 일시적으로 포커스를 잃은 포커스 요청입니다.
자동차 오디오 서비스는 자동차의 모든 오디오 활동을 관리해야 합니다. 자동차 오디오 서비스가 오디오 동작의 일부분을 관리하지 않으면 자동차 OEM 오디오 서비스에 노출된 정보가 불완전해집니다. 예를 들어 OEM이 자체 오디오 포커스 정책을 등록하여 자동차 서비스의 오디오 포커스 처리를 덮어쓰면 자동차 오디오 서비스는 자동차 OEM 오디오 서비스에 완전한 정보를 제공할 수 없습니다. 이는 자동차 OEM 오디오 서비스의 결정을 내리는 기능에 영향을 미칠 수 있습니다. 자동차 오디오 서비스에 표시되지 않는 정보가 부족할 수 있기 때문입니다.
조치를 취하기 위해 자동차 오디오 서비스는 OEM 자동차 서비스를 호출합니다. 이러한 호출은 프로세스 전반에서 이루어지므로 프로세스 간 통신(IPC)이 필요합니다. IPC는 각 호출에 지연 시간을 추가합니다. OEM 서비스에서는 지연 시간을 최소화하는 것이 중요합니다.
OEM 서비스에 대한 자동차 오디오 서비스 호출이 차단되므로 OEM 서비스는 직접 API 평가 시 자동차 오디오 서비스를 호출하지 않아야 합니다. 대신 자동차 오디오 서비스는 두 프로세스 간 호출이 한 방향으로만 이동하도록 필요한 정보를 제공합니다.
OEM 자동차 오디오 서비스 정의
OEM 자동차 오디오 포커스 서비스
자동차 오디오 서비스는 오디오 정책 포커스 리스너를 등록하여 앱의 오디오 포커스 요청을 관리합니다. 자동차 오디오 서비스에는 정적 상호작용 매트릭스에 기반하여 포커스 동작을 관리하는 메커니즘이 있습니다. 매트릭스는 다음과 같은 세 가지 상호작용을 정의합니다.
동시 상호작용. 포커스 보유자가 동시에 포커스를 유지할 수 있습니다.
배타적 상호작용. 들어오는 포커스 요청은 현재 포커스 보유자에서 포커스를 가져옵니다.
거부 상호작용. 들어오는 포커스 요청이 현재 포커스 보유자에 따라 거부되었습니다.
이는 일부 자동차 사용 사례에 충분하지만 OEM 요구사항으로 인해 다를 수 있는 상호작용의 모든 요구를 충족하지는 않습니다. 이를 위해 다음과 같이 OemCarAudioFocusService
를 도입합니다.
public interface OEmCarAudioFocusService {
OemCarAuddioFocusResults evaluateAudioFocusRequest(
OemCarAudioFocusEvaluationRequest request);
void notifyAudioFocusChange(
List<AudioFocusEntry> holder,
List<AudioFocusEntry> losers, int zoneId);
}
API evaluateAudioFocusRequest
는 평가해야 하는 오디오 포커스 요청이 있을 때마다 자동차 오디오 서비스에서 호출되며 결과가 반환되는 것을 차단하는 양방향 API입니다. 요청에는 오디오 스택의 현재 상태에 관한 정보가 포함되어 있습니다.
이 정보는 focusHolders
의 현재 포커스 보유자 및 focusLosers
의 현재 포커스 손실자와 비교하여 newFocusRequest
를 평가하는 데 사용할 수 있습니다. API는 다음과 같이 결과를 반환해야 합니다.
class OemCarAudioFocusResult {
int audioZoneId;
int audioFocusEvaluationResults;
AudioFocusEntry focusResult;
List<AudioFocusEntry> newLosers;
List<AudioFocusEntry> newlyBlocked;
}
여기에는 현재 요청이 승인되었는지 지연되었는지 또는 실패했는지 나타내는 audioFocusEvaluationResults
의 실제 평가 결과에 관한 정보가 포함됩니다. 현재 포커스 스택의 변경사항은 스택 변경사항의 성격에 따라 newLosers
및 newlyBlocked
항목에 설정되어야 합니다.
여기서 newLosers
에는 이전에 포커스를 보유했지만 지금은 영구적으로 또는 일시적으로 포커스를 잃어야 하는 항목이 포함됩니다. 영구적인 포커스 손실자는 오디오 포커스 스택에서 추가로 삭제되며 일시적인 포커스 손실자는 포커스를 다시 얻거나 원래 포커스 요청자로부터 포기될 때까지 현재 포커스 손실자 스택으로 이동됩니다. 여하튼 요청의 포커스 리스너는 손실된 해당 포커스를 수신합니다.
newlyBlocked
목록에는 이전에 포커스 손실자 목록에 있었지만 이제는 새 항목으로 차단된 항목이 포함됩니다. 차단은 영구적이거나 일시적일 수 있습니다. 영구적 포커스 차단 항목은 스택에서 삭제되며 포커스 손실이 포커스 리스너에 전송됩니다. 일시적인 포커스 손실의 경우 항목은 포커스 손실자 스택에 유지되지만 새로운 포커스 차단기가 차단기 목록에 추가되며 처음 차단될 때 이전에 전송된 것처럼 포커스 손실이 전송되지는 않습니다. 요청은 모든 현재 차단기가 삭제될 때 최종적으로 차단 해제되거나 포커스가 포기되면 스택에서 삭제됩니다.
두 번째 API notifyAudioFocusChange
는 모든 오디오 포커스 요청 또는 포기 시 호출되는 단방향 API입니다. 이 API는 OEM 자동차 오디오 서비스의 동작에 영향을 미칠 수 있는 포커스 변경사항을 OEM 서비스에 알려주는 데 주로 사용됩니다.
포커스 평가 가이드라인
AAOS에서 오디오 포커스는 오디오 재생을 관리하고, 사용자에게 최적의 환경을 제공하기 위해 어떤 앱을 준수해야 하는지 결정하는 데 사용됩니다. 따라서 OEM 플러그인 서비스는 오디오 포커스 요청을 관리할 때 다음을 고려해야 합니다.
높은 우선순위의 오디오 포커스(예: 전화 통화, 긴급 상황 또는 안전)가 없으면 앱은 일시적 또는 영구적으로 오디오 포커스를 획득할 수 있어야 합니다.
미디어 포커스가 활성 상태인 동안 앱은 다음을 요청합니다.
통화 용도 포커스. 동시에 또는 독점적으로 포커스를 수신할 수 있어야 합니다.
내비게이션 용도 포커스. 동시에 또는 독점적으로 포커스를 수신할 수 있어야 합니다.
어시스턴트 용도 포커스. 동시에 또는 독점적으로 포커스를 수신할 수 있어야 합니다.
높은 우선순위의 오디오 포커스(예: 전화 통화, 긴급 재난 문자 또는 안전 알림) 앱이 활성 상태인 동안 지연된 수신 오디오 포커스 요청은 필요에 따라 승인되거나 지연되어야 합니다.
위의 제안사항은 일부만 나온 것이지만 활성 상태의 높은 우선순위 사운드가 없으면 포커스를 요청하는 앱이 포커스를 획득할 수 있도록 하는 데 도움이 될 수 있습니다. 우선순위가 높은 사운드가 활성 상태인 동안에도 지연된 포커스 요청은 여전히 존중되어야 하며 높은 우선순위의 사운드가 중지되면 포커스를 획득할 수 있어야 합니다.
OEM 자동차 볼륨 서비스
자동차 오디오 서비스는 오디오 시스템의 볼륨 조정을 수신 대기하거나 자동차 입력 서비스에서 직접 볼륨 키 이벤트를 수신 대기하여 볼륨 키 이벤트를 관리합니다. 각각의 경우 자동차 오디오 서비스의 기본 동작은 활성 오디오 플레이어와 오디오 컨텍스트 우선순위 목록에 기반하여 변경할 볼륨 그룹을 결정하는 것입니다.
Google에서는 두 가지 볼륨 우선순위 목록을 제공합니다. 첫 번째 목록은 모든 오디오 컨텍스트를 다음 순서로 고려합니다. 목록은 내림차순으로 제공되며 가장 높은 우선순위가 맨 위에 가장 낮은 우선순위가 맨 아래에 위치합니다. 예를 들어 내비게이션 오디오와 음악 오디오가 모두 동시에 활성 상태면 내비게이션 볼륨이 볼륨 키 이벤트 중에 변경됩니다.
- 내비게이션
- 통화
- 음악
- 공지사항
- 음성 명령
- 전화 벨소리
- 시스템 사운드
- 안전
- 알람
- 알림
- 차량 상태
- 긴급 상황
볼륨 키 이벤트를 좀 더 쉽게 관리하기 위해 자동차 오디오 서비스에는 다음과 같은 두 번째 오디오 컨텍스트 우선순위 목록이 있습니다.
- 통화
- 미디어
- 공지사항
- 음성 명령
이 목록도 내림차순으로 제공됩니다. 이 두 번째 목록의 목적은 좀 더 일반적인 사운드가 키 이벤트를 통해 변경될 수 있도록 하는 것입니다. 짧은 지속 시간의 일반적이지 않은 사운드는 오디오 설정 UI를 통해서만 관리할 수 있습니다.
볼륨의 실제 버전은 audioVolumeAdjustmentContextsVersion
구성을 사용하여 설정할 수 있습니다. 이 구성은 1
또는 2
(2
가 기본값)로 설정할 수 있습니다.
좀 더 유연하게 볼륨을 관리할 수 있도록 Android 14에서는 OemCarAudioVolumeService
를 도입했습니다.
public interface OemCarAudioVolumeService {
OemCarvolumeChangeInfo getSuggestedGroupForVolumeChange(
OemCarAudioVolumeRequest request, int volumeAdjustment);
}
OEM 자동차 오디오 볼륨 서비스에는 단일 메서드가 있으며 volumeAdjustment
와 OemCarAudioVolumeRequest
를 사용합니다.
class OemCarAudioVolumeRequest {
int audioZoneId;
int callState;
List<AudioAttributes> activePlaybackAttributes;
List<AudioAttributes> duckedAttributes;
List<CarVolumeGroupInfo> volumeGroupState;
}
요청의 activePlaybackAttributes
에는 활성 오디오 속성이 있습니다. duckedAttributes
는 모두 현재 볼륨을 낮춘 오디오 속성입니다. volumeGroupState
에는 볼륨 그룹의 현재 상태가 있습니다. 요청은 오디오 스택의 현재 상태를 나타내며 변경해야 하는 볼륨 그룹을 결정하는 데 사용할 수 있습니다. 결과는 OemCarVolumeChangeInfo
에 반환되어야 합니다.
class OemCarVolumeChangeInfo {
boolean change;
CarVolumeGroupInfo volumeGroupChanged;
}
change
부울은 변경된 볼륨이 있는지 나타내고 true
는 변경이 있으며 볼륨 그룹을 업데이트해야 함을 나타냅니다. volumeGroupChanged
는 변경해야 하는 실제 볼륨 그룹입니다. 이 그룹은 API에 전달된 원래 volumeAdjustment
매개변수에 따라 변경되어야 합니다. 예를 들어 결과에서 내비게이션 볼륨 그룹을 음소거해야 한다고 나타내면 부울은 true
가 되고 반환된 볼륨 그룹은 내비게이션용 볼륨 그룹이 되어야 합니다.
OEM 자동차 볼륨 낮추기 서비스
자동차 오디오 서비스는 오디오 포커스 변경사항을 모니터링하고 볼륨을 낮출 오디오 기기에 관한 신호를 AudioControl
HAL에 전송하여 오디오 볼륨 낮추기를 관리합니다.
포커스가 변경되면 모든 활성 포커스 보유자를 평가하여 다음과 같은 정적 볼륨 낮추기 규칙에 따라 볼륨을 낮춰야 하는 항목을 결정합니다.
- 긴급 상황 사운드는 통화음을 제외한 모든 소리를 낮춥니다.
- 안전은 긴급 상황 사운드를 제외한 모든 소리를 낮춥니다.
- 내비게이션은 안전 및 긴급 상황 사운드를 제외한 모든 소리를 낮춥니다.
- 통화는 안전, 긴급 상황 및 내비게이션 사운드를 제외한 모든 소리를 낮춥니다.
- 음성은 전화 벨소리를 낮춥니다.
- 음악과 공지사항은 모든 항목에서 볼륨을 낮춰야 합니다.
이러한 규칙은 일부만 나타낸 것이며 이러한 가이드라인에 따라 볼륨을 낮추는 방법을 결정하는 책임은 OEM에 있습니다. OEM은 사용 가능한 요구사항에 따라 이러한 권장사항을 좀 더 적극적으로 제어할 수 있습니다. OemCarDuckingService
가 Android 14에서 도입되었습니다.
class OemCarAudioDuckingService {
List<AudioAttributes> evaluateAttributesToDuck(
OemCarAudioVolumeRequest request);
}
이 API는 오디오 포커스 변경 시 자동차 오디오 서비스에서 호출됩니다. OEM 자동차 볼륨 서비스에서 도입된 OemCarAudioVolumeRequest
를 재사용하며 볼륨을 낮출 속성을 결정하는 데 필요한 관련 정보를 포함합니다. API에서 볼륨을 낮출 오디오 속성 목록은 현재 오디오 상태와 비교됩니다.
현재 볼륨을 낮춘 오디오 속성:
- 목록에 있음. 계속 볼륨을 낮춥니다.
- 목록에 없음. 볼륨 낮추기를 사용 중지합니다.
현재 볼륨을 낮추지 않은 오디오 속성:
- 목록에 있음. 볼륨을 낮춥니다.
- 목록에 없음. 볼륨 낮추기를 사용 중지합니다.
그러면 자동차 오디오 서비스는 오디오 속성이 속하는 오디오 출력 장치를 확인하고 이를 각각 볼륨을 낮춘 오디오 출력 장치 목록이나 볼륨을 낮추지 않은 오디오 기기 목록에 추가합니다. 이는 최종적으로 AudioControl HAL에 전송되어 하드웨어 수준에서 필요한 볼륨 낮추기를 실행합니다.
아래 그림은 OEM 볼륨 낮추기 서비스를 사용할 때 포커스 요청의 오디오 볼륨 낮추기 제어에 관한 간소화된 시퀀스 다이어그램을 보여줍니다.
시퀀스는 앱이 공개 오디오 관리자 API를 통해 오디오 포커스 관리를 요청할 때 시작됩니다. 요청은 자동차 오디오 서비스에 전달되고 결과가 결정됩니다. 오디오 포커스가 결정되면 OemCarAudioDuckingService
를 호출하는 자동차 오디오 서비스에서 오디오 볼륨 낮추기를 평가하여 어떤 오디오 속성의 볼륨을 낮춰야 하는지 평가합니다. evaluateAttributesToDuck
API에서 결과가 반환되면 볼륨을 낮출 오디오 기기가 계산되고 최종적으로 정보를 AudioControl
로 전송하여 오디오 하드웨어에 볼륨 낮추기를 적용합니다.
OEM 자동차 오디오 서비스 참조 구현
AAOS는 packages/services/Car/tests/OemCarServiceTestApp
에 OEM 자동차 서비스의 참조 구현을 제공하며 이는 OemCarAudioFocusService
, OemCarAudioDuckingService
, OemCarAudioVolumeService
와 함께 OemCarService
를 구현합니다. 후자의 경우 각 서비스는 XML 파일을 사용하여 정적 동작을 로드합니다. 예를 들어 OemCarAudioFocusServiceImp
는 oem_focus_config.xml
을 로드하며 여기에는 상호작용 매트릭스가 포함되어 있습니다. 매트릭스는 evaluateAudioFocusRequest
가 호출될 때 포커스 요청을 평가하는 데 사용됩니다.
참조 테스트 앱 디버깅
OEM 자동차 서비스 테스트 앱은 AOSP 소스 코드에 포함되어 있습니다. OEM은 필요에 따라 이를 변경할 수 있습니다. 디버깅의 경우 config_oemCarService
구성을 사용하여 테스트 앱을 사용 설정하세요.
<!-- This is the component name for the OEM customization service. OEM can choose to implement
this service to customize car service behavior for different policies. If OEMs choose to
implement it, they have to implement a service extending OemCarService exposed by car-lib,
and implement the required component services.
If the component name is invalid, CarService would not connect to any OEM service.
Component name can not be a third party package. It should be pre-installed -->
<string name="config_oemCarService" translatable="false">
com.android.car.oemcarservice.testapp/.OemCarServiceImpl
</string>
OEM 자동차 서비스가 OEM 서비스의 자동차 서비스 dump
명령어를 사용하는지 확인하려면 다음을 실행하세요.
adb shell dumpsys car_service --oem-service
결과는 아래 출력과 같을 수 있습니다.
***CarOemProxyService dump***
mIsFeatureEnabled: true
mIsOemServiceBound: true
mIsOemServiceReady: true
mIsOemServiceConnected: true
mInitComplete: true
OEM_CAR_SERVICE_CONNECTED_TIMEOUT_MS: 5000
OEM_CAR_SERVICE_READY_TIMEOUT_MS: 5000
mComponentName: com.android.car.oemcarservice.testapp/.OemCarServiceImpl
각 배치의 dump
정보에 있는 각 불리언 값이 기능과 서비스의 상태를 결정합니다. 예를 들어 덤프 정보 mIsOemServiceReady
는 서비스를 사용할 준비가 되었는지 지정하며 여기서 true
는 준비되었음을 나타내고 false
는 준비되지 않았음을 나타냅니다.