音訊控制 HAL

音訊控制 HAL 已在 Android 9 中推出,可支援與汽車相關的音訊用途。自 Android 14 起,Audio control HAL 支援以下功能:

  • 淡出和平衡
  • HAL 音訊焦點要求
  • 裝置靜音和降低音量
  • 音訊裝置增益變更
  • 音訊埠設定變更

圖 1 概略說明車用音訊服務架構,其中車用音訊服務會與音訊控制 HAL 通訊。

設定多區域音訊

圖 1. 設定多區域音訊。

音訊淡出和平衡

HIDL 音訊控制 HAL 1.0 版在 Android 9 中推出,可支援汽車用途的音訊淡出和平衡功能。除了 Android 已提供的一般音效外,這個機制還可讓系統應用程式透過 CarAudioManager API 設定音訊平衡和淡出效果:

class CarAudioManager {
       /**
       *   Adjust the relative volume in the front vs back of the vehicle cabin.
       *
       *   @param value in the range -1.0 to 1.0 for fully toward the back through
       *   fully toward the front. 0.0 means evenly balanced.
       */
       @SystemApi
       @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
       public void setFadeTowardFront(float value);

       /**
       *   Adjust the relative volume on the left vs right side of the vehicle cabin.
       *
       *   @param value in the range -1.0 to 1.0 for fully toward the left through
       *   fully toward the right. 0.0 means evenly balanced.
       */
       @SystemApi
       @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
       public void setBalanceTowardRight(float value);
}

呼叫這些 API 後,系統會從車輛音訊服務呼叫相應的音訊控制 HAL API:

interface IAudioControl {
       /**
       *   Control the right/left balance setting of the car speakers.
       */
       oneway setBalanceTowardRight(float value);

       /**
       *   Control the fore/aft fade setting of the car speakers.
       */
       oneway setFadeTowardFront(float value);
}

這個 API 適用於所有版本的音訊控制 HAL,包括新的 AIDL HAL 介面。

來自 HAL 的音訊焦點要求

與 Android 類似,AAOS 會依賴應用程式主動參與音訊焦點,以便管理車輛中的音訊播放作業。焦點資訊可用於管理要控制音量和靜音的串流。因此,為了進一步擴大音訊焦點,並在 Android 體驗中更妥善地整合車輛專屬音效,我們在 Android 11 中推出了下列音訊屬性:

  • EMERGENCY
  • SAFETY
  • VEHICLE_STATUS
  • ANNOUNCEMENT

除了這項變更之外,我們也新增了一個機制,讓來自 Android 以外的聲音參與音訊焦點請求。因此,我們推出了 HIDL 音訊控制 HAL 2 版,以便允許來自 Android 以外的焦點要求:

interface IAudioControl {
       /**
       *   Registers focus listener to be used by HAL for requesting and
       *   abandoning audio focus.
       *   @param listener the listener interface
       *   @return closeHandle A handle to unregister observer.
       */
       registerFocusListener(IFocusListener listener)
       generates (ICloseHandle closeHandle);

       /**
       *   Notifies HAL of changes in audio focus status for focuses requested
       *   or abandoned by the HAL.
       *
       *   @param usage The audio usage associated with the focus change
       *   @param zoneId The identifier for the audio zone that the HAL is
       *   playing the stream in
       *   @param focusChange the AudioFocusChange that has occurred
       */
       oneway onAudioFocusChange(bitfield<AudioUsage> usage, int32_t zoneId,
       bitfield<AudioFocusChange> focusChange);
}

其中 IFocusListener 的定義為:

interface IFocusListener {
       /**
       *   Called whenever HAL is requesting focus as it is starting to play
       *   audio of a given usage in a specified zone.
       *
       *   @param usage The audio usage associated with the focus request
       *    {@code AttributeUsage}
       *   @param zoneId The identifier for the audio zone where the HAL is
       *    requesting focus
       *   @param focusGain The AudioFocusChange associated with this request.
       */
       oneway requestAudioFocus(bitfield<AudioUsage> usage,
       int32_t zoneId, bitfield<AudioFocusChange> focusGain);
       /**
       *   Called whenever HAL is abandoning focus as it is finished playing audio
       *   of a given usage in a specific zone.
       *
       *   @param usage The audio usage for which the HAL is abandoning focus
       *    {@code AttributeUsage}
       *   @param zoneId The identifier for the audio zone that the HAL
       *    abandoning focus
       */
       oneway abandonAudioFocus(bitfield<AudioUsage> usage, int32_t zoneId);
}

