Bluetooth の概要

Android プラットフォームは Bluetooth ネットワーク スタックをサポートしています。これにより、デバイスは他の Bluetooth デバイスとワイヤレスでデータを交換できます。アプリ フレームワークでは、Android Bluetooth API を介して Bluetooth 機能にアクセスできます。これらの API を使用すると、アプリは他の Bluetooth デバイスにワイヤレス接続し、ポイントツーポイントおよびマルチポイントの無線機能を実現できます。

Bluetooth API を使用すると、Android アプリは次のことができます。

  • 他の Bluetooth デバイスをスキャンする
  • ペア設定された Bluetooth デバイスのローカル Bluetooth アダプターをクエリする
  • RFCOMM チャネルの確立
  • サービス ディスカバリを介して他のデバイスに接続する
  • 他のデバイスとの間でデータを転送する
  • 複数の接続を管理する

このページでは、従来の Bluetooth に焦点を当てています。ストリーミングや Android デバイス間の通信など、バッテリー消費量の多い操作には、クラシック Bluetooth が適しています。低消費電力の要件がある Bluetooth デバイス向けに、Android 4.3(API レベル 18)では Bluetooth Low Energy の API サポートが導入されています。詳細については、Bluetooth Low Energy をご覧ください。

このドキュメントでは、ヘルスデバイス プロファイルを含む、さまざまな Bluetooth プロファイルについて説明します。次に、Android Bluetooth API を使用して、Bluetooth を使用した通信に必要な 4 つの主要タスク(Bluetooth の設定、ペア設定されたデバイスまたはローカルエリアで使用可能なデバイスの検出、デバイスの接続、デバイス間のデータ転送)を実行する方法について説明します。

基本情報

Bluetooth 対応デバイスが相互にデータを送信するには、まずペアリングプロセスを使用して通信チャネルを形成する必要があります。1 つのデバイス(検出可能なデバイス)は、受信接続リクエストに対して自身を使用可能にします。別のデバイスが、サービス ディスカバリ プロセスを使用して検出可能なデバイスを検出します。検出可能なデバイスがペア設定リクエストを受け入れると、2 つのデバイスはボンディング プロセスを完了し、セキュリティ キーを交換します。デバイスは後で使用するためにこれらのキーをキャッシュに保存します。ペア設定とボンディングのプロセスが完了すると、2 つのデバイスは情報を交換します。セッションが完了すると、ペア設定リクエストを開始したデバイスは、検出可能なデバイスにリンクしていたチャンネルを解放します。ただし、2 つのデバイスはボンドされたままになるため、今後のセッション中に自動的に再接続できます。ただし、これらのデバイスが互いに通信範囲内にあり、どちらのデバイスもボンドを除去していないことが条件となります。

Bluetooth の権限

アプリで Bluetooth 機能を使用するには、2 つの権限を宣言する必要があります。最初のメソッドは BLUETOOTH です。接続のリクエスト、接続の受け入れ、データ転送などの Bluetooth 通信を行うには、この権限が必要です。

他に宣言する必要がある権限は ACCESS_FINE_LOCATION です。Bluetooth スキャンを使用してユーザーの位置情報を収集できるため、アプリにこの権限が必要になります。この情報は、ユーザーが所有するデバイスだけでなく、店舗や交通機関などの場所で使用されている Bluetooth ビーコンから得られることもあります。

Android 10 以降で実行されるサービスは、ACCESS_BACKGROUND_LOCATION 権限がない限り、Bluetooth デバイスを検出できません。この要件の詳細については、バックグラウンドでの位置情報へのアクセスをご覧ください。

次のコード スニペットは、権限を確認する方法を示しています。

Kotlin

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    if (ContextCompat.checkSelfPermission(baseContext,
        Manifest.permission.ACCESS_BACKGROUND_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
                    PERMISSION_CODE)
        }
}

Java

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
  if (ContextCompat.checkSelfPermission(baseContext,
      Manifest.permission.ACCESS_BACKGROUND_LOCATION)
      != PackageManager.PERMISSION_GRANTED) {
          ActivityCompat.requestPermissions(
              MyActivity.this,
              new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
                  PERMISSION_CODE)
      }
}

この権限要件の例外は、Android 11 以降を搭載したデバイスにアプリがインストールされ、コンパニオン デバイスのペア設定を使用してデバイスを関連付けている場合です。この場合、デバイスが関連付けられると、アプリは位置情報の利用許可がなくても、関連付けられている Bluetooth デバイスをスキャンできます。

Android 8.0(API レベル 26)以降を搭載したデバイスでは、位置情報の利用許可がなくても、CompanionDeviceManager を使用することで、アプリに代わって付近のコンパニオン デバイスをスキャンできます。このオプションの詳細については、コンパニオン デバイスのペア設定をご覧ください。

注: アプリが Android 9(API レベル 28)以前をターゲットとしている場合は、ACCESS_FINE_LOCATION 権限の代わりに ACCESS_COARSE_LOCATION 権限を宣言できます。

アプリでデバイスの検出を開始したり、Bluetooth 設定を操作したりする場合は、BLUETOOTH 権限に加えて BLUETOOTH_ADMIN 権限を宣言する必要があります。ほとんどのアプリは、ローカルの Bluetooth デバイスを検出する機能のためにのみ、この権限を必要とします。この権限によって付与されるその他の能力は、アプリがユーザーのリクエストに応じて Bluetooth 設定を変更する「電源マネージャー」である場合を除き、使用しないでください。

アプリのマニフェスト ファイルで Bluetooth 権限を宣言します。たとえば、以下の場合です。

<manifest ... >
  <uses-permission android:name="android.permission.BLUETOOTH" />
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

  <!-- If your app targets Android 9 or lower, you can declare
       ACCESS_COARSE_LOCATION instead. -->
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  ...
</manifest>

アプリの権限の宣言について詳しくは、<uses-permission> のリファレンスをご覧ください。

プロフィールを操作する

Android 3.0 以降、Bluetooth API は Bluetooth プロファイルの操作をサポートしています。Bluetooth プロファイルは、デバイス間で Bluetooth ベースの通信を行うためのワイヤレス インターフェース仕様です。一例として、Hands-Free プロファイルがあります。スマートフォンをワイヤレス ヘッドセットに接続するには、両方のデバイスがハンズフリー プロファイルをサポートしている必要があります。

