블루투스 기기 찾기

BluetoothAdapter를 사용하면 기기 검색을 통해 또는 페어링된 기기 목록을 쿼리하여 원격 블루투스 기기를 찾을 수 있습니다.

블루투스 기기를 찾으려면 적절한 블루투스 권한이 있고 블루투스에 앱을 설정했는지 확인하세요.

기기 검색은 주변 지역에서 블루투스 지원 기기를 검색하고 각 기기에 관한 몇 가지 정보를 요청하는 스캔 절차입니다. 이 프로세스를 검색, 쿼리 또는 스캔이라고도 합니다. 근처 블루투스 기기는 현재 검색 가능 상태에서 정보 요청을 수락하는 경우에만 검색 요청에 응답합니다. 기기를 검색할 수 있는 경우 기기 이름, 클래스, 고유 MAC 주소와 같은 일부 정보를 공유하여 검색 요청에 응답합니다. 그러면 이 정보를 사용하여 검색 프로세스를 실행하는 기기가 검색된 기기에 대한 연결을 시작할 수 있습니다.

검색 가능한 기기는 사용자의 위치에 관한 정보를 공개할 수 있으므로 기기 검색 프로세스에는 위치 액세스가 필요합니다. 앱이 Android 8.0 (API 수준 26) 이상을 실행하는 기기에서 사용되는 경우 Companion Device Manager API를 대신 사용하는 것이 좋습니다. 이 API는 앱을 대신하여 기기 검색을 실행하므로 앱에서 위치 정보 액세스 권한을 요청할 필요가 없습니다.

원격 기기와 처음 연결되면 페어링 요청이 사용자에게 자동으로 표시됩니다. 기기가 페어링되면 기기 이름, 클래스, MAC 주소와 같은 기기에 관한 기본 정보가 저장되며 블루투스 API를 사용하여 읽을 수 있습니다. 원격 기기의 알려진 MAC 주소를 사용하면 기기가 아직 범위 내에 있다고 가정할 때 언제든지 검색을 실행하지 않고도 기기와 연결을 시작할 수 있습니다.

페어링된 것과 연결된 것에는 차이점이 있습니다.

  • 페어링은 두 기기가 서로의 존재를 인식하고, 인증에 사용할 수 있는 공유 링크 키를 보유하며, 서로 암호화된 연결을 설정할 수 있다는 것을 의미합니다.
  • 연결됨은 기기가 현재 RFCOMM 채널을 공유하고 서로 데이터를 전송할 수 있다는 의미입니다. 현재 블루투스 API에서는 RFCOMM 연결을 설정하기 전에 기기를 페어링해야 합니다. 블루투스 API로 암호화된 연결을 시작하면 페어링이 자동으로 실행됩니다.

다음 섹션에서는 페어링된 기기를 찾는 방법과 기기 검색을 사용하여 새 기기를 검색하는 방법을 설명합니다.

페어링된 기기 쿼리

기기 검색을 실행하기 전에 페어링된 기기 세트를 쿼리하여 원하는 기기가 이미 알려져 있는지 확인하는 것이 좋습니다. getBondedDevices()를 호출하면 됩니다. 페어링된 기기를 나타내는 BluetoothDevice 객체 집합을 반환합니다. 예를 들어 다음 코드 스니펫과 같이 페어링된 모든 기기를 쿼리하고 각 기기의 이름과 MAC 주소를 가져올 수 있습니다.

Kotlin

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

자바

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
   }
}

블루투스 기기와의 연결을 시작하려면 연결된 BluetoothDevice 객체에서 MAC 주소만 있으면 되며, 이 주소는 getAddress()를 호출하여 가져옵니다. 블루투스 기기 연결에서 연결 생성에 관해 자세히 알아보세요.

기기 검색

기기 검색을 시작하려면 startDiscovery()를 호출합니다. 이 프로세스는 비동기식이며 검색이 성공적으로 시작되었는지 나타내는 불리언 값을 반환합니다. 검색 프로세스에는 일반적으로 약 12초의 쿼리 스캔이 포함되며, 그다음에는 발견된 각 기기의 페이지를 스캔하여 블루투스 이름을 가져옵니다.

감지된 각 기기에 관한 정보를 수신하려면 앱에서 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)
}

자바

@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);
}

블루투스 기기와의 연결을 시작하려면 BluetoothDevice에서 getAddress()를 호출하여 연결된 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)

자바

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
기기가 검색 가능 모드가 아니며 연결을 수신할 수 없습니다.

원격 기기에 대한 연결을 시작하는 경우 기기 검색 가능성을 사용 설정할 필요가 없습니다. 검색 가능성을 사용 설정하는 것은 앱이 수신 연결을 허용하는 서버 소켓을 호스팅하려는 경우에만 필요합니다. 원격 기기는 다른 기기에 대한 연결을 시작하기 전에 다른 기기를 검색할 수 있어야 하기 때문입니다.