上述 API 可分別用於要求和放棄 HAL 的音訊焦點。在回應中,車輛音訊服務會考量音訊焦點要求,並將結果以非同步方式轉送至 IAudioControl#onAudioFocusChange 方法。

這個 API 也可用於監控來自音訊控制 HAL 的音訊焦點要求變更。一般來說,HAL 的任何持續音訊焦點要求都會視為「有效」,這與 Android 的音訊焦點要求不同,後者只會將對應的有效音訊軌播放視為有效。

將 HIDL 遷移至 AIDL 音訊控制 HAL

隨著 AIDL 的推出,以及 Android 12 中所需的遷移作業 (詳情請參閱「HAL 專用 AIDL」),音訊控制 HAL 已遷移至 AIDL。針對現有的 HIDL 音訊控制第 2 版 API,遷移作業需要對現有方法進行小幅更新:

interface IAudioControl {
       /**
       *   Notifies HAL of changes in audio focus status for focuses requested
       *   or abandoned by the HAL.
       *
       *   @param usage The audio usage associated with the focus change
       *        {@code AttributeUsage}. See {@code audioUsage} in
       *        audio_policy_configuration.xsd for the list of allowed values.
       *   @param zoneId The identifier for the audio zone that the HAL is
       *        playing the stream in
       *   @param focusChange the AudioFocusChange that has occurred.
       */
       oneway void onAudioFocusChange(in String usage, in int zoneId,
              in AudioFocusChange focusChange);
       /**
       *   Registers focus listener to be used by HAL for requesting and
       *   abandoning audio focus.
       *   @param listener the listener interface.
       */
       oneway void registerFocusListener(in IFocusListener listener);
       /**
       *   Control the right/left balance setting of the car speakers.
       */
       oneway void setBalanceTowardRight(in float value);
       /**
       *   Control the fore/aft fade setting of the car speakers.
       */
       oneway void setFadeTowardFront(in float value);
}

以及對應的 IFocusListener

       interface IFocusListener {
       /**
       *   Called whenever HAL is abandoning focus as it is finished playing audio
       *   of a given usage in a specific zone.
       *
       *   @param usage The audio usage for which the HAL is abandoning focus
       *        {@code AttributeUsage}. See {@code audioUsage} in
       *        audio_policy_configuration.xsd for the list of allowed values.
       *   @param zoneId The identifier for the audio zone that the HAL
       *        abandoning focus
       */
       oneway void abandonAudioFocus(in String usage, in int zoneId);
       /**
       *   Called whenever HAL is requesting focus as it is starting to play audio
       *        of a given usage in a specified zone.
       *
       *   @param usage The audio usage associated with the focus request
       *        {@code AttributeUsage}. See {@code audioUsage} in
       *        audio_policy_configuration.xsd for the list of allowed values.
       *   @param zoneId The identifier for the audio zone where the HAL is
       *        requesting focus
       *   @param focusGain The AudioFocusChange associated with this request.
       */
       oneway void requestAudioFocus(in String usage, in int zoneId,
              in AudioFocusChange focusGain);
}

音量群組靜音

Android 12 推出了音量群組靜音功能,可在使用者進行音訊互動時提供更全面的靜音控制功能。這可讓音訊控制 HAL 接收汽車音訊服務攔截的靜音事件。

如要啟用這項功能,原始設備製造商 (OEM) 必須在車輛服務 config.xml 中將 audioUseCarVolumeGroupMuting 設定設為 true

<!-- Configuration to enable muting of individual volume groups.
If this is set to false, muting of individual volume groups is disabled,
instead muting will toggle master mute. If this is set to true, car volume
group muting is enabled and each individual volume group can be muted separately. -->
<bool name="audioUseCarVolumeGroupMuting">true</bool>

在 Android 13 之前,您必須使用 packages/services/Car/service/res/values/config.xml 的執行階段資源重疊層覆寫設定 (如需瞭解詳情,請參閱「使用資源重疊層自訂建構」)。從 Android 13 開始,您可以使用執行階段資源覆蓋來變更設定值。詳情請參閱「在執行階段變更應用程式資源的值」。

系統應用程式可以使用 CarAudioManager#isAudioFeatureEnabled API 判斷是否已啟用這項功能。傳入的參數必須是 CarAudioManager.AUDIO_FEATURE_VOLUME_GROUP_MUTING 常數。如果裝置已啟用這項功能,此方法會傳回 true,否則會傳回 false