Android Bluetooth API には、次の Bluetooth プロファイルの実装が用意されています。

  • ヘッドセット: ヘッドセット プロファイルは、スマートフォンで使用する Bluetooth ヘッドセットをサポートします。Android には、Bluetooth ヘッドセット サービスを制御するためのプロキシである BluetoothHeadset クラスが用意されています。これには、Bluetooth ヘッドセットとハンズフリー(v1.5)の両方のプロファイルが含まれます。BluetoothHeadset クラスは AT コマンドをサポートしています。このトピックの詳細については、ベンダー固有の AT コマンドをご覧ください。
  • A2DP。Advanced Audio Distribution Profile(A2DP)プロファイルは、Bluetooth 接続を介してデバイス間で高品質のオーディオをストリーミングする方法を定義します。Android には、Bluetooth A2DP サービスを制御するためのプロキシである BluetoothA2dp クラスが用意されています。
  • Health Device:Android 4.0(API レベル 14)では、Bluetooth ヘルス デバイス プロファイル(HDP)のサポートが導入されています。これにより、心拍数モニター、血液計、体温計、体重計など、Bluetooth をサポートする健康機器と Bluetooth で通信するアプリを作成できます。サポートされているデバイスと、それぞれに対応するデバイスデータ スペシャライゼーション コードのリストについては、Bluetooth の HDP デバイスデータ スペシャライゼーションをご覧ください。これらの値は、ISO/IEEE 11073-20601 [7] 仕様の命名コードの付録でも MDC_DEV_SPEC_PROFILE_* として参照されています。HDP の詳細については、Health Device Profile をご覧ください。

プロフィールに関する基本的な手順は次のとおりです。

  1. Bluetooth の設定の説明に沿って、デフォルトのアダプターを取得します。
  2. BluetoothProfile.ServiceListener を設定します。このリスナーは、サービスとの接続または接続解除を BluetoothProfile クライアントに通知します。
  3. getProfileProxy() を使用して、プロファイルに関連付けられているプロファイル プロキシ オブジェクトへの接続を確立します。以下の例では、プロファイル プロキシ オブジェクトは BluetoothHeadset のインスタンスです。
  4. onServiceConnected() で、プロファイル プロキシ オブジェクトに対するハンドルを取得します。
  5. プロファイル プロキシ オブジェクトを取得したら、それを使用して接続の状態をモニタリングし、そのプロファイルに関連するその他のオペレーションを実行できます。

たとえば、次のコード スニペットは、BluetoothHeadset プロキシ オブジェクトに接続して、ヘッドセット プロファイルを制御する方法を示しています。

Kotlin

var bluetoothHeadset: BluetoothHeadset? = null

// Get the default adapter
val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()

private val profileListener = object : BluetoothProfile.ServiceListener {

    override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
        if (profile == BluetoothProfile.HEADSET) {
            bluetoothHeadset = proxy as BluetoothHeadset
        }
    }

    override fun onServiceDisconnected(profile: Int) {
        if (profile == BluetoothProfile.HEADSET) {
            bluetoothHeadset = null
        }
    }
}

// Establish connection to the proxy.
bluetoothAdapter?.getProfileProxy(context, profileListener, BluetoothProfile.HEADSET)

// ... call functions on bluetoothHeadset

// Close proxy connection after use.
bluetoothAdapter?.closeProfileProxy(BluetoothProfile.HEADSET, bluetoothHeadset)

Java

BluetoothHeadset bluetoothHeadset;

// Get the default adapter
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

private BluetoothProfile.ServiceListener profileListener = new BluetoothProfile.ServiceListener() {
    public void onServiceConnected(int profile, BluetoothProfile proxy) {
        if (profile == BluetoothProfile.HEADSET) {
            bluetoothHeadset = (BluetoothHeadset) proxy;
        }
    }
    public void onServiceDisconnected(int profile) {
        if (profile == BluetoothProfile.HEADSET) {
            bluetoothHeadset = null;
        }
    }
};

// Establish connection to the proxy.
bluetoothAdapter.getProfileProxy(context, profileListener, BluetoothProfile.HEADSET);

// ... call functions on bluetoothHeadset

// Close proxy connection after use.
bluetoothAdapter.closeProfileProxy(bluetoothHeadset);

ベンダー固有の AT コマンド

Android 3.0(API レベル 11)以降では、ヘッドセットが送信する事前定義されたベンダー固有の AT コマンド(Plantronics +XEVENT コマンドなど)のシステム ブロードキャストを受信するようにアプリを登録できます。たとえば、アプリは接続済みデバイスのバッテリー残量を示すブロードキャストを受信し、必要に応じてユーザーに通知したり、その他のアクションを実行したりできます。ヘッドセットのベンダー固有の AT コマンドを処理する ACTION_VENDOR_SPECIFIC_HEADSET_EVENT インテントのブロードキャスト レシーバを作成します。

ヘルスデバイスのプロファイル

Android 4.0(API レベル 14)では、Bluetooth ヘルスデバイス プロファイル(HDP)のサポートが導入されています。これにより、心拍数モニター、血液計、体温計、体重計など、Bluetooth をサポートする健康機器と Bluetooth で通信するアプリを作成できます。Bluetooth Health API には、BluetoothHealth クラス、BluetoothHealthCallback クラス、BluetoothHealthAppConfiguration クラスが含まれています。これらのクラスについては、鍵クラスとインターフェースをご覧ください。

Bluetooth Health API を使用する場合は、次の HDP の主なコンセプトを理解しておくことをおすすめします。

ソース
Android スマートフォンやタブレットなどのスマート デバイスに医療データを送信する健康デバイス(体重計、血糖測定器、体温計など)。
シンク
医療データを受け取るスマート デバイス。Android HDP アプリの場合、シンクは BluetoothHealthAppConfiguration オブジェクトで表されます。
登録
特定の健康機器と通信するためにシンクを登録するために使用されるプロセス。
つながり
健康機器(ソース)とスマート デバイス(シンク)の間でチャネルを開くために使用されるプロセス。

HDP アプリケーションを作成する

