藍牙

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來停用預設汽車策略。此資源最初定義為packages/services/Car/service/res/values/config.xmlMAXIMUM_CONNECTED_DEVICES的一部分。

啟用和停用藍牙適配器

策略的核心功能之一是在適當的時間開啟和關閉藍牙適配器。您可以使用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);
}

確定何時開啟和關閉藍牙適配器

透過自訂策略,您可以自由確定哪些事件指示啟用和停用適配器的最佳時間。實現此目的的一種方法是使用CarPowerManager中的電源狀態MAXIMUM_CONNECTED_DEVICES

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 上啟用藍牙並驗證它是否以適當的順序自動連接到正確的裝置。您可以透過設定 UI 或使用以下 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 連接到多個裝置。 HeadsetClientService中的MAX_STATE_MACHINES_POSSIBLE MAXIMUM_CONNECTED_DEVICES定義同時 HFP 連線的最大數量。

當使用者從裝置撥打或接聽電話時,對應的電話帳戶會建立一個HfpClientConnection物件。 Dialer 應用程式與HfpClientConnection物件互動以管理通話功能,例如接聽通話或掛斷電話。

應該注意的是,預設的 Dialer 應用程式不支援多個同時連接的 HFP 裝置。為了實現多設備H​​FP,需要進行定制,讓用戶在撥打電話時選擇使用哪個設備帳戶。然後,應用程式使用正確的帳戶呼叫telecomManager.placeCall 。您需要驗證其他多設備功能是否也能如預期運作。

驗證多設備 HFP

若要檢查藍牙多裝置連線是否正常運作:

  1. 使用藍牙將裝置連接到 IVI 並從裝置傳輸音訊。
  2. 透過藍牙將兩部手機連接到 IVI。
  3. 選擇一部手機。直接從電話撥出電話,並使用 IVI 撥出電話。
    1. 兩次都驗證串流音訊暫停以及電話音訊透過 IVI 連接的揚聲器播放。
  4. 使用同一通電話,直接在電話上接聽來電,並使用 IVI 接聽來電。
    1. 兩次都驗證串流音訊暫停以及電話音訊透過 IVI 連接的揚聲器播放。
  5. 對另一部已連線的手機重複步驟 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 及更高版本中,汽車中的撥號器可以直接撥打緊急號碼,前提是apps/Bluetooth/res/values/config.xml中的MAXIMUM_CONNECTED_DEVICES

<!-- 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 實例化與PbapClientService中任何MAXIMUM_CONNECTED_DEVICES的連接,定義了 IVI 允許的同時 PBAP 設備連接的最大數量。 PBAP 用戶端將每個連接裝置的聯絡人儲存在聯絡人提供者中,然後應用程式可以存取該聯絡人以取得每個裝置的電話簿。

此外,設定檔連線必須獲得 IVI 和行動裝置的授權才能建立連線。當 PBAP 用戶端中斷連線時,內部資料庫會刪除與先前連線的裝置關聯的所有聯絡人和通話記錄。

訊息存取設定檔

藍牙訊息存取設定檔 (MAP) 允許車輛透過連接的遠端裝置發送和接收 SMS 訊息。目前,訊息未本機儲存在 IVI 上。相反,每當連接的遠端裝置收到訊息時,IVI 都會接收並解析該訊息,並在Intent實例中廣播其內容,然後應用程式可以接收該內容。

為了連接到行動裝置以發送和接收訊息,IVI 必須啟動 MAP 連線。 MapClientService中的MAXIMUM_CONNECTED_DEVICES定義了 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存取該服務。建立媒體瀏覽器服務詳細說明如何執行此操作。