Bluetooth デバイスを探す

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

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

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

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

リモート デバイスと初めて接続すると、ペア設定リクエストが自動的にユーザーに表示されます。デバイスがペア設定されると、そのデバイスに関する基本情報(デバイス名、クラス、MAC アドレスなど)が保存され、Bluetooth API を使用して読み取ることができます。リモート デバイスの既知の 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
デバイスが検出可能モードになっておらず、接続を受信できません。

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