音訊控制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);
}

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

來自 HAL 的音訊焦點請求

AAOS 與 Android 類似,依靠音訊焦點應用程式的積極參與來管理汽車中的音訊播放。焦點資訊用於管理要控制哪些串流的音量和閃避。因此,為了進一步擴展音訊焦點並將汽車特定聲音更好地整合到 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 的音訊焦點請求不同,在 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 {:.external} 的按鍵事件。

  • 使用汽車音訊管理器靜音 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 的所有其他功能均保留並可使用。音訊焦點變更方法有一個例外,如關於音訊焦點變更方法中所述。

帶有播放軌道元資料的音訊控制焦點

為了向 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 應用程式以通知使用者。後一部分,即暴露可能的系統 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。當從音訊控制 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。應用相應的更改並相應更新汽車音訊服務。