블루투스 권한

앱에서 블루투스 기능을 사용하려면 여러 권한을 선언해야 합니다. 또한 앱에 기본 블루투스 지원 또는 저전력 블루투스 (BLE) 지원이 필요한지 여부도 지정해야 합니다. 앱에 블루투스 기본 또는 BLE가 필요하지 않지만 이러한 기술을 여전히 활용할 수 있는 경우 런타임에 사용 가능 여부를 확인할 수 있습니다.

권한 선언

앱에서 선언하는 권한 집합은 앱의 타겟 SDK 버전에 따라 다릅니다.

Android 12 이상 타겟팅

참고: Android 8.0 (API 수준 26) 이상에서는 부속 기기 관리자 (CDM)에서 이 섹션에 설명된 권한과 비교할 때 부속 기기에 연결하는 더 간소화된 방법을 제공합니다. CDM 시스템은 앱을 대신하여 페어링 UI를 제공하며 위치 정보 액세스 권한이 필요하지 않습니다.

페어링 및 연결 환경을 더 세부적으로 제어하려면 이 섹션에 설명된 권한을 사용하세요.

블루투스 권한 대화상자
시스템 권한 대화상자. 사용자에게 근처 기기를 검색, 광고하고 연결할 수 있는 권한을 앱에 부여하도록 요청합니다.

앱이 Android 12 (API 수준 31) 이상을 타겟팅한다면 앱의 매니페스트 파일에서 다음 권한을 선언합니다.

  1. 앱이 BLE 주변기기와 같은 블루투스 기기를 검색하는 경우 BLUETOOTH_SCAN 권한을 선언합니다.
  2. 앱이 현재 기기를 다른 블루투스 기기에서 검색 가능하도록 설정하는 경우 BLUETOOTH_ADVERTISE 권한을 선언합니다.
  3. 앱이 이미 페어링된 블루투스 기기와 통신한다면 BLUETOOTH_CONNECT 권한을 선언합니다.
  4. 기존 블루투스 관련 권한 선언의 경우 android:maxSdkVersion30으로 설정합니다. 이 앱 호환성 단계를 통해 시스템은 Android 12 이상을 실행하는 기기에 설치할 때 필요한 블루투스 권한만 앱에 부여할 수 있습니다.
  5. 앱에서 블루투스 검색 결과를 사용하여 실제 위치를 얻는 경우 ACCESS_FINE_LOCATION 권한을 선언합니다. 아니면 앱이 실제 위치를 파생하지 않는다고 강력하게 어설션할 수 있습니다.

BLUETOOTH_ADVERTISE, BLUETOOTH_CONNECT, BLUETOOTH_SCAN 권한은 런타임 권한입니다. 따라서 명시적으로 앱에서 사용자 승인을 요청해야 블루투스 기기를 찾거나 다른 기기에서 기기를 검색 가능하도록 설정하거나 이미 페어링된 블루투스 기기와 통신할 수 있습니다. 앱이 이러한 권한 중 하나 이상을 요청하면 시스템은 그림 1과 같이 앱이 근처 기기에 액세스하도록 허용하라는 메시지를 사용자에게 표시합니다.

다음 코드 스니펫은 앱이 Android 12 이상을 타겟팅하는 경우 앱에서 블루투스 관련 권한을 선언하는 방법을 보여줍니다.

<manifest>
    <!-- Request legacy Bluetooth permissions on older devices. -->
    <uses-permission android:name="android.permission.BLUETOOTH"
                     android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
                     android:maxSdkVersion="30" />

    <!-- Needed only if your app looks for Bluetooth devices.
         If your app doesn't use Bluetooth scan results to derive physical
         location information, you can
         <a href="#assert-never-for-location">strongly assert that your app
         doesn't derive physical location</a>. -->
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

    <!-- Needed only if your app makes the device discoverable to Bluetooth
         devices. -->
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />

    <!-- Needed only if your app communicates with already-paired Bluetooth
         devices. -->
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

    <!-- Needed only if your app uses Bluetooth scan results to derive physical location. -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    ...
</manifest>

앱이 물리적 위치를 얻지 않는다고 강력하게 어설션합니다

앱이 블루투스 검색 결과를 사용하여 실제 위치를 파생하지 않으면 앱이 실제 위치를 파생하는 데 블루투스 권한을 사용하지 않는다는 강력한 어설션을 만들 수 있습니다. 그러려면 다음 단계를 완료하세요.

  1. android:usesPermissionFlags 속성을 BLUETOOTH_SCAN 권한 선언에 추가하고 이 속성 값을 neverForLocation으로 설정합니다.

  2. 위치가 앱에 달리 필요하지 않으면 앱의 매니페스트에서 ACCESS_FINE_LOCATION 권한을 삭제합니다.

