Bluetooth

Android は、多くの一般的な車載 Bluetooth プロファイルをサポートする完全な Bluetooth 実装を提供しています。また、他のデバイスやサービスとのパフォーマンスとエクスペリエンスを改善する拡張機能も多数あります。

Bluetooth 接続管理

Android 内では、CarBluetoothService により、IVI への各プロファイル接続について、現在のユーザーの Bluetooth デバイスと優先度リストが維持されます。デバイスは、定義された優先順位でプロファイルに接続されます。デバイスの有効化、無効化、プロファイルへの接続を行うタイミングは、デフォルトの接続ポリシーによって決まります。デフォルトの接続ポリシーは、必要に応じてリソース オーバーレイを使用してオーバーライドできます。

自動車接続管理の設定

デフォルトの通話ポリシーを無効にする

Android Bluetooth スタックでは、デフォルトで有効になっている通話の接続ポリシーを維持できます。このポリシーは、CarBluetoothService において指定された自動車ポリシーと競合しないように、デバイスで無効にする必要があります。Car プロダクト オーバーレイがこの処理を行いますが、 /packages/apps/Bluetooth/res/values/config.xmlenable_phone_policyfalse に設定することで、リソース オーバーレイの通話ポリシーを無効にできます。

デフォルトの自動車ポリシーを使用する

CarBluetoothService ではデフォルトのプロファイルの権限が維持されます。既知のデバイスとそのプロファイル再接続の優先順位のリストは、 service/src/com/android/car/BluetoothProfileDeviceManager.java にあります。

また、Bluetooth 接続管理ポリシーは service/src/com/android/car/BluetoothDeviceConnectionPolicy.java にあります。デフォルトでは、Bluetooth がボンディング デバイスとの接続と接続解除を行うケースを定義します。また、アダプターをオン/オフにするタイミングに関する車両固有のケースも管理します。

独自のカスタム自動車接続管理ポリシーの作成

デフォルトの自動車ポリシーではニーズを満たせない場合は無効にして、代わりに独自のカスタム ポリシーを選択することもできます。カスタム ポリシーでは少なくとも、Bluetooth アダプターを有効または無効にするタイミング、またデバイスを接続するタイミングを決定します。特定の車のプロパティ変更によるイベントなど、さまざまなイベントを使用して、Bluetooth アダプターの有効化と無効化、デバイス接続の開始を行えます。

デフォルトの自動車ポリシーを無効にする

まず、カスタム ポリシーを使用するには、リソース オーバーレイuseDefaultBluetoothConnectionPolicyfalse に設定してデフォルトの自動車ポリシーを無効にする必要があります。このリソースは、元々 packages/services/Car/service/res/values/config.xml の一部として定義されています。

Bluetooth アダプターを有効または無効にする

ポリシーの中核的な機能の 1 つに、適切なタイミングでの Bluetooth アダプターのオンとオフの切り替えがあります。BluetoothAdapter.enable()BluetoothAdapter.disable() のフレームワーク API を使用して、アダプターを有効または無効にできます。こうした呼び出しでは、ユーザーが設定などの方法で選択した永続的な状態を優先する必要があります。その方法の 1 つは次のとおりです。