除了啟用 audioUseCarVolumeGroupMuting 功能,AIDL 音訊控制 HAL 必須實作音量群組靜音機制:

interface IAudioControl {
       /**
       *   Notifies HAL of changes in output devices that the HAL should apply
       *   muting to.
       *
       *   This will be called in response to changes in audio mute state for each
       *   volume group and will include a {@link MutingInfo} object per audio
       *   zone that experienced a mute state event.
       *
       *   @param mutingInfos an array of {@link MutingInfo} objects for the audio
       *   zones where audio mute state has changed.
       */
       oneway void onDevicesToMuteChange(in MutingInfo[] mutingInfos);
}

其中,靜音資訊包含音訊系統的相關靜音資訊:

parcelable MutingInfo {
       /**
       *   ID of the associated audio zone
       */
       int zoneId;
       /**
       *   List of addresses for audio output devices that should be muted.
       */
       String[] deviceAddressesToMute;
       /**
       *   List of addresses for audio output devices that were previously be
       *   muted and should now be unmuted.
       */
       String[] deviceAddressesToUnmute;
}

AAOS 有兩種不同的靜音機制,分別是:

  • 使用音訊 KEYCODE_VOLUME_MUTE 的重要事件。

  • 使用汽車音訊管理員靜音 API (CarAudioManager#setVolumeGroupMute) 直接呼叫汽車音訊服務。

啟用後,這兩種機制都會觸發音訊控制 HAL 的通話靜音功能。

降低其他應用程式音量

Android 12 推出了車輛音訊調低功能,可針對音訊串流的同時播放功能進行最佳化控制。這樣一來,原始設備製造商 (OEM) 就能根據車輛音響服務決定的車輛實體音訊設定和目前的播放狀態,實作自己的迴避行為。

降低其他應用程式音量機制會根據音訊焦點堆疊的變更情形進行調整。只要發生焦點變更 (無論是焦點要求或焦點放棄),系統就會通知音訊控制 HAL。與汽車音量群組靜音支援功能類似,您可以使用 audioUseHalDuckingSignals 設定旗標啟用汽車音訊調低功能:

<!-- Configuration to enable IAudioControl#onDevicesToDuckChange API to
inform HAL when to duck. If this is set to true, the API will receive signals
indicating which output devices to duck as well as what usages are currently
holding focus. If set to false, the API will not be called. -->
<bool name="audioUseHalDuckingSignals">true</bool>

如要啟用這項功能,AIDL 音訊控制 HAL 必須使用從車輛音訊服務接收的信號,實作相關邏輯:

interface IAudioControl {
       /**
       *   Notifies HAL of changes in output devices that the HAL should apply
       *   ducking to.
       *
       *   This will be called in response to changes in audio focus, and will
       *   include a {@link DuckingInfo} object per audio zone that experienced
       *   a change in audo focus.
       *
       *   @param duckingInfos an array of {@link DuckingInfo} objects for the
       *   audio zones where audio focus has changed.
       */
       oneway void onDevicesToDuckChange(in DuckingInfo[] duckingInfos);
}

相關的音訊系統資訊包含在音訊靜音資訊中:

parcelable DuckingInfo {
       /**
       *   ID of the associated audio zone
       */
       int zoneId;
       /**
       *   List of addresses for audio output devices that should be ducked.
       */
       String[] deviceAddressesToDuck;
       /**
       *   List of addresses for audio output devices that were previously be
       *   ducked and should now be unducked.
       */
       String[] deviceAddressesToUnduck;
       /**
       *   List of usages currently holding focus for this audio zone.
       */
       String[] usagesHoldingFocus;
}

除了裝置地址中包含的車輛音訊設定資訊,用於 (取消) 靜音的資訊也包含哪些音訊屬性用途會保留焦點。這項資料的用意是通知音訊系統哪些音訊屬性使用方式處於活動狀態。

這是必要的,因為在車輛音訊設定中,單一裝置可指派多個音訊屬性,如果沒有額外資訊,就無法明確指出哪些用途處於活動狀態。

AIDL 音訊控制 HAL 2.0

為更新 API 並促進新功能的開發,AIDL 音訊控制 HAL 已在 Android 13 更新至 2.0 版:

  • 使用 PlaybackTrackMetadata 取得音訊焦點
  • 音訊增益回呼

播放中繼資料在 android.hardware.audio.common 中的定義如下:

parcelable PlaybackTrackMetadata {
       AudioUsage usage = INVALID;
       AudioContentType contentType = UNKNOWN;
       float gain;
       AudioChannelLayout channelMask;
       AudioDevice sourceDevice;
       String[] tags;
}

AIDL 音訊控制 1.0 版的所有其他功能都會保留,且可供使用。例外狀況是指音訊焦點變更方法,請參閱「OnAudioFocusChange 方法」一節。

使用播放音軌中繼資料設定音訊控制焦點

為了向 HAL 以下的音訊系統提供更多資訊,更新內容現在會公開 PlaybackTrackMetadata。具體來說,音訊控制 HAL 已透過新方法擴充:

interface IAudioControl {
       /**
       *   Notifies HAL of changes in audio focus status for focuses requested
       *   or abandoned by the HAL.
       *
       *   The HAL is not required to wait for a callback of AUDIOFOCUS_GAIN
       *   before playing audio, nor is it required to stop playing audio in the
       *   event of a AUDIOFOCUS_LOSS callback is received.
       *
       *   @param playbackMetaData The output stream metadata associated with
       *    the focus request
       *   @param zoneId The identifier for the audio zone that the HAL is
       *    playing the stream in
       *   @param focusChange the AudioFocusChange that has occurred.
       */
       oneway void onAudioFocusChangeWithMetaData(
       in PlaybackTrackMetadata playbackMetaData, in int zoneId,
       in AudioFocusChange focusChange);
}

IFocusListener 進行類似的對應變更:

       /**
       *   Called to indicate that the audio output stream associated with
       *   {@link android.hardware.audio.common.PlaybackTrackMetadata} is
       *   abandoning focus as playback has stopped.
       *
       *   @param playbackMetaData The output stream metadata associated with
       *    the focus request
       *   @param zoneId The identifier for the audio zone that the HAL
       *    abandoning focus
       */
       oneway void abandonAudioFocusWithMetaData(
       in PlaybackTrackMetadata playbackMetaData, in int zoneId);
       /**
       *   Called to indicate that the audio output stream associated with
       *   {@link android.hardware.audio.common.PlaybackTrackMetadata} has taken
       *   the focus as playback is starting for the corresponding stream.
       *
       *   @param playbackMetaData The output stream metadata associated with
       *    the focus request
       *   @param zoneId The identifier for the audio zone that the HAL
       *    abandoning focus
       *   @param focusGain The focus type requested.
       */
       oneway void requestAudioFocusWithMetaData(
       in PlaybackTrackMetadata playbackMetaData, in int zoneId,
       in AudioFocusChange focusGain);
}

