音訊控制 HAL

Android 9 導入了音訊控制 HAL,可支援與車輛相關的音訊使用情境。自 Android 14 起,音訊控制 HAL 支援:

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

圖 1 概略顯示車輛音訊服務架構,其中車輛音訊服務會與音訊控制 HAL 通訊。

設定多區域音訊

圖 1. 設定多區域音訊。

音訊淡入/淡出和平衡

Android 9 導入了 HIDL 音訊控制 HAL 第 1 版,支援車輛使用情境中的音訊淡出和平衡。這個機制與 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);
}

所有版本的音訊控制 HAL 均可使用這項 API,包括新的 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 就能接收車輛音訊服務攔截的靜音事件。

如要啟用這項功能,原始設備製造商必須在車輛服務 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 並提供新功能,Android 13 已將 AIDL 音訊控制 HAL 更新至 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 版的所有其他功能都保留下來,且可供使用。音訊焦點變更方法是例外狀況,詳情請參閱「音訊焦點變更方法」。

音訊控制焦點,並顯示播放曲目的中繼資料

如要向 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);
}

音訊焦點變更方法

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

如果 HAL 開發人員決定不支援 IAudioControl#onAudioFocusChangeWithMetaData,方法應傳回含有 UNKNOWN_TRANSACTION 錯誤的結果,如「使用版本化介面方法」一文所述。

音訊服務會先呼叫 onAudioFocusChangeWithMetaData,如果結果為 UNKNOWN_TRANSACTION 失敗,則會使用 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 參數仍會包含必要資訊。

音訊增益回呼

為了讓 Android 13 中的 AAOS 更清楚瞭解 HAL 下方的音訊變化,我們新增了一種機制,可讓您將車輛音訊系統的音訊增益變化傳達給車輛音訊服務。這項機制會公開音訊增益音量索引變更,以及增益變更的原因:

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

這些變更會將 HAL 以下的限制公開給車輛音訊服務,最後再公開給系統 UI 應用程式,以通知使用者。在 Android 14 中,我們進一步擴充了後者 (可能接觸到系統 UI),讓系統 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 註冊。從音訊控制 HAL 呼叫 API 時,車輛音訊服務會以相應的動作 (例如封鎖、限制或衰減增益指數) 回應。

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,實作項目應清除現有回呼,或在回呼不存在時不執行任何動作。建議音訊控制項實作項目為回呼註冊終止觀察器,並在觸發繫結器終止時清除回呼。

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。系統會套用相應的變更,並更新車輛音訊服務。