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 を追加することで、最大 5 分までの異なる時間を定義できます。

次のコード スニペットは、デバイスを 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
デバイスが検出可能モードになっていないため、接続を受信できません。

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