Android Automotive OS (AAOS) 以 Android 核心音訊堆疊為基礎,可支援在車輛中以資訊娛樂系統運作的用途。AAOS 負責提供資訊娛樂音效 (即媒體、導航和通訊),但不直接負責提供有嚴格可用性和時間限制的鈴聲和警示。雖然 AAOS 提供信號和機制,協助車輛管理音訊,但最終還是要由車輛決定應向駕駛員和乘客播放哪些音訊,確保安全性重要音訊和法規音訊不會中斷,讓駕駛員和乘客都能正確聽到這些音訊。
由於 Android 會管理車輛的媒體體驗,因此應用程式應代表外部媒體來源 (例如收音機),以便處理來源的音訊焦點和媒體鍵事件。
Android 11 針對汽車相關的音訊支援功能做出下列變更:
Android 音效和串流
汽車音響系統會處理下列音訊和串流:
圖 1. 以串流為中心的架構圖
Android 會管理來自 Android 應用程式的音訊,控制這些應用程式,並根據音訊類型將音訊路由至 HAL 的輸出裝置:
- 邏輯串流 (在核心音訊命名法中稱為來源) 會加上音訊屬性標記。
- 實體串流 (在核心音訊命名法中稱為裝置) 在混合後沒有任何背景資訊。
為確保可靠性,外部聲響 (來自獨立來源,例如安全帶警示鈴聲) 會在 Android 外部管理,位於 HAL 之下,甚至在獨立硬體中。系統導入者必須提供混合器,接受來自 Android 的一或多個音訊輸入串流,然後以適當方式將這些串流與車輛所需的外部音訊來源結合。
HAL 實作和外部混音器負責確保可聽到安全性至關重要的外部聲音,並混合 Android 提供的串流,並將其路由至適當的喇叭。
Android 音效
應用程式可能會有一或多個透過標準 Android API 互動的播放器 (例如用於焦點控制的 AudioManager,或用於串流的 MediaPlayer),以便發出一個或多個邏輯音訊資料串流。這類資料可能是單聲道或 7.1 環場音效,但會以單一來源進行路由和處理。應用程式串流會與 AudioAttributes 建立關聯,為系統提供音訊應如何呈現的提示。
邏輯串流會透過 AudioService 傳送,並路由至一個 (且僅一個) 可用的實體輸出串流,每個串流都是 AudioFlinger 中混合器的輸出內容。音訊屬性混合至實體串流後,就無法再使用。
然後將每個實體串流傳送至 Audio HAL,以便在硬體上轉譯。在車用應用程式中,算繪硬體可以是本機編碼器 (類似行動裝置),或是車輛實體網路中的遠端處理器。無論如何,Audio HAL 實作功能都會提供實際的取樣資料,並使其可供播放。
外部串流
不應透過 Android 路由的音訊串流 (出於認證或時間因素) 可能會直接傳送至外部混音器。自 Android 11 起,HAL 現可為這些外部聲音要求焦點,以便通知 Android 採取適當的動作,例如暫停媒體或防止其他人獲得焦點。
如果外部串流是應與 Android 產生的音訊環境互動的媒體來源 (例如在外部調諧器開啟時停止 MP3 播放),則應由 Android 應用程式代表媒體來源 (而非 HAL) 要求音訊焦點,並在需要時啟動/停止外部來源,以符合 Android 焦點政策,以便回應焦點通知。應用程式也負責處理媒體按鍵事件,例如播放/暫停。HwAudioSource 是控制這類外部裝置的建議機制。
輸出裝置
在 Audio HAL 層級,裝置類型 AUDIO_DEVICE_OUT_BUS
會提供一般輸出裝置,用於車輛音響系統。匯流排裝置支援可尋址的通訊埠 (每個通訊埠都是實體串流的端點),且預期會是車輛中唯一支援的輸出裝置類型。
系統實作可為所有 Android 音效使用一個匯流排埠,在這種情況下,Android 會將所有音效混合在一起,並以單一串流提供。或者,HAL 可以為每個 CarAudioContext
提供一個匯流排埠,以便同時傳送任何音訊類型。這樣一來,HAL 實作項目就能視需要混合及降低不同音訊的音量。
音訊背景資訊的輸出裝置指派作業是透過 car_audio_configuration.xml
完成。
麥克風輸入
擷取音訊時,Audio HAL 會收到 openInputStream
呼叫,其中包含 AudioSource
引數,指出應如何處理麥克風輸入內容。
VOICE_RECOGNITION
來源 (特別是 Google 助理) 預期會收到含有回音消除效果 (如有) 的立體聲麥克風串流,但不會套用其他處理。助理應會執行波束成形。
多聲道麥克風輸入
如要擷取具有超過兩個聲道 (立體聲) 的裝置音訊,請使用聲道索引遮罩,而非位置索引遮罩 (例如 CHANNEL_IN_LEFT
)。範例:
final AudioFormat audioFormat = new AudioFormat.Builder() .setEncoding(AudioFormat.ENCODING_PCM_16BIT) .setSampleRate(44100) .setChannelIndexMask(0xf /* 4 channels, 0..3 */) .build(); final AudioRecord audioRecord = new AudioRecord.Builder() .setAudioFormat(audioFormat) .build(); audioRecord.setPreferredDevice(someAudioDeviceInfo);
當 setChannelMask
和 setChannelIndexMask
都已設定時,AudioRecord
只會使用 setChannelMask
設定的值 (最多兩個管道)。
並行擷取
自 Android 10 起,Android 架構便支援同時擷取輸入內容,但會設有限制以保護使用者隱私。根據這些限制,系統會忽略 AUDIO_SOURCE_FM_TUNER
等虛擬來源,並允許同時擷取這些來源和一般輸入內容 (例如麥克風)。HwAudioSources
也不會視為同時擷取限制的一部分。
設計用於 AUDIO_DEVICE_IN_BUS
裝置或次要 AUDIO_DEVICE_IN_FM_TUNER
裝置的應用程式,必須明確識別這些裝置,並使用 AudioRecord.setPreferredDevice()
略過 Android 預設來源選取邏輯。
音訊用途
AAOS 主要使用
AudioAttributes.AttributeUsages
進行路由、音量調整和焦點管理。用途代表串流播放的「原因」。因此,所有串流和音訊焦點要求都應指定音訊播放的用途。如果在建構 AudioAttributes 物件時未特別設定,使用情形預設為 USAGE_UNKNOWN
。雖然目前會將此行為視為 USAGE_MEDIA
,但不應在媒體播放時依賴此行為。
系統用量
在 Android 11 中,我們推出了系統用途。這些用法與先前建立的用法類似,但需要使用系統 API 和 android.permission.MODIFY_AUDIO_ROUTING
。新的系統用途如下:
USAGE_EMERGENCY
USAGE_SAFETY
USAGE_VEHICLE_STATUS
USAGE_ANNOUNCEMENT
如要建構含有系統用量的 AudioAttributes
,請使用 AudioAttributes.Builder#setSystemUsage
而非 setUsage
。以非系統用途呼叫此方法,會導致擲回 IllegalArgumentException
。此外,如果在建構工具上同時設定系統用途和用途,則在建構時會擲回 IllegalArgumentException
。
如要查看與 AudioAttributes
例項相關聯的用量,請呼叫 AudioAttributes#getSystemUsage
。這會傳回相關聯的使用量或系統用量。
音訊內容
為簡化 AAOS 音訊的設定,我們將類似用途歸類為 CarAudioContext
。這些音訊內容會在整個 CarAudioService
中使用,用於定義路由、音量群組和音訊焦點管理。
Android 11 中的音訊背景資訊如下:
CarAudioContext | 相關的 AttributeUsages |
---|---|
MUSIC |
UNKNOWN, GAME, MEDIA |
NAVIGATION |
ASSISTANCE_NAVIGATION_GUIDANCE |
VOICE_COMMAND |
ASSISTANT, ASSISTANCE_ACCESSIBILITY |
CALL_RING |
NOTIFICATION_RINGTONE |
CALL |
VOICE_COMMUNICATION, VOICE_COMMUNICATION_SIGNALING |
ALARM |
ALARM |
NOTIFICATION |
NOTIFICATION, NOTIFICATION_* |
SYSTEM_SOUND |
ASSISTANCE_SONIFICATION |
EMERGENCY |
EMERGENCY |
SAFETY |
SAFETY |
VEHICLE_STATUS |
VEHICLE_STATUS |
ANNOUNCEMENT |
ANNOUNCEMENT |
音訊背景和用途之間的對應關係。醒目顯示的資料列是新的系統用途。
多區音訊
汽車應用程式帶來了一系列新的用途,包括使用者同時與平台互動,並想使用不同的媒體。舉例來說,駕駛人可以在車內播放音樂,後座乘客則可透過後座螢幕觀看 YouTube 影片。多區域音訊可讓不同音訊來源同時透過車輛的不同區域播放,實現這項功能。
從 Android 10 開始,多區域音訊功能可讓原始設備製造商將音訊設定為不同的區域。每個區域都是車輛內裝置的集合,各自有自己的音量群組、情境的路由設定和焦點管理。如此一來,主車廂可設定為一個音訊區,而後座螢幕的耳機插孔則可設定為第二個區。
這些區域是 car_audio_configuration.xml
的一部分。CarAudioService
接著會讀取設定,並協助 AudioService 根據相關聯的區域路由音訊串流。每個區域仍會定義規則,根據情境和應用程式用戶端 ID 進行轉送。建立播放器時,CarAudioService
會決定播放器與哪個區域相關聯,然後根據用途,決定 AudioFlinger 應將音訊路由至哪個裝置。
每個音訊區域也會獨立維持專注模式。這樣一來,不同區域中的應用程式就能獨立產生音訊,不會彼此干擾,同時應用程式仍會尊重其區域內的焦點變更。CarAudioService
中的 CarZonesAudioFocus
負責管理每個區域的焦點。
圖 2. 設定多區域音訊
音訊 HAL
車用音訊實作功能會依賴標準 Android Audio HAL,其中包含以下內容:
IDevice.hal
:建立輸入和輸出串流、處理主要音量和靜音功能,並使用:createAudioPatch
:在裝置之間建立外部-外部修補程式。IDevice.setAudioPortConfig()
提供每個實體串流的音量。
IStream.hal
:除了輸入和輸出變數外,還可管理從硬體傳送至硬體的音訊樣本串流。
汽車裝置類型
下列裝置類型與汽車平台相關。
裝置類型 | 說明 |
---|---|
AUDIO_DEVICE_OUT_BUS |
Android 的主要輸出內容 (這是 Android 將所有音訊傳送至車輛的方式)。用於為每個情境區分串流的地址。 |
AUDIO_DEVICE_OUT_TELEPHONY_TX |
用於將音訊傳送至行動電台進行傳輸。 |
AUDIO_DEVICE_IN_BUS |
用於無法歸類的輸入內容。 |
AUDIO_DEVICE_IN_FM_TUNER |
僅用於廣播電台輸入內容。 |
AUDIO_DEVICE_IN_TV_TUNER |
用於電視裝置 (如有)。 |
AUDIO_DEVICE_IN_LINE |
用於 AUX 輸入插孔。 |
AUDIO_DEVICE_IN_BLUETOOTH_A2DP |
透過藍牙接收的音樂。 |
AUDIO_DEVICE_IN_TELEPHONY_RX |
用於接收與電話通話相關聯的行動無線電音訊。 |
設定音訊裝置
Android 可見的音訊裝置必須在 /audio_policy_configuration.xml
中定義,其中包含下列元件:
- 模組名稱。支援「primary」(用於汽車用途)、「A2DP」、「remote_submix」和「USB」。模組名稱和對應的音訊驅動程式應編譯為
audio.primary.$(variant).so
。 - devicePorts。包含可透過此模組存取的所有輸入和輸出裝置 (包括永久連接的裝置和可移除的裝置) 的裝置描述元資料清單。
- 針對每個輸出裝置,您可以定義增益控制,其中包含以毫貝為單位的 min/max/default/step 值 (1 毫貝 = 1/100 dB = 1/1000 貝)。
- 即使有多部裝置的裝置類型與
AUDIO_DEVICE_OUT_BUS
相同,您仍可使用 devicePort 例項的 address 屬性來尋找裝置。 - mixPorts。包含音訊 HAL 公開的所有輸出和輸入串流清單。每個 mixPort 例項可視為 Android AudioService 的實體串流。
- 路線。定義輸入和輸出裝置之間,或串流和裝置之間的可能連線清單。
以下範例定義了輸出裝置 bus0_phone_out,其中所有 Android 音訊串流都由 mixer_bus0_phone_out 混合。路由會將 mixer_bus0_phone_out
的輸出串流傳送至裝置 bus0_phone_out
。
<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude"> <modules> <module name="primary" halVersion="3.0"> <attachedDevices> <item>bus0_phone_out</item> <defaultOutputDevice>bus0_phone_out</defaultOutputDevice> <mixPorts> <mixPort name="mixport_bus0_phone_out" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/> </mixPort> </mixPorts> <devicePorts> <devicePort tagName="bus0_phone_out" role="sink" type="AUDIO_DEVICE_OUT_BUS" address="BUS00_PHONE"> <profile name="" 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> </devicePorts> <routes> <route type="mix" sink="bus0_phone_out" sources="mixport_bus0_phone_out"/> </routes> </module> </modules> </audioPolicyConfiguration>