OnAudioFocusChange 方法

上述焦點作業的執行方式與「來自 HAL 的音訊焦點要求」一文所述的焦點作業相同。只有播放曲目中繼資料會提供更多資訊,以及音訊屬性用途。一般來說,除非需要播放軌中繼資料提供的額外資訊,否則更新後的 Android 控制 HAL 可繼續使用先前的做法。

如果 HAL 開發人員決定不支援 IAudioControl#onAudioFocusChangeWithMetaData,則方法應傳回結果,並附上 UNKNOWN_TRANSACTION 錯誤,如「使用版本控制介面方法」所述。

如果 UNKNOWN_TRANSACTION 失敗,音訊服務會先呼叫 onAudioFocusChangeWithMetaData,然後使用 onAudioFocusChange 方法重試。

使用播放曲目中繼資料的車用音訊靜音功能

AIDL 音訊控制 HAL 2.0 版將播放曲目中繼資料新增至音訊靜音資訊:

parcelable DuckingInfo {
       /**
       *   ID of the associated audio zone
       */
       int zoneId;
       /**
       *   List of addresses for audio output devices that should be ducked.
       */
       String[] deviceAddressesToDuck;
       /**
       *   List of addresses for audio output devices that were previously be
       *   ducked and should now be unducked.
       */
       String[] deviceAddressesToUnduck;
       /**
       *   List of usages currently holding focus for this audio zone.
       */
       String[] usagesHoldingFocus;
       /**
       *   List of output stream metadata associated with the current focus
       *   holder for this audio zone
       */
       @nullable PlaybackTrackMetadata[] playbackMetaDataHoldingFocus;
}