Android HDP アプリの作成に関する基本的な手順は次のとおりです。

  1. BluetoothHealth プロキシ オブジェクトへの参照を取得します。

    通常のヘッドセットや A2DP プロファイル デバイスと同様に、BluetoothProfile.ServiceListenerHEALTH プロファイル タイプを指定して getProfileProxy() を呼び出して、プロファイル プロキシ オブジェクトとの接続を確立する必要があります。

  2. BluetoothHealthCallback を作成し、ヘルスシンクとして機能するアプリケーション構成(BluetoothHealthAppConfiguration)を登録します。
  3. ヘルスデバイスへの接続を確立します。

    注: デバイスによっては、接続が自動的に開始されます。そのようなデバイスでは、この手順を行う必要はありません。

  4. ヘルスデバイスに正常に接続されたら、ファイル記述子を使用してヘルスデバイスの読み取りと書き込みを行います。受信したデータは、IEEE 11073 仕様を実装したヘルス マネージャーを使用して解釈する必要があります。
  5. 完了したら、Health チャンネルを閉じてアプリケーションの登録を解除します。また、非アクティブが長期間続いた場合も、チャネルはクローズされます。

Bluetooth を設定する

アプリが Bluetooth で通信する前に、デバイスで Bluetooth がサポートされているか、サポートされている場合は有効になっていることを確認します。

Bluetooth がサポートされていない場合は、すべての Bluetooth 機能を適切に無効にする必要があります。Bluetooth はサポートされているものの、無効になっている場合は、アプリを離れることなく、Bluetooth を有効にするようユーザーにリクエストできます。この設定は、BluetoothAdapter を使用して次の 2 つのステップで行うことができます。

  1. BluetoothAdapter を取得します。

    BluetoothAdapter は、あらゆる Bluetooth アクティビティに必要です。BluetoothAdapter を取得するには、静的な getDefaultAdapter() メソッドを呼び出します。これにより、デバイス独自の Bluetooth アダプター(Bluetooth 無線通信)を表す BluetoothAdapter が返されます。システム全体に 1 つの Bluetooth アダプターがあり、アプリはこのオブジェクトを使用して操作できます。getDefaultAdapter()null を返す場合、デバイスは Bluetooth をサポートしていません。次に例を示します。

    Kotlin

    val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
    if (bluetoothAdapter == null) {
        // Device doesn't support Bluetooth
    }
    

    Java

    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (bluetoothAdapter == null) {
        // Device doesn't support Bluetooth
    }
    
  2. Bluetooth を有効にします。

    次に、Bluetooth が有効になっていることを確認します。isEnabled() を呼び出して、Bluetooth が現在有効になっているかどうかを確認します。このメソッドが false を返した場合、Bluetooth は無効になっています。Bluetooth の有効化をリクエストするには、startActivityForResult() を呼び出して、ACTION_REQUEST_ENABLE インテントのアクションを渡します。この呼び出しは、アプリを停止せずに、システム設定から Bluetooth を有効にするリクエストを発行します。次に例を示します。

    Kotlin

    if (bluetoothAdapter?.isEnabled == false) {
        val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
    }
    

    Java

    if (!bluetoothAdapter.isEnabled()) {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    }
    

    図 1 に示すように、Bluetooth を有効にするためのユーザー権限をリクエストするダイアログが表示されます。ユーザーが「はい」と応答すると、システムは Bluetooth の有効化を開始し、プロセスが完了(または失敗)すると、アプリにフォーカスが戻ります。

    図 1: Bluetooth の有効化ダイアログ

    startActivityForResult() に渡される REQUEST_ENABLE_BT 定数は、0 より大きいローカルで定義された整数です。この定数は、onActivityResult() の実装で requestCode パラメータとして渡されます。

    Bluetooth の有効化が成功すると、アクティビティは onActivityResult() コールバックで RESULT_OK 結果コードを受け取ります。エラーのため Bluetooth が有効でなかった場合(またはユーザーが「いいえ」と応答した場合)、結果コードは RESULT_CANCELED です。

必要に応じて、アプリは ACTION_STATE_CHANGED ブロードキャスト インテントをリッスンすることもできます。このインテントは、Bluetooth 状態が変化するたびにシステムがブロードキャストします。このブロードキャストには、追加フィールド EXTRA_STATEEXTRA_PREVIOUS_STATE が含まれており、それぞれ新しい Bluetooth 状態と古い Bluetooth 状態が含まれています。これらの追加フィールドで使用できる値は STATE_TURNING_ONSTATE_ONSTATE_TURNING_OFFSTATE_OFF です。このブロードキャストのリッスンは、Bluetooth の状態に加えられたランタイムの変更をアプリで検出する必要がある場合に役立ちます。

ヒント: 検出の許可を有効にすると、Bluetooth が自動的に有効になります。Bluetooth アクティビティを実行する前にデバイスの検出可能性を一貫して有効にする場合は、上記のステップ 2 をスキップできます。詳細については、このページの検出可能性を有効にするをご覧ください。

デバイスの検索

BluetoothAdapter を使用すると、デバイス検出によって、またはペア設定されたデバイスのリストをクエリして、リモート Bluetooth デバイスを見つけることができます。

デバイス検出とは、ローカルエリアで Bluetooth 対応デバイスを検索し、各デバイスの情報を要求するスキャン手順です。このプロセスは、検出、照会、スキャンと呼ばれることもあります。ただし、付近の Bluetooth デバイスが検出リクエストに応答するのは、そのデバイスが現在「検出可能」な状態で情報リクエストを受け入れている場合に限られます。デバイスが検出可能な場合、検出リクエストに応答して、デバイス名、クラス、一意の MAC アドレスなどの情報を共有します。この情報を使用して、検出プロセスを実行しているデバイスは、検出されたデバイスへの接続を開始することを選択できます。

検出可能なデバイスからユーザーの位置情報が公開される可能性があるため、デバイス検出プロセスでは位置情報へのアクセスが必要です。Android 8.0(API レベル 26)以降を搭載しているデバイスでアプリを使用している場合は、Companion Device Manager API を使用します。この API は、アプリに代わってデバイスの検出を実行するため、アプリで位置情報の利用許可をリクエストする必要はありません。

リモート デバイスと初めて接続すると、ペア設定リクエストが自動的にユーザーに表示されます。デバイスがペア設定されると、そのデバイスに関する基本情報(デバイス名、クラス、MAC アドレスなど)が保存され、Bluetooth API を使用して読み取ることができます。リモート デバイスの既知の MAC アドレスを使用すると、デバイスが範囲内にあれば、検出を行うことなくいつでもその MAC アドレスを使用して接続を開始できます。

