Android 提供完整的藍牙實作,支援許多常見的車內藍牙設定檔。此外,我們也進行了許多強化作業,提升其他裝置和服務的效能與體驗。
管理藍牙連線
在 Android 中, CarBluetoothService 會維護目前使用者的藍牙裝置,以及每個設定檔連線至 IVI 的優先順序清單。裝置會依據定義的優先順序連線至設定檔。啟用、停用裝置及將裝置連線至設定檔的時機,取決於預設連線政策,如果需要,可以使用 資源疊加覆寫政策。
設定車輛連線管理
停用預設電話政策
Android 藍牙堆疊會維護手機的連線政策,這項政策預設為啟用。您必須在裝置上停用這項政策,以免與
CarBluetoothService 中的預期車輛政策發生衝突。雖然車輛產品疊加層應會為您處理這項問題,但您可以在
資源疊加層中,將
/packages/apps/Bluetooth/res/values/config.xml
的 MAXIMUM_CONNECTED_DEVICES
內 enable_phone_policy
設為 false
,停用手機政策。
使用預設的車輛政策
CarBluetoothService 會維護預設設定檔權限。已知裝置清單及其設定檔重新連線優先順序位於 service/src/com/android/car/BluetoothProfileDeviceManager.java
。
此外,您可以在
service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
中找到藍牙連線管理政策。根據預設,這項政策會定義藍牙何時應連線至已配對的裝置,以及何時應與這些裝置中斷連線。此外,它也會管理車輛專屬的案例,判斷何時應開啟及關閉轉接器。
建立自訂車用連線管理政策
如果預設車輛政策不符合需求,您也可以停用這項政策,改用自訂政策。您的自訂政策至少要負責判斷何時啟用及停用藍牙介面卡,以及何時連線裝置。您可以使用各種事件啟用/停用藍牙介面卡,以及啟動裝置連線,包括因特定車輛屬性變更而發生的事件。
停用預設車輛政策
首先,如要使用自訂政策,必須在 資源疊加中將 useDefaultBluetoothConnectionPolicy
設為 false
,停用預設車輛政策。這項資源原本定義為 MAXIMUM_CONNECTED_DEVICES
的一部分,位於
packages/services/Car/service/res/values/config.xml
中。
啟用及停用藍牙介面卡
政策的核心功能之一,是在適當時間開啟及關閉藍牙介面卡。您可以使用 BluetoothAdapter.enable()
和 BluetoothAdapter.disable()
架構 API 啟用及停用轉接程式。這些呼叫應尊重使用者透過「設定」或其他方式選取的持續性狀態。其中一種方法如下:
/** * Turn on the Bluetooth adapter. */ private void enableBluetooth() { BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter == null) { return; } bluetoothAdapter.enable(); } /** * Turn off the Bluetooth adapter. */ private void disableBluetooth() { BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter == null) { return; } // Will shut down _without_ persisting the off state as the desired state // of the Bluetooth adapter for next start up. This does nothing if the adapter // is already off, keeping the existing saved desired state for next reboot. bluetoothAdapter.disable(false); }
決定何時開啟及關閉藍牙介面卡
您可以自由決定哪些事件表示啟用和停用轉接程式的最佳時機。其中一種做法是使用 MAXIMUM_CONNECTED_DEVICES
中的電源狀態:CarPowerManager
private final CarPowerStateListenerWithCompletion mCarPowerStateListener = new CarPowerStateListenerWithCompletion() { @Override public void onStateChanged(int state, CompletableFuture<Void> future) { if (state == CarPowerManager.CarPowerStateListener.ON) { if (isBluetoothPersistedOn()) { enableBluetooth(); } return; } // "Shutdown Prepare" is when the user perceives the car as off // This is a good time to turn off Bluetooth if (state == CarPowerManager.CarPowerStateListener.SHUTDOWN_PREPARE) { disableBluetooth(); // Let CarPowerManagerService know we're ready to shut down if (future != null) { future.complete(null); } return; } } };
判斷裝置連線時機
同樣地,當您決定應觸發裝置連線的事件時,
CarBluetoothManager 會提供 connectDevices()
API 呼叫,根據為每個藍牙設定檔定義的優先順序清單,繼續連線裝置。
舉例來說,您可能會想在藍牙轉接器開啟時執行這項操作:
private class BluetoothBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); if (state == BluetoothAdapter.STATE_ON) { // mContext should be your app's context Car car = Car.createCar(mContext); CarBluetoothManager carBluetoothManager = (CarBluetoothManager) car.getCarManager(Car.BLUETOOTH_SERVICE); carBluetoothManager.connectDevices(); } } } }
驗證車輛連線管理
如要驗證連線政策的行為,最簡單的方法是在 IVI 上啟用藍牙,並確認系統會自動依適當順序連線至正確裝置。你可以透過設定使用者介面或下列 adb 指令,切換藍牙轉接器:
adb shell su u$(adb shell am get-current-user)_system svc bluetooth disable
adb shell su u$(adb shell am get-current-user)_system svc bluetooth enable
此外,下列指令的輸出內容可用於查看與藍牙連線相關的偵錯資訊:
adb shell dumpsys car_service
最後,如果您已建立自己的車輛政策,如要驗證任何自訂連線行為,必須控管您選擇觸發裝置連線的事件。
車用藍牙設定檔
在 Android 系統中,IVI 可支援透過藍牙同時連線的多部裝置。透過多裝置藍牙電話服務,使用者可以同時連線多部裝置 (例如個人手機和公司手機),並透過任一裝置撥打免持電話。
連線限制是由各個藍牙設定檔強制執行,通常是在設定檔服務本身的實作範圍內。根據預設, CarBluetoothService 不會進一步判斷允許連線的裝置數量上限。
免持設定檔
藍牙免持聽筒設定檔 (HFP) 可讓車輛透過連線的遠端裝置撥打及接聽電話。每個裝置連線都會向 TelecomManager 註冊個別電話帳戶,並向 IVI 應用程式宣傳任何可用的電話帳戶。
IVI 可透過 HFP 連接多部裝置。MAX_STATE_MACHINES_POSSIBLE
MAXIMUM_CONNECTED_DEVICES
定義同時 HFP 連線數量上限。
HeadsetClientService
當使用者透過裝置撥打或接聽電話時,對應的電話帳戶會建立 HfpClientConnection
物件。撥號器應用程式會與 HfpClientConnection
物件互動,管理通話功能,例如接聽電話或掛斷電話。
請注意,預設的撥號程式應用程式不支援同時連線多個 HFP 裝置。如要實作多裝置 HFP,必須進行自訂,讓使用者在撥打電話時選取要使用的裝置帳戶。然後,應用程式會使用正確的帳戶呼叫 telecomManager.placeCall
。請確認其他多裝置功能也能正常運作。
驗證多裝置 HFP
如要確認藍牙多裝置連線功能是否正常運作,請按照下列步驟操作:
- 透過藍牙將裝置連線至 IVI,然後從裝置串流音訊。
- 透過藍牙將兩支手機連線至 IVI。
- 選擇一部手機。直接從手機撥打外撥電話,
以及使用 IVI 撥打外撥電話。
- 兩次都請確認串流音訊暫停,且手機音訊透過 IVI 連線的音箱播放。
- 使用同一部手機直接接聽來電,並透過 IVI 接聽來電。
- 兩次都請確認串流音訊暫停,且手機音訊透過 IVI 連線的音箱播放。
- 使用其他已連線的手機重複步驟 3 和 4。
緊急電話撥號
撥打緊急電話是車輛電話和藍牙功能的重要環節。你可以透過多種方式從 IVI 發起緊急電話,包括:
- 獨立 eCall 解決方案
- 整合至 IVI 的 eCall 解決方案
- 在沒有內建系統的情況下,透過藍牙連線手機
撥打緊急電話
雖然 eCall 設備對安全至關重要,但目前尚未整合至 Android。 您可以使用 ConnectionService,透過 Android 公開緊急電話功能,這項功能也能提供緊急電話的無障礙選項。詳情請參閱「 建構通話應用程式」。
以下範例說明如何建立緊急情況的 ConnectionService:
public class YourEmergencyConnectionService extends ConnectionService { @Override public Connection onCreateOutgoingConnection( PhoneAccountHandle connectionManagerAccount, ConnectionRequest request) { // Your equipment specific procedure to make ecall // ... } private void onYourEcallEquipmentReady() { PhoneAccountHandle handle = new PhoneAccountHandle(new ComponentName(context, YourEmergencyConnectionService), YourEmergencyConnectionId); PhoneAccount account = new PhoneAccount.Builder(handle, eCallOnlyAccount) .setSupportedUriSchemes(Arrays.asList(PhoneAccount.SCHEME_TEL)) .setCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS | PhoneAccount.CAPABILITY_MULTI_USER) .build(): mTelecomManager.registerPhoneAccount(account); mTelecomManager.enablePhoneAccount(account.getAccountHandle(), true); } }
啟用藍牙緊急電話撥號功能
在 Android 10 之前,撥打緊急電話需要直接從手機撥號,並視情況啟動特殊設備 (例如在偵測到危險或使用者採取行動時自動觸發)。在 Android 10 以上版本中,車輛的撥號程式可以直接撥打緊急電話號碼,前提是 MAXIMUM_CONNECTED_DEVICES
位於
apps/Bluetooth/res/values/config.xml
:
<!-- For supporting emergency call through the hfp client connection service -->
<bool name=”hfp_client_connection_service_support_emergency_call”>true</bool>
以這種方式實作緊急電話功能後,其他應用程式 (例如語音辨識) 也能撥打緊急電話號碼。
電話簿存取設定檔
藍牙電話簿存取設定檔 (PBAP) 會從連線的遠端裝置下載聯絡人和通話記錄。PBAP 會維護可供搜尋的彙整聯絡人清單,並由 PBAP 用戶端狀態機器更新。每個連線裝置都會與個別的 PBAP 用戶端狀態機器互動,因此撥打電話時,系統會將聯絡人與正確的裝置建立關聯。
PBAP 是單向的,因此 IVI 必須將連線例項化至任何 MAXIMUM_CONNECTED_DEVICES
,
PbapClientService
則定義 IVI 允許的 PBAP 裝置同時連線數量上限。PBAP 用戶端會將每個連線裝置的聯絡人儲存在
聯絡人供應器中,應用程式可以存取這些聯絡人,進而取得每個裝置的電話簿。
此外,IVI 和行動裝置都必須授權設定檔連線,才能建立連線。PBAP 用戶端中斷連線時,內部資料庫會移除與先前連線裝置相關聯的所有聯絡人和通話記錄。
訊息存取設定檔
藍牙訊息存取設定檔 (MAP) 可讓車輛透過連線的遠端裝置傳送及接收簡訊。目前 IVI 不會在本機儲存訊息。當連線的遠端裝置收到訊息時,IVI 會接收並剖析訊息,然後在 Intent 例項中廣播訊息內容,應用程式即可接收訊息。
如要連線至行動裝置以傳送及接收訊息,IVI 必須啟動 MAP 連線。MAXIMUM_CONNECTED_DEVICES
in
MapClientService
定義 IVI 允許的 MAP 裝置同時連線數量上限。訊息必須先經過 IVI 和行動裝置授權,才能轉移。
藍牙立體聲音訊傳輸規範
藍牙進階音訊散布設定檔 (A2DP) 可讓車輛接收連線遙控裝置的音訊串流。
與其他設定檔不同,連線的 A2DP 裝置數量上限是在原生堆疊中強制執行,而非在 Java 中。目前,系統會使用
packages/modules/Bluetooth/system/btif/src/btif_av.cc
中的 kDefaultMaxConnectedAudioDevices
變數,將值硬式編碼為 1
。
音訊/視訊遙控設定檔
藍牙音訊/視訊遙控設定檔 (AVRCP) 可讓車輛控制及瀏覽連線遠端裝置上的媒體播放器。由於 IVI 扮演 AVRCP 控制器的角色,任何影響音訊播放的觸發控制項,都必須透過 A2DP 連線至目標裝置。
如要讓 IVI 透過 AVRCP 瀏覽 Android 手機上的特定媒體播放器,手機上的媒體應用程式必須提供
MediaBrowserService
,並允許 com.android.bluetooth
存取該服務。
建立媒體瀏覽器服務一文詳細說明如何執行這項操作。