usagesHoldingFocus 已淘汰,開發人員現在應使用 playbackMetaDataHoldingFocus 來判斷音訊屬性用法和其他音訊資訊。不過,在正式移除這個選項之前,usagesHoldingFocus 參數仍會包含必要資訊。

音訊增益回呼

為讓 HAL 以下的音訊變更更容易在 Android 13 中的 AAOS 中顯示,我們新增了一種機制,可用於將音訊增益變更從車輛音訊系統傳達至車輛音訊服務。此機制會揭露音訊增益音量索引變更,並說明變更增益的原因:

  • 已封鎖或靜音的限制
  • 限制
  • 衰減限制

這些變更會將這些限制從 HAL 以下層級公開至車輛音訊服務,最後再公開至系統 UI 應用程式,以便通知使用者。後半段的部分,也就是向可能的系統 UI 公開資訊,在 Android 14 中進一步擴充,讓系統 UI 應用程式更容易透過音量群組資訊回呼機制取得這項資訊。

音訊控制 HAL API 會註冊增益回呼,如下所示:

interface IAudioControl {
       /**
       *   Registers callback to be used by HAL for reporting unexpected gain(s)
       *    changed and the reason(s) why.
       *
       *   @param callback The {@link IAudioGainCallback}.
       */
       oneway void registerGainCallback(in IAudioGainCallback callback);
}

IAudioGainCallback 的定義如下:

interface IAudioGainCallback {
       /**
       *   Used to indicate that one or more audio device port gains have changed,
       *   i.e. initiated by HAL, not by CarAudioService.
       *   This is the counter part of the
       *   {@link onDevicesToDuckChange}, {@link onDevicesToMuteChange} and,
       *   {@link setAudioDeviceGainsChanged} APIs.
       *
       *   @param reasons List of reasons that triggered the given gains changed.
       *   @param gains List of gains affected by the change.
       */
       void onAudioDeviceGainsChanged(in Reasons[] reasons,
       in AudioGainConfigInfo[] gains);
}

如 API 說明文件所述,車用音訊服務會將增益回呼註冊至音訊控制 HAL。當 API 從音訊控制 HAL 呼叫時,車輛音訊服務會以相應的動作 (例如封鎖、限制或減弱增益索引) 回應。

HAL 會決定呼叫 API 的時機,主要目的是回報增益索引狀態的變更。根據法規要求,車輛的音響系統應採取必要行動,並使用回呼向車輛音響服務回報資訊,以便使用者使用。例如向使用者顯示 UI。

AIDL 音訊控制 HAL 3.0

Android 14 AIDL 音訊控制 HAL 版本已更新至 3.0 版,以便更新 API,提供更強大的音訊增益索引功能。音訊控制 HAL API 可讓音訊服務設定及取消設定 IModuleChangeCallback

interface IAudioControl {
       /**
       *   Sets callback with HAL for notifying changes to hardware module
       *   (that is: {@link android.hardware.audio.core.IModule}) configurations.
       *
       *   @param callback The {@link IModuleChangeCallback} interface to use
       *    use when new updates are available for
       */
       void setModuleChangeCallback(in IModuleChangeCallback callback);
       /**
       *   Clears module change callback
       */
       void clearModuleChangeCallback();
}

在服務啟動或從錯誤復原時,車輛音響服務會註冊 setModuleChangeCallback。例如,車輛音訊服務收到的音訊控制 HAL 繫結器死亡通知。在呼叫 API 時,音訊控制 HAL 實作應取代任何現有的模組變更回呼。

針對 clearModuleChangeCallback API,實作方式應清除現有的回呼,如果不存在,則不採取任何動作。建議您在實作音訊控制時,為回呼註冊死亡觀察器,然後在觸發 on binder death 時清除回呼。

IModuleChangeCallback 的定義如下:

oneway interface IModuleChangeCallback {
       /**
       *   Used to indicate that one or more {@link AudioPort} configs have
       *   changed. Implementations MUST return at least one AudioPort.
       *
       *   @param audioPorts list of {@link AudioPort} that are updated
       */
       void onAudioPortsChanged(in AudioPort[] audioPorts);
}

當車輛音訊服務註冊模組變更回呼時,便可透過 onAudioPortChanged API 接收音訊埠變更。這個 API 可用於在註冊回呼後,立即初始化音訊系統的音量增益。如要變更其他動態增益,您可以隨時呼叫 API。系統會套用相應的變更,並相應更新車輛音訊服務。