ペア設定と接続には違いがあります。

  • ペアリングとは、2 つのデバイスが互いの存在を認識し、認証に使用できる共有リンクキーを持ち、相互に暗号化された接続を確立できることを意味します。
  • 「接続」とは、現在デバイスが RFCOMM チャネルを共有し、相互にデータを送信できることを意味します。現在の Android Bluetooth API では、RFCOMM 接続を確立する前にデバイスをペア設定する必要があります。Bluetooth API を使用して暗号化された接続を開始すると、自動的にペア設定が実行されます。

以下のセクションでは、ペア設定されたデバイスを見つける方法と、デバイスの検出を使用して新しいデバイスを検出する方法について説明します。

注: デフォルトでは、Android 搭載デバイスは検出できません。ユーザーは、システム設定を使用して一定期間デバイスを検出可能にすることができます。また、アプリは、アプリを離れることなく、検出の許可を有効にするようリクエストすることもできます。詳細については、このページの検出の許可を有効にするをご覧ください。

ペア設定されたデバイスをクエリする

デバイス検出を実行する前に、ペア設定されたデバイスのセットをクエリして、目的のデバイスがすでに認識されているかどうかを確認することをおすすめします。そのためには、getBondedDevices() を呼び出します。これは、ペア設定されたデバイスを表す BluetoothDevice オブジェクトのセットを返します。たとえば、次のコード スニペットに示すように、ペア設定されているすべてのデバイスを照会して、各デバイスの名前と MAC アドレスを取得できます。

Kotlin

val pairedDevices: Set<BluetoothDevice>? = bluetoothAdapter?.bondedDevices
pairedDevices?.forEach { device ->
    val deviceName = device.name
    val deviceHardwareAddress = device.address // MAC address
}

Java

Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();

if (pairedDevices.size() > 0) {
    // There are paired devices. Get the name and address of each paired device.
    for (BluetoothDevice device : pairedDevices) {
        String deviceName = device.getName();
        String deviceHardwareAddress = device.getAddress(); // MAC address
    }
}

Bluetooth デバイスとの接続を開始するために、関連付けられた BluetoothDevice オブジェクトから必要なのは、getAddress() を呼び出して MAC アドレスを取得することだけです。接続の作成について詳しくは、デバイスの接続をご覧ください。

注意: デバイス検出を実行すると、Bluetooth アダプターのリソースが大量に消費されます。接続するデバイスが見つかったら、接続を試す前に cancelDiscovery() で検出を停止してください。また、デバイスに接続している間は検出を実行しないでください。検出プロセスにより、既存の接続で利用可能な帯域幅が大幅に減少します。

デバイスの検出

デバイスの検出を開始するには、startDiscovery() を呼び出します。プロセスは非同期で、検出が正常に開始されたかどうかを示すブール値を返します。検出プロセスでは通常、約 12 秒間の照会スキャンが行われ、その後に Bluetooth 名を取得することが判明した各デバイスのページスキャンが続きます。

検出された各デバイスに関する情報を受信するには、アプリで ACTION_FOUND インテントに BroadcastReceiver を登録する必要があります。システムは、デバイスごとにこのインテントをブロードキャストします。インテントには追加フィールド EXTRA_DEVICEEXTRA_CLASS が含まれ、これらのフィールドにはそれぞれ BluetoothDeviceBluetoothClass が含まれます。次のコード スニペットは、デバイスが検出されたときにブロードキャストを処理するように登録する方法を示しています。

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    ...

    // Register for broadcasts when a device is discovered.
    val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
    registerReceiver(receiver, filter)
}

// Create a BroadcastReceiver for ACTION_FOUND.
private val receiver = object : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        val action: String = intent.action
        when(action) {
            BluetoothDevice.ACTION_FOUND -> {
                // Discovery has found a device. Get the BluetoothDevice
                // object and its info from the Intent.
                val device: BluetoothDevice =
                        intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
                val deviceName = device.name
                val deviceHardwareAddress = device.address // MAC address
            }
        }
    }
}

override fun onDestroy() {
    super.onDestroy()
    ...

    // Don't forget to unregister the ACTION_FOUND receiver.
    unregisterReceiver(receiver)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...

    // Register for broadcasts when a device is discovered.
    IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
    registerReceiver(receiver, filter);
}

// Create a BroadcastReceiver for ACTION_FOUND.
private final BroadcastReceiver receiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Discovery has found a device. Get the BluetoothDevice
            // object and its info from the Intent.
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            String deviceName = device.getName();
            String deviceHardwareAddress = device.getAddress(); // MAC address
        }
    }
};

@Override
protected void onDestroy() {
    super.onDestroy();
    ...

    // Don't forget to unregister the ACTION_FOUND receiver.
    unregisterReceiver(receiver);
}

Bluetooth デバイスとの接続を開始するために、関連付けられた BluetoothDevice オブジェクトから必要なのは、getAddress() を呼び出して MAC アドレスを取得することだけです。接続の作成について詳しくは、デバイスの接続をご覧ください。

注意: デバイス検出を実行すると、Bluetooth アダプターのリソースが大量に消費されます。接続するデバイスが見つかったら、接続を試す前に cancelDiscovery() で検出を停止してください。また、デバイスに接続している間は検出を実行しないでください。検出プロセスにより、既存の接続で利用可能な帯域幅が大幅に減少します。

検出の許可を有効にする

ローカル デバイスを他のデバイスから検出可能にするには、ACTION_REQUEST_DISCOVERABLE インテントを指定して startActivityForResult(Intent, int) を呼び出します。これにより、設定アプリに移動せずにシステムの検出可能モードを有効にするリクエストが発行されます。これにより、自分のアプリが停止します。デフォルトでは、デバイスは 120 秒間(2 分間)検出可能となります。EXTRA_DISCOVERABLE_DURATION エクストラを追加することで、異なる時間(最大 3,600 秒(1 時間))を定義できます。

注意: EXTRA_DISCOVERABLE_DURATION エクストラの値を 0 に設定すると、デバイスは常に検出可能になります。この構成は安全性が低いため、おすすめしません。

次のコード スニペットでは、デバイスを 5 分間(300 秒)検出可能に設定しています。

Kotlin

val discoverableIntent: Intent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE).apply {
    putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300)
}
startActivity(discoverableIntent)

Java

Intent discoverableIntent =
        new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
