Bluetooth デバイスを探す

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

Bluetooth デバイスを検出する前に、適切な Bluetooth の権限があることを確認し、Bluetooth 用にアプリをセットアップします。

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

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

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

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

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

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

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

デバイス検出を実行する前に、ペア設定されたデバイスのセットをクエリして、目的のデバイスがすでに認識されているかどうかを確認することをおすすめします。そのためには、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 オブジェクトから必要なのは MAC アドレスだけです。MAC アドレスは、getAddress() を呼び出して取得します。接続の作成方法については、Bluetooth デバイスの接続をご覧ください。

デバイスの検出

デバイスの検出を開始するには、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 デバイスとの接続を開始するには、BluetoothDevicegetAddress() を呼び出して、関連付けられている MAC アドレスを取得します。

検出の許可を有効にする

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

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

Kotlin

val requestCode = 1;
val discoverableIntent: Intent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE).apply {
   putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300)
}
startActivityForResult(discoverableIntent, requestCode)

Java

int requestCode = 1;
Intent discoverableIntent =
       new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivityForResult(discoverableIntent, requestCode);


図 2: 検出の許可を有効にするダイアログ。

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

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

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

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