藍牙

Android 提供完整的藍牙實現,支持許多常見的車載藍牙配置文件。還有許多增強功能可以提高其他設備和服務的性能和體驗。

藍牙連接管理

在 Android 中, CarBluetoothService維護當前用戶的藍牙設備和與 IVI 的每個配置文件連接的優先級列表。設備以定義的優先級順序連接到配置文件。何時啟用、禁用和將設備連接到配置文件由默認連接策略驅動,如果需要,可以使用資源覆蓋覆蓋該策略。

配置汽車連接管理

禁用默認電話策略

Android 藍牙堆棧維護默認啟用的手機連接策略。必須在您的設備上禁用此策略,以免它與CarBluetoothService中的預期汽車策略衝突。雖然 Car 產品覆蓋應為您處理此問題,但您可以通過在/packages/apps/Bluetooth/res/values/config.xml enable_phone_policyfalse來禁用資源覆蓋中的電話策略。

使用默認汽車政策

CarBluetoothService維護默認配置文件權限。已知設備列表及其配置文件重新連接優先級位於service/src/com/android/car/BluetoothProfileDeviceManager.java中。

同樣,藍牙連接管理策略可以在service/src/com/android/car/BluetoothDeviceConnectionPolicy.java中找到。默認情況下,此策略定義藍牙應連接和斷開綁定設備的實例。它還管理應打開和關閉適配器的汽車特定情況。

創建您自己的自定義汽車連接管理策略

如果默認汽車策略不足以滿足您的需求,也可以禁用它以支持您自己的自定義策略。您的自定義策略將至少負責確定何時啟用和禁用藍牙適配器,以及何時連接設備。可以使用各種事件來啟用/禁用藍牙適配器並啟動設備連接,包括由於特定汽車屬性變化而引起的事件。

禁用默認汽車政策

首先,要使用自定義策略,必須通過在資源覆蓋useDefaultBluetoothConnectionPolicy設置為false來禁用默認汽車策略。此資源最初定義為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 will do nothing if the adapter
    // is already off, keeping the existing saved desired state for next reboot.
    bluetoothAdapter.disable(false);
}

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

使用您的自定義策略,您可以自由確定哪些事件指示啟用和禁用適配器的最佳時間。一種方法是使用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 will perceive 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 application’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定義了同時 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 中。可以通過 Android 使用ConnectionService來公開緊急呼叫功能,這還具有為緊急呼叫引入可訪問性選項的好處。要了解更多信息,請參閱構建呼叫應用程序

以下是如何建立緊急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.registerPhoneAcccount(account);
        mTelecomManager.enablePhoneAccount(account.getAccountHandle(), true);
    }
}

為緊急呼叫啟用藍牙

在 Android 10 之前呼叫緊急呼叫涉及從電話直接撥號並在可用時調用特殊設備(例如,在檢測到危險或用戶操作時自動觸發)。從 Android 10 開始,車內的 Dialer 可以直接撥打緊急號碼,前提是此代碼包含在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 實例化與任何所需移動設備的連接。 PbapClientService中的MAXIMUM_DEVICES定義了 IVI 允許的最大同時 PBAP 設備連接數。 PBAP 客戶端將每個連接設備的聯繫人存儲在Contacts Provider中,然後應用程序可以訪問這些聯繫人以獲取每個設備的電話簿。

此外,配置文件連接必須得到 IVI 和移動設備的授權才能建立連接。當 PBAP 客戶端斷開連接時,內部數據庫會刪除與之前連接的設備相關的所有聯繫人和通話歷史記錄。

消息訪問配置文件

藍牙消息訪問配置文件 (MAP) 允許車輛通過連接的遠程設備發送和接收 SMS 消息。目前,消息並未本地存儲在 IVI 上。相反,每當連接的遠程設備接收到消息時,IVI 就會接收並解析消息,並在Intent中廣播其內容,然後應用程序可以接收到。

為了連接到移動設備以發送和接收消息,IVI 必須啟動 MAP 連接。 MapClientService中的MAXIMUM_CONNECTED_DEVICES定義了 IVI 允許的最大同時 MAP 設備連接數。每次連接都必須經過 IVI 和移動設備的授權,然後才能傳輸消息。

高級音頻分配配置文件

藍牙高級音頻分配配置文件 (A2DP) 允許車輛從連接的遠程設備接收音頻流。

與其他配置文件不同,連接的 A2DP 設備的最大數量是在本機堆棧中而不是在 Java 中強制執行的。該值當前使用packages/modules/Bluetooth/system/vtif/src/btif_av.cc中的kDefaultMaxConnectedAudioDevices變量硬編碼為1

音頻/視頻遠程控製配置文件

藍牙音頻/視頻遠程控製配置文件 (AVRCP) 允許車輛控制和瀏覽連接的遠程設備上的媒體播放器。由於 IVI 扮演 AVRCP 控制器的角色,任何影響音頻播放的觸發控件都依賴於與目標設備的 A2DP 連接。

要讓 IVI 通過 AVRCP 瀏覽 Android 手機上的特定媒體播放器,手機上的媒體應用程序必須提供MediaBrowserService並允許com.android.bluetooth訪問該服務。構建媒體瀏覽器服務詳細說明瞭如何執行此操作。