図 2: 検出の許可を有効にするダイアログ。

図 2 に示すように、デバイスを検出可能にする許可をユーザーにリクエストするダイアログが表示されます。ユーザーが「はい」と応答すると、デバイスは指定された時間、検出可能になります。次に、アクティビティは onActivityResult() コールバックへの呼び出しを受け取ります。結果コードは、デバイスが検出可能である期間と同じになります。ユーザーが「いいえ」と応答した場合、またはエラーが発生した場合、結果コードは RESULT_CANCELED です。

注: デバイスで Bluetooth が有効になっていない場合、デバイスを検出可能にすると、Bluetooth が自動的に有効になります。

デバイスは指定された時間、通知なく検出可能モードのままになります。検出可能なモードが変更されたときに通知を受け取るには、ACTION_SCAN_MODE_CHANGED インテントに BroadcastReceiver を登録します。このインテントには追加フィールド EXTRA_SCAN_MODEEXTRA_PREVIOUS_SCAN_MODE があり、それぞれ新しいスキャンモードと古いスキャンモードを提供します。各エクストラの有効な値は次のとおりです。

SCAN_MODE_CONNECTABLE_DISCOVERABLE
デバイスが検出可能モードになっています。
SCAN_MODE_CONNECTABLE
デバイスは検出可能モードではないが、接続を受信できる状態である。
SCAN_MODE_NONE
デバイスが検出可能モードではなく、接続を受信できません。

リモート デバイスへの接続を開始する場合は、デバイスの検出を有効にする必要はありません。検出の許可の有効化は、受信接続を受け入れるサーバー ソケットをアプリでホストする場合にのみ必要です。リモート デバイスは、他のデバイスへの接続を開始する前に、他のデバイスを検出できる必要があるためです。

デバイスの接続

2 つのデバイス間の接続を作成するには、一方のデバイスがサーバー ソケットを開く必要があり、もう一方のデバイスはサーバー デバイスの MAC アドレスを使用して接続を開始する必要があるため、サーバー側とクライアント側の両方のメカニズムを実装する必要があります。サーバー デバイスとクライアント デバイスは、それぞれ異なる方法で必要な BluetoothSocket を取得します。サーバーは受信接続が受け入れられると、ソケット情報を受け取ります。クライアントは、サーバーに対して RFCOMM チャネルを開くときにソケット情報を提供します。

サーバーとクライアントは、同じ RFCOMM チャネル上でそれぞれ接続されている BluetoothSocket を接続している場合、相互接続しているとみなされます。この時点で、各デバイスは入出力ストリームを取得し、データ転送を開始できます。これについては、接続を管理するのセクションをご覧ください。このセクションでは、2 つのデバイス間で接続を開始する方法について説明します。

接続方法

実装方法の 1 つは、各デバイスをサーバーとして自動的に準備し、各デバイスでサーバー ソケットを開いて接続をリッスンするようにすることです。この場合、どちらかのデバイスがもう片方のデバイスとの接続を開始して、クライアントになります。または、一方のデバイスで接続を明示的にホストし、オンデマンドでサーバー ソケットをオープンして、もう一方のデバイスで接続を開始することもできます。

図 3: Bluetooth ペア設定ダイアログ

注: 2 台のデバイスが以前にペア設定されていない場合、図 3 に示すように、Android フレームワークは接続手順中にペア設定リクエスト通知またはダイアログを自動的にユーザーに表示します。 そのため、アプリはデバイスを接続しようとする際に、デバイスがペア設定されているかどうかを考慮する必要はありません。ユーザーが 2 つのデバイスを正常にペア設定するまで RFCOMM 接続はブロックされます。ユーザーがペア設定を拒否した場合、またはペア設定プロセスが失敗またはタイムアウトした場合は、接続の試行が失敗します。

サーバーとして接続

2 つのデバイスを接続する場合、一方のデバイスは開いている BluetoothServerSocket を保持してサーバーとして動作する必要があります。サーバー ソケットの目的は、受信接続リクエストをリッスンし、リクエストの受け入れ後に接続された BluetoothSocket を提供することです。BluetoothSocketBluetoothServerSocket から取得すると、デバイスが追加の接続を受け入れる場合を除き、BluetoothServerSocket は破棄できます(破棄する必要があります)。

サーバー ソケットを設定して接続を受け入れるには、次の手順を完了します。

  1. listenUsingRfcommWithServiceRecord() を呼び出して BluetoothServerSocket を取得します。

    この文字列はサービスの識別可能な名前で、デバイス上の新しいサービス ディスカバリ プロトコル(SDP)データベース エントリに自動的に書き込まれます。名前は任意で、簡単にアプリケーション名を指定できます。Universally Unique Identifier(UUID)も SDP エントリに含まれ、クライアント デバイスとの接続の合意の基礎を形成します。つまり、クライアントはこのデバイスと接続しようとすると、接続するサービスを一意に識別する UUID を受け取ります。接続が受け入れられるには、これらの UUID が一致している必要があります。

    UUID は、情報を一意に識別するために使用される、文字列 ID の標準化された 128 ビット形式です。UUID の重要な点は、任意のランダムな ID を選択できる十分な大きさであり、他の ID と競合しないことです。この場合は、アプリの Bluetooth サービスを一意に識別するために使用されます。アプリケーションで使用する UUID を取得するには、ウェブ上で多くのランダムな UUID 生成ツールのいずれかを使用して、fromString(String)UUID を初期化します。

  2. 接続リクエストのリッスンを開始するには、accept() を呼び出します。

    これはブロッキング呼び出しです。接続が受け入れられたか、例外が発生したときに返されます。接続が受け入れられるのは、リモート デバイスが、このリスニング サーバー ソケットに登録された UUID と一致する UUID を含む接続リクエストを送信した場合のみです。成功すると、accept() は接続された BluetoothSocket を返します。

  3. 追加の接続を受け入れる場合を除き、close() を呼び出します。

    このメソッド呼び出しは、サーバー ソケットとそのすべてのリソースを解放しますが、accept() によって返された接続済み BluetoothSocket は閉じません。TCP/IP とは異なり、RFCOMM ではチャネルごとに一度に 1 つの接続クライアントしか許可されないため、ほとんどの場合、接続されたソケットを受け入れた直後に BluetoothServerSocketclose() を呼び出すのが合理的です。