/**
 * 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);
}

Bluetooth アダプターのオンとオフを切り替えるタイミングを決定する

カスタム ポリシーを使用すると、アダプターの有効と無効の切り替えに最適なタイミングを示すイベントを自由に決定できます。これを行う方法の 1 つとしては、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 呼び出しが提供されます。これにより、各 Bluetooth プロファイル用に定義された優先順位リストに基づいてデバイスの接続が進められます。

これを行うのは、たとえば以下のように Bluetooth アダプターがオンになっている場合です。

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 で Bluetooth を有効にし、適切な順番で正しいデバイスに自動的に接続されることを検証することです。Bluetooth アダプターは、設定 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

また、Bluetooth 接続に関連するデバッグ情報を表示するには、次のコマンドの出力を使用します。

adb shell dumpsys car_service

最後に、独自の自動車ポリシーを作成した場合、カスタム接続の動作を検証するには、デバイス接続をトリガーするために選択したイベントを制御する必要があります。

自動車用 Bluetooth プロファイル

Android では、IVI は Bluetooth で同時接続された複数のデバイスをサポートします。Bluetooth 通話サービスのマルチデバイス機能により、ユーザーは個人用のスマートフォンと仕事用のスマートフォンといった異なる 2 台のデバイスを同時接続し、どちらのデバイスからでもハンズフリー通話を行うことができます。

接続台数の上限は、個々の Bluetooth プロファイルによって、通常そのプロファイル サービス自体の実装内で適用されます。デフォルトでは、デバイス接続台数の上限を CarBluetoothService が別途適用することはありません。

ハンズフリー プロファイル

Bluetooth ハンズフリー プロファイル(HFP)を使用すると、接続されたリモート デバイスを介して車両で電話の発着信が行えます。各デバイス接続では、使用可能なスマートフォン アカウントを IVI アプリにアドバタイズする TelecomManager に個別のスマートフォン アカウントが登録されます。

IVI は HFP 経由で複数のデバイスに接続できます。 HeadsetClientServiceMAX_STATE_MACHINES_POSSIBLE は、HFP 経由で同時接続できるデバイスの最大台数を定義します。

ユーザーがデバイスを使用して電話を発信または受信すると、対応するスマートフォン アカウントによって HfpClientConnection オブジェクトが作成されます。電話アプリは HfpClientConnection オブジェクトとやり取りを行い、通話の受け入れや切断などの通話機能を管理します。

注意しなければならないのは、複数の HFP デバイスを同時接続することがデフォルトの電話アプリでサポートされていないことです。マルチデバイス HFP を実装するには、カスタマイズで、通話時に使用するデバイス アカウントをユーザーが選択できるようにする必要があります。これにより、アプリは正しいアカウントで telecomManager.placeCall を呼び出します。他のマルチデバイス機能も意図したとおりに機能することを確認する必要があります。

マルチデバイス HFP の確認

Bluetooth 経由でマルチデバイス接続が正常に機能していることを確認するには、次の操作を行います。

  1. Bluetooth を使用してデバイスを IVI に接続し、デバイスから音声をストリーミングします。
  2. 2 台のスマートフォンを Bluetooth で IVI に接続します。
  3. 1 台のスマートフォンを選択します。スマートフォンから直接発信し、次に IVI を使用して発信を行います。
    1. どちらの場合も音声のストリーミングが一時停止し、IVI に接続したスピーカーからスマートフォンの音声が再生されることを確認します。
  4. 同じスマートフォンを使用して直接着信を受け、次に IVI を使用して着信を受けます。
    1. どちらの場合も音声のストリーミングが一時停止し、IVI に接続したスピーカーからスマートフォンの音声が再生されることを確認します。
  5. 接続されたもう 1 台のスマートフォンで手順 3 と 4 を繰り返します。

緊急通報

緊急通報機能は、車内での電話機能および Bluetooth 機能の中でも重要な機能です。IVI から緊急通報を開始するには、次のような方法があります。

  • スタンドアロンの eCall ソリューション
  • IVI に統合された eCall ソリューション
  • 組み込みシステムが使用できない場合は、接続された Bluetooth 電話を利用

緊急通報の接続

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.registerPhoneAcccount(account);
        mTelecomManager.enablePhoneAccount(account.getAccountHandle(), true);
    }
}

緊急通報用の Bluetooth の有効化

Android 10 より前の緊急通報の場合、スマートフォンからの直接発信のほか、可能な場合は特殊機器の呼び出し(たとえば、危険やユーザー操作の検出時に自動的にトリガーする)を行っていました。 Android 10 以降では、次のコードが 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>

このように緊急通報を実装すると、他のアプリ(音声認識など)から緊急通報番号に発信することも可能です。

電話帳アクセス プロファイル

Bluetooth 電話帳アクセス プロファイル(PBAP)は、接続されたリモート デバイスから連絡先と通話履歴をダウンロードします。PBAP は、PBAP クライアント ステートマシンによって更新される検索可能な連絡先のリストを集約、保持します。接続されている各デバイスが個別の PBAP クライアント ステートマシンと連携するため、通話時には連絡先情報が適切なデバイスに関連付けられます。

PBAP は単方向であるため、目的のモバイル デバイスとの接続をインスタンス化するには IVI が必要となります。 PbapClientServiceMAXIMUM_DEVICES は、IVI で同時接続できる PBAP デバイスの最大台数を定義します。 PBAP クライアントは、接続されている各デバイスの連絡先を連絡先プロバイダに格納します。この連絡先プロバイダにアプリからアクセスし、各デバイスの電話帳を取得できます。

また、接続を確立するには、IVI とモバイル デバイスの両方でプロファイル接続が承認されている必要があります。PBAP クライアントが切断されると、内部データベースでは、以前に接続されていたデバイスに関連するすべての連絡先と通話履歴が削除されます。

メッセージ アクセス プロファイル

Bluetooth メッセージ アクセス プロファイル(MAP)を使用すると、接続されたリモート デバイスを介して車両で SMS メッセージの送受信が行えます。現在、メッセージが IVI 上でローカルに保存されることはありません。リモート デバイスがメッセージを受信するたびに、IVI はメッセージを受信して解析し、その内容をインテントにブロードキャストします。その後、アプリはこのインテントを受信できます。

メッセージを送受信するためにモバイル デバイスに接続するには、IVI で MAP 接続を開始する必要があります。 MapClientServiceMAXIMUM_CONNECTED_DEVICES は、IVI で同時接続できる MAP デバイスの最大台数を定義します。メッセージを転送する前に、各接続が IVI とモバイル デバイスで承認されている必要があります。

高度な音声配信プロファイル

Bluetooth 高度音声配信プロファイル(A2DP)を使用すると、接続されたリモート デバイスを介して車両で音声ストリームの受信が行えます。 v

他のプロファイルとは異なり、接続できる A2DP デバイスの最大台数は Java ではなくネイティブ スタックで適用されます。この値は、現在、 packages/modules/Bluetooth/system/btif/src/btif_av.cckDefaultMaxConnectedAudioDevices 変数を使用して 1 にハードコードされています。

音声 / 動画リモート コントロール プロファイル

Bluetooth 音声 / 動画リモート コントロール プロファイル(AVRCP)を使用すると、接続されたリモート デバイスでメディア プレーヤーを操作し、ブラウジングできます。IVI は AVRCP コントローラの役割を果たすため、音声再生に影響するトリガーは、ターゲット デバイスへの A2DP 接続に依存します。

Android スマートフォンの特定のメディア プレーヤーで AVRCP を介してブラウズできるようにするには、スマートフォンのメディアアプリで MediaBrowserService を提供し、com.android.bluetooth がそのサービスにアクセスできるようにする必要があります。構築方法について詳しくは、メディア ブラウザ サービスの構築をご覧ください。