다음 코드 스니펫은 앱의 매니페스트 파일을 업데이트하는 방법을 보여줍니다.

<manifest>
    <!-- Include "neverForLocation" only if you can strongly assert that
         your app never derives physical location from Bluetooth scan results. -->
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"
                     android:usesPermissionFlags="neverForLocation" />

    <!-- Not needed if you can strongly assert that your app never derives
         physical location from Bluetooth scan results and doesn't need location
         access for any other purpose. -->
    <strike><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /></strike>
    ...
</manifest>

Android 11 이하 타겟팅

앱이 Android 11 (API 수준 30) 이하를 타겟팅한다면 앱의 매니페스트 파일에서 다음 권한을 선언합니다.

  • BLUETOOTH는 연결 요청, 연결 수락, 데이터 전송과 같은 기본 블루투스 또는 BLE 통신을 실행하는 데 필요합니다.
  • ACCESS_FINE_LOCATION가 필요한 이유는 Android 11 이하에서는 사용자 위치에 관한 정보를 수집하는 데 블루투스 스캔이 사용될 수 있기 때문입니다.

위치 정보 액세스 권한은 런타임 권한이므로 매니페스트에서 선언하는 것과 함께 런타임에 이러한 권한을 요청해야 합니다.

로컬 블루투스 기기 찾기

앱에서 기기 검색을 시작하거나 블루투스 설정을 조작하려면 BLUETOOTH_ADMIN 권한을 선언해야 합니다. 대부분의 앱에는 로컬 블루투스 기기를 검색하는 기능에만 이 권한이 필요합니다. 앱이 사용자 요청 시 블루투스 설정을 수정하는 '전원 관리자'가 아닌 경우 이 권한이 부여하는 다른 기능을 사용하지 마세요. 앱 매니페스트 파일에서 이 권한을 선언합니다. 예를 들면 다음과 같습니다.

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

앱이 서비스를 지원하고 Android 10 (API 수준 29) 또는 Android 11에서 실행할 수 있는 경우 ACCESS_BACKGROUND_LOCATION 권한도 선언하여 블루투스 기기를 검색해야 합니다. 이 요구사항에 관한 자세한 내용은 백그라운드에서 위치 액세스를 참고하세요.

다음 코드 스니펫은 ACCESS_BACKGROUND_LOCATION 권한을 선언하는 방법을 보여줍니다.

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

앱 권한 선언에 관한 자세한 내용은 <uses-permission> 참조를 확인하세요.

블루투스 기능 사용 지정

블루투스가 앱에서 중요한 부분이라면 매니페스트 파일에 이 요구사항을 나타내는 플래그를 추가할 수 있습니다. <uses-feature> 요소를 사용하면 앱에서 사용하는 하드웨어의 유형과 필수 여부를 지정할 수 있습니다.

이 예에서는 앱에 블루투스 클래식이 필요하다고 표시하는 방법을 보여줍니다.

<uses-feature android:name="android.hardware.bluetooth" android:required="true"/>

앱이 저전력 블루투스를 사용하는 경우 다음을 사용할 수 있습니다.

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

앱에 해당 기능이 필요하다고 명시하면 Google Play 스토어는 이러한 기능이 없는 기기의 사용자에게 앱을 숨깁니다. 따라서 이 기능이 없으면 앱이 작동할 수 없는 경우에만 필수 속성을 true로 설정해야 합니다.

런타임 시 기능 사용 가능 여부 확인

블루투스 클래식 또는 BLE를 지원하지 않는 기기에서 앱을 사용할 수 있도록 하려면 앱의 매니페스트에 <uses-feature> 요소를 계속 포함하되 required="false"를 설정해야 합니다. 그런 다음 런타임 시 PackageManager.hasSystemFeature()을 사용하여 기능 사용 가능 여부를 확인할 수 있습니다.

Kotlin

// Check to see if the Bluetooth classic feature is available.
val bluetoothAvailable = packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)

// Check to see if the BLE feature is available.
val bluetoothLEAvailable = packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)

Java

// Use this check to determine whether Bluetooth classic is supported on the device.
// Then you can selectively disable BLE-related features.
boolean bluetoothAvailable = getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);

// Use this check to determine whether BLE is supported on the device. Then
// you can selectively disable BLE-related features.
boolean bluetoothLEAvailable = getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);