accept() 呼び出しはブロッキング呼び出しであるため、アプリが他のユーザーの操作に引き続き応答できるように、メイン アクティビティの UI スレッドでは実行しないでください。通常、BluetoothServerSocket または BluetoothSocket を含むすべての作業は、アプリケーションによって管理される新しいスレッドで行うのが合理的です。accept() などのブロックされた呼び出しを中止するには、BluetoothServerSocket または BluetoothSocket に対して別のスレッドから close() を呼び出します。BluetoothServerSocket または BluetoothSocket のすべてのメソッドはスレッドセーフです。

受信接続を受け入れるサーバー コンポーネントの簡略化したスレッドを次に示します。

Kotlin

private inner class AcceptThread : Thread() {
    
    private val mmServerSocket: BluetoothServerSocket? by lazy(LazyThreadSafetyMode.NONE) {
        bluetoothAdapter?.listenUsingInsecureRfcommWithServiceRecord(NAME, MY_UUID)
    }

    override fun run() {
        // Keep listening until exception occurs or a socket is returned.
        var shouldLoop = true
        while (shouldLoop) {
            val socket: BluetoothSocket? = try {
                mmServerSocket?.accept()
            } catch (e: IOException) {
                Log.e(TAG, "Socket's accept() method failed", e)
                shouldLoop = false
                null
            }
            socket?.also {
                manageMyConnectedSocket(it)
                mmServerSocket?.close()
                shouldLoop = false
            }
        }
    }

    // Closes the connect socket and causes the thread to finish.
    fun cancel() {
        try {
            mmServerSocket?.close()
        } catch (e: IOException) {
            Log.e(TAG, "Could not close the connect socket", e)
        }
    }
}

Java

private class AcceptThread extends Thread {
    private final BluetoothServerSocket mmServerSocket;

    public AcceptThread() {
        // Use a temporary object that is later assigned to mmServerSocket
        // because mmServerSocket is final.
        BluetoothServerSocket tmp = null;
        try {
            // MY_UUID is the app's UUID string, also used by the client code.
            tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException e) {
            Log.e(TAG, "Socket's listen() method failed", e);
        }
        mmServerSocket = tmp;
    }

    public void run() {
        BluetoothSocket socket = null;
        // Keep listening until exception occurs or a socket is returned.
        while (true) {
            try {
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                Log.e(TAG, "Socket's accept() method failed", e);
                break;
            }

            if (socket != null) {
                // A connection was accepted. Perform work associated with
                // the connection in a separate thread.
                manageMyConnectedSocket(socket);
                mmServerSocket.close();
                break;
            }
        }
    }

    // Closes the connect socket and causes the thread to finish.
    public void cancel() {
        try {
            mmServerSocket.close();
        } catch (IOException e) {
            Log.e(TAG, "Could not close the connect socket", e);
        }
    }
}

この例では、受信接続が 1 つだけ必要なため、接続が受け入れられ BluetoothSocket が取得されるとすぐに、アプリは取得した BluetoothSocket を別のスレッドに渡して BluetoothServerSocket を閉じてループから抜けます。

accept()BluetoothSocket を返す場合、ソケットはすでに接続されています。したがって、クライアント側から行うように、connect() を呼び出さないでください。

アプリ固有の manageMyConnectedSocket() メソッドは、データ転送用のスレッドを開始するように設計されています。これについては、接続を管理するのセクションをご覧ください。

通常は、受信接続のリッスンが完了したらすぐに BluetoothServerSocket を閉じる必要があります。この例では、BluetoothSocket が取得されるとすぐに close() が呼び出されます。サーバー ソケットでのリッスンを停止する必要がある場合にプライベート BluetoothSocket をクローズできるパブリック メソッドをスレッド内に用意することもできます。

クライアントとして接続

オープン サーバー ソケットで接続を受け入れているリモート デバイスとの接続を開始するには、まず、リモート デバイスを表す BluetoothDevice オブジェクトを取得する必要があります。BluetoothDevice の作成方法については、デバイスの検索をご覧ください。次に、BluetoothDevice を使用して BluetoothSocket を取得し、接続を開始する必要があります。

基本的な手順は次のとおりです。

  1. BluetoothDevice を使用して、createRfcommSocketToServiceRecord(UUID) を呼び出して BluetoothSocket を取得します。

    このメソッドは、クライアントが BluetoothDevice に接続できるように BluetoothSocket オブジェクトを初期化します。ここで渡される UUID は、サーバー デバイスが listenUsingRfcommWithServiceRecord(String, UUID) を呼び出して BluetoothServerSocket を開く際に使用する UUID と一致する必要があります。一致する UUID を使用するには、UUID 文字列をアプリケーションにハードコードし、サーバーコードとクライアントコードの両方から参照します。

  2. connect() を呼び出して接続を開始します。なお、このメソッドはブロッキング呼び出しです。

    クライアントがこのメソッドを呼び出すと、システムは SDP 検索を実行して、一致する UUID を持つリモート デバイスを見つけます。ルックアップが成功し、リモート デバイスが接続を受け入れると、接続中に使用する RFCOMM チャネルが共有され、connect() メソッドが戻ります。接続が失敗した場合、または connect() メソッドが(約 12 秒後に)タイムアウトした場合、メソッドは IOException をスローします。

    connect() はブロッキング呼び出しであるため、この接続プロシージャは、常にメイン アクティビティ(UI)スレッドとは別のスレッドで実行する必要があります。

    注: connect() を呼び出す前に、必ず cancelDiscovery() を呼び出して、デバイスがデバイスの検出を実行していないことを確認する必要があります。検出が進行中の場合、接続の試行は非常に遅く、失敗する可能性が高くなります。

Bluetooth 接続を開始するクライアント スレッドの基本的な例を次に示します。

Kotlin

private inner class ConnectThread(device: BluetoothDevice) : Thread() {

    private val mmSocket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) {
        device.createRfcommSocketToServiceRecord(MY_UUID)
    }

    public override fun run() {
        // Cancel discovery because it otherwise slows down the connection.
        bluetoothAdapter?.cancelDiscovery()

        mmSocket?.use { socket ->
            // Connect to the remote device through the socket. This call blocks
            // until it succeeds or throws an exception.
            socket.connect()

            // The connection attempt succeeded. Perform work associated with
            // the connection in a separate thread.
            manageMyConnectedSocket(socket)
        }
    }

    // Closes the client socket and causes the thread to finish.
    fun cancel() {
        try {
            mmSocket?.close()
        } catch (e: IOException) {
            Log.e(TAG, "Could not close the client socket", e)
        }
    }
}

Java

private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;

    public ConnectThread(BluetoothDevice device) {
        // Use a temporary object that is later assigned to mmSocket
        // because mmSocket is final.
        BluetoothSocket tmp = null;
        mmDevice = device;

        try {
            // Get a BluetoothSocket to connect with the given BluetoothDevice.
            // MY_UUID is the app's UUID string, also used in the server code.
            tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
        } catch (IOException e) {
            Log.e(TAG, "Socket's create() method failed", e);
        }
        mmSocket = tmp;
    }

    public void run() {
        // Cancel discovery because it otherwise slows down the connection.
        bluetoothAdapter.cancelDiscovery();

        try {
            // Connect to the remote device through the socket. This call blocks
            // until it succeeds or throws an exception.
            mmSocket.connect();
        } catch (IOException connectException) {
            // Unable to connect; close the socket and return.
            try {
                mmSocket.close();
            } catch (IOException closeException) {
                Log.e(TAG, "Could not close the client socket", closeException);
            }
            return;
        }

        // The connection attempt succeeded. Perform work associated with
        // the connection in a separate thread.
        manageMyConnectedSocket(mmSocket);
    }

    // Closes the client socket and causes the thread to finish.
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) {
            Log.e(TAG, "Could not close the client socket", e);
        }
    }
}

このスニペットでは、接続の試行が行われる前に cancelDiscovery() が呼び出されています。常に connect() の前に cancelDiscovery() を呼び出す必要があります。これは、現在デバイスの検出が進行中かどうかにかかわらず、cancelDiscovery() は成功するためです。ただし、デバイスの検出が進行中かどうかをアプリで判断する必要がある場合は、isDiscovering() を使用して確認できます。

アプリ固有の manageMyConnectedSocket() メソッドは、データ転送用のスレッドを開始するように設計されています。これについては、接続の管理に関するセクションをご覧ください。

BluetoothSocket による作業が完了したら、必ず close() を呼び出します。これを行うと、接続されたソケットがすぐに閉じられ、関連するすべての内部リソースが解放されます。

接続を管理する

複数のデバイスが正常に接続されると、各デバイスに BluetoothSocket が接続されます。デバイス間で情報を共有できるので、ここからがもっと楽しくなります。BluetoothSocket を使用してデータを転送する一般的な手順は次のとおりです。

  1. getInputStream()getOutputStream() を使用して、それぞれソケットを介した伝送を処理する InputStreamOutputStream を取得します。
  2. read(byte[])write(byte[]) を使用して、ストリームに対するデータの読み取りと書き込みを行う。

もちろん、検討すべき実装の詳細もあります。特に、ストリームからの読み取りとストリームへの書き込みには、専用のスレッドを使用する必要があります。read(byte[]) メソッドと write(byte[]) メソッドの両方が呼び出しをブロックしているため、このことは重要です。read(byte[]) メソッドは、ストリームから読み取るものが見つかるまでブロックします。通常、write(byte[]) メソッドはブロックされませんが、リモート デバイスによる read(byte[]) の呼び出し速度が十分でなく、その結果中間バッファがいっぱいになった場合はフロー制御をブロックできます。そのため、スレッドのメインループは InputStream からの読み取り専用にする必要があります。スレッド内の個別のパブリック メソッドを使用して、OutputStream への書き込みを開始できます。

Bluetooth 経由で接続された 2 つのデバイス間でデータを転送する方法の例を次に示します。

Kotlin

private const val TAG = "MY_APP_DEBUG_TAG"

// Defines several constants used when transmitting messages between the
// service and the UI.
const val MESSAGE_READ: Int = 0
const val MESSAGE_WRITE: Int = 1
const val MESSAGE_TOAST: Int = 2
// ... (Add other message types here as needed.)

class MyBluetoothService(
        // handler that gets info from Bluetooth service
        private val handler: Handler) {

    private inner class ConnectedThread(private val mmSocket: BluetoothSocket) : Thread() {

        private val mmInStream: InputStream = mmSocket.inputStream
        private val mmOutStream: OutputStream = mmSocket.outputStream
        private val mmBuffer: ByteArray = ByteArray(1024) // mmBuffer store for the stream

        override fun run() {
            var numBytes: Int // bytes returned from read()

            // Keep listening to the InputStream until an exception occurs.
            while (true) {
                // Read from the InputStream.
                numBytes = try {
                    mmInStream.read(mmBuffer)
                } catch (e: IOException) {
                    Log.d(TAG, "Input stream was disconnected", e)
                    break
                }

                // Send the obtained bytes to the UI activity.
                val readMsg = handler.obtainMessage(
                        MESSAGE_READ, numBytes, -1,
                        mmBuffer)
                readMsg.sendToTarget()
            }
        }

        // Call this from the main activity to send data to the remote device.
        fun write(bytes: ByteArray) {
            try {
                mmOutStream.write(bytes)
            } catch (e: IOException) {
                Log.e(TAG, "Error occurred when sending data", e)

                // Send a failure message back to the activity.
                val writeErrorMsg = handler.obtainMessage(MESSAGE_TOAST)
                val bundle = Bundle().apply {
                    putString("toast", "Couldn't send data to the other device")
                }
                writeErrorMsg.data = bundle
                handler.sendMessage(writeErrorMsg)
                return
            }

            // Share the sent message with the UI activity.
            val writtenMsg = handler.obtainMessage(
                    MESSAGE_WRITE, -1, -1, bytes)
            writtenMsg.sendToTarget()
        }

        // Call this method from the main activity to shut down the connection.
        fun cancel() {
            try {
                mmSocket.close()
            } catch (e: IOException) {
                Log.e(TAG, "Could not close the connect socket", e)
            }
        }
    }
}

Java

public class MyBluetoothService {
    private static final String TAG = "MY_APP_DEBUG_TAG";
    private Handler handler; // handler that gets info from Bluetooth service

    // Defines several constants used when transmitting messages between the
    // service and the UI.
    private interface MessageConstants {
        public static final int MESSAGE_READ = 0;
        public static final int MESSAGE_WRITE = 1;
        public static final int MESSAGE_TOAST = 2;

        // ... (Add other message types here as needed.)
    }

    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;
        private byte[] mmBuffer; // mmBuffer store for the stream

        public ConnectedThread(BluetoothSocket socket) {
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;

            // Get the input and output streams; using temp objects because
            // member streams are final.
            try {
                tmpIn = socket.getInputStream();
            } catch (IOException e) {
                Log.e(TAG, "Error occurred when creating input stream", e);
            }
            try {
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "Error occurred when creating output stream", e);
            }

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        public void run() {
            mmBuffer = new byte[1024];
            int numBytes; // bytes returned from read()

            // Keep listening to the InputStream until an exception occurs.
            while (true) {
                try {
                    // Read from the InputStream.
                    numBytes = mmInStream.read(mmBuffer);
                    // Send the obtained bytes to the UI activity.
                    Message readMsg = handler.obtainMessage(
                            MessageConstants.MESSAGE_READ, numBytes, -1,
                            mmBuffer);
                    readMsg.sendToTarget();
                } catch (IOException e) {
                    Log.d(TAG, "Input stream was disconnected", e);
                    break;
                }
            }
        }

        // Call this from the main activity to send data to the remote device.
        public void write(byte[] bytes) {
            try {
                mmOutStream.write(bytes);

                // Share the sent message with the UI activity.
                Message writtenMsg = handler.obtainMessage(
                        MessageConstants.MESSAGE_WRITE, -1, -1, bytes);
                writtenMsg.sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "Error occurred when sending data", e);

                // Send a failure message back to the activity.
                Message writeErrorMsg =
                        handler.obtainMessage(MessageConstants.MESSAGE_TOAST);
                Bundle bundle = new Bundle();
                bundle.putString("toast",
                        "Couldn't send data to the other device");
                writeErrorMsg.setData(bundle);
                handler.sendMessage(writeErrorMsg);
            }
        }

        // Call this method from the main activity to shut down the connection.
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "Could not close the connect socket", e);
            }
        }
    }
}

コンストラクタが必要なストリームを取得した後、スレッドは InputStream からデータが来るのを待ちます。read(byte[]) がストリームのデータを返すと、データは親クラスのメンバー Handler を使用してメイン アクティビティに送信されます。スレッドは、InputStream からさらにバイトが読み取られるのを待機します。

データの送信は、メイン アクティビティからスレッドの write() メソッドを呼び出し、送信するバイトを渡すだけで簡単に行うことができます。このメソッドは write(byte[]) を呼び出して、リモート デバイスにデータを送信します。write(byte[]) の呼び出し時に IOException がスローされると、スレッドはメイン アクティビティにトーストを送信し、指定されたバイトを他のデバイス(接続済み)に送信できなかったことをユーザーに伝えます。

スレッドの cancel() メソッドを使用すると、BluetoothSocket を閉じることで、いつでも接続を終了できます。このメソッドは、Bluetooth 接続の使用が終了したら常に呼び出す必要があります。

Bluetooth API の使用方法について詳しくは、Bluetooth Chat サンプルアプリをご覧ください。

キークラスとインターフェース

すべての Bluetooth API は、android.bluetooth パッケージに含まれています。Bluetooth 接続の作成に必要なクラスとインターフェースの概要は次のとおりです。

BluetoothAdapter
ローカル Bluetooth アダプター(Bluetooth 無線通信)を表します。BluetoothAdapter は、すべての Bluetooth インタラクションのエントリ ポイントです。これを使用して、他の Bluetooth デバイスの検出、ボンディングされた(ペア設定された)デバイスのリストをクエリし、既知の MAC アドレスを使用して BluetoothDevice をインスタンス化し、他のデバイスからの通信をリッスンする BluetoothServerSocket を作成できます。
BluetoothDevice
リモートの Bluetooth デバイスを表します。これを使用して、BluetoothSocket を介してリモート デバイスとの接続をリクエストするか、デバイスに関する情報(名前、アドレス、クラス、ボンディング状態など)をクエリします。
BluetoothSocket
Bluetooth ソケットのインターフェースを表します(TCP Socket に類似)。この接続ポイントにより、アプリは InputStreamOutputStream を使用して別の Bluetooth デバイスとデータを交換できます。
BluetoothServerSocket
受信リクエストをリッスンするオープンなサーバー ソケットを表します(TCP ServerSocket と同様)。2 つの Android デバイスを接続するには、一方のデバイスがこのクラスを使用してサーバー ソケットを開く必要があります。リモートの Bluetooth デバイスがこのデバイスへの接続リクエストを行うと、デバイスは接続を受け入れ、接続された BluetoothSocket を返します。
BluetoothClass
Bluetooth デバイスの一般的な特性と機能について説明します。デバイスのクラスとサービスを定義する読み取り専用のプロパティ セットです。この情報はデバイスの種類に関する有用なヒントを提供しますが、このクラスの属性は、デバイスがサポートするすべての Bluetooth プロファイルとサービスについて説明しているとは限りません。
BluetoothProfile
Bluetooth プロファイルを表すインターフェース。Bluetooth プロファイルは、デバイス間で Bluetooth ベースの通信を行うためのワイヤレス インターフェース仕様です。その一例が、Hands-Free プロファイルです。プロファイルについて詳しくは、プロファイルの操作をご覧ください。
BluetoothHeadset
スマートフォンで使用する Bluetooth ヘッドセットをサポートします。これには、Bluetooth ヘッドセット プロファイルと Hands-Free(v1.5)プロファイルの両方が含まれます。
BluetoothA2dp
: Advanced Audio Distribution Profile(A2DP)を使用して、Bluetooth 接続でデバイス間で高品質のオーディオをストリーミングする方法を定義します。
BluetoothHealth
: Bluetooth サービスを制御する Health Device Profile プロキシを表します。
BluetoothHealthCallback
BluetoothHealth コールバックの実装に使用する抽象クラス。このクラスを拡張して、アプリの登録状態と Bluetooth チャネル状態の変化に関する最新情報を受け取るためのコールバック メソッドを実装する必要があります。
BluetoothHealthAppConfiguration
Bluetooth Health のサードパーティ アプリがリモート Bluetooth Health デバイスと通信するために登録するアプリ構成を表します。
BluetoothProfile.ServiceListener
BluetoothProfile プロセス間通信(IPC)クライアントを、特定のプロファイルを実行する内部サービスに接続したとき、または接続解除したときに通知するインターフェース。