Wi-Fi 위치: RTT를 사용한 범위 설정

Wi-Fi RTT(Round-Trip-Time) API가 제공하는 Wi-Fi 위치 기능을 사용하여 주변의 RTT 지원 Wi-Fi 액세스 포인트 및 동종 앱 Wi-Fi Aware 기기와의 거리를 측정할 수 있습니다.

기기에서 3개 이상의 액세스 지점에 대한 거리를 측정하는 경우, 다변측정(MLAT) 알고리즘을 사용하여 해당 측정에 가장 적합한 기기 위치를 측정할 수 있습니다. 일반적으로 결과의 정확도는 1~2m 이내입니다.

이 정도의 정확도에서는 세밀한 위치 기반 서비스를 개발할 수 있습니다. 예를 들어 실내 탐색, 명확한 음성 제어(예: '조명 켜기'), 위치 기반 정보(예: '이 제품 관한 특별 이벤트가 있는지 여부')가 있습니다.

요청하는 기기는 Wi-Fi RTT와의 거리를 측정하기 위해 액세스 포인트에 연결하지 않아도 됩니다. 개인정보 보호를 위해 요청하는 기기에서만 액세스 포인트에 대한 거리를 측정할 수 있으며, 액세스 포인트에는 이 정보가 없습니다. Wi-Fi RTT 작업은 포그라운드 앱에 관한 제한이 없지만 백그라운드 앱 사용이 제한됩니다.

Wi-Fi RTT 및 관련 FTM (Fine-Time-Measurement) 기능은 IEEE 802.11-2016 표준에서 지정합니다. Wi-Fi RTT는 FTM에서 제공하는 정확한 시간 측정이 필요합니다. 패킷이 두 기기를 왕복하는 시간을 측정하고 그 시간에 광속을 곱하여 두 기기 간의 거리를 계산하기 때문입니다.

Android 버전에 따른 구현의 차이

Wi-Fi RTT는 Android 9(API 수준 28)에 도입되었습니다. 이 프로토콜을 사용하여 Android 9를 실행하는 기기로 다변 측정하고 기기의 위치를 알아낼 경우, 앱에서 사전에 결정된 액세스 포인트(AP) 데이터에 액세스해야 합니다. 개발자는 이 데이터를 저장하고 검색하는 방법을 결정합니다.

Android 10(API 수준 29) 이상을 실행하는 기기에서 AP 위치 데이터는 ResponderLocation 객체로 표시될 수 있으며 여기에는 위도, 경도, 고도가 포함됩니다. 위치 구성 정보/위치 시민 보고(LCI/LCR 데이터)를 지원하는 Wi-Fi RTT AP의 경우, 범위 설정 프로세스에서 프로토콜이 ResponderLocation 객체를 반환합니다.

이 기능을 사용하면 앱이 미리 정보를 저장할 필요 없이 AP를 쿼리하여 위치 정보를 직접 요청할 수 있습니다. 그러므로 사용자가 새로운 건물에 들어갔을 때처럼 AP의 위치에 관해 몰랐더라도 앱이 AP를 찾고 그 위치를 알아낼 수 있습니다.

요구사항

  • 범위 설정을 요청하는 기기의 하드웨어는 802.11-2016 FTM 표준을 구현해야 합니다.
  • 범위 설정 요청을 하는 기기는 Android 9(API 수준 28) 이상을 실행해야 합니다.
  • 범위 설정 요청을 하는 기기는 위치 서비스가 활성화되어 있고 Wi-Fi 검색을 켜야 합니다.(Settings > Location)
  • 범위 설정 요청을 하는 앱이 Android 13 (API 수준 33) 이상을 타겟팅한다면 앱에 NEARBY_WIFI_DEVICES 권한이 있어야 합니다. 이러한 앱이 이전 버전의 Android를 타겟팅하는 경우에는 대신 ACCESS_FINE_LOCATION 권한이 있어야 합니다.
  • 앱은 표시되는 동안 또는 포그라운드 서비스 상태에서 액세스 포인트 범위를 쿼리해야 합니다. 앱은 백그라운드에서 위치 정보에 액세스할 수 없습니다.
  • 액세스 포인트는 IEEE 802.11-2016 FTM 표준을 구현해야 합니다.

설정

앱이 Wi-Fi RTT를 사용하도록 설정하려면 다음 단계를 따라 하세요.

1. 권한 요청

앱 매니페스트에서 다음 권한을 요청합니다.

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!-- If your app targets Android 13 (API level 33)
     or higher, you must declare the NEARBY_WIFI_DEVICES permission. -->
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
                 <!-- If your app derives location information from Wi-Fi APIs,
                      don't include the "usesPermissionFlags" attribute. -->
                 android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
                 <!-- If any feature in your app relies on precise location
                      information, don't include the "maxSdkVersion"
                      attribute. -->
                 android:maxSdkVersion="32" />

NEARBY_WIFI_DEVICESACCESS_FINE_LOCATION 권한은 위험한 권한이므로 사용자가 RTT 스캔 작업을 실행하려고 할 때마다 런타임에 요청해야 합니다. 아직 권한이 부여되지 않았다면 앱에서 사용자 권한을 요청해야 합니다. 런타임 권한에 관한 자세한 내용은 앱 권한 요청을 참고하세요.

2. 기기가 Wi-Fi RTT를 지원하는지 확인

기기가 Wi-Fi RTT를 지원하는지 확인하려면 다음과 같은 PackageManager API를 사용하세요.

Kotlin

context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)

Java

context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT);

3. Wi-Fi RTT를 사용할 수 있는지 확인

Wi-Fi RTT가 기기에 있을 수는 있으나 사용자가 Wi-Fi를 비활성화했기 때문에 사용하지 못하는 상태일 수 있습니다. 하드웨어와 펌웨어 기능에 따라 SoftAP 또는 테더링을 사용하고 있을 경우 일부 기기는 Wi-Fi RTT를 지원하지 않을 수 있습니다. Wi-Fi RTT를 현재 사용할 수 있는지 확인하려면 isAvailable()을 호출하세요.

Wi-Fi RTT의 사용 가능 여부는 언제든지 변경될 수 있습니다. 앱은 BroadcastReceiver를 등록하여 사용 가능 여부가 변경될 때 전송되는 ACTION_WIFI_RTT_STATE_CHANGED를 수신할 수 있습니다. 앱이 이 브로드캐스트 인텐트를 수신하면 사용 가능 여부의 현재 상태를 확인하고 그에 맞게 동작을 수정해야 합니다.

예:

Kotlin

val filter = IntentFilter(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED)
val myReceiver = object: BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        if (wifiRttManager.isAvailable) {
            …
        } else {
            …
        }
    }
}
context.registerReceiver(myReceiver, filter)

Java

IntentFilter filter =
    new IntentFilter(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED);
BroadcastReceiver myReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (wifiRttManager.isAvailable()) {
            …
        } else {
            …
        }
    }
};
context.registerReceiver(myReceiver, filter);

자세한 내용은 브로드캐스트를 참조하세요.

범위 설정 요청 만들기

범위를 요청하는 AP 또는 Wi-Fi Aware 동종 앱의 목록을 지정하여 범위 설정 요청(RangingRequest)을 생성합니다. 한 번의 범위 설정 요청에서 여러 개의 액세스 포인트 또는 Wi-Fi Aware 동종 앱을 지정할 수 있습니다. 이 경우, 모든 기기와의 거리를 측정해서 반환합니다.

예를 들어 요청에서 addAccessPoint() 메서드를 사용하여 거리를 측정할 액세스 포인트를 지정할 수 있습니다.

Kotlin

val req: RangingRequest = RangingRequest.Builder().run {
    addAccessPoint(ap1ScanResult)
    addAccessPoint(ap2ScanResult)
    build()
}

Java

RangingRequest.Builder builder = new RangingRequest.Builder();
builder.addAccessPoint(ap1ScanResult);
builder.addAccessPoint(ap2ScanResult);

RangingRequest req = builder.build();

액세스 포인트는 ScanResult 객체로 식별되고, 이는 WifiManager.getScanResults()를 호출하면 얻을 수 있습니다. addAccessPoints(List)를 사용하여 여러 액세스 포인트를 일괄 추가할 수 있습니다.

마찬가지로 범위 설정 요청은 MAC 주소나 PeerHandle, addWifiAwarePeer(MacAddress peer)addWifiAwarePeer(PeerHandle peer) 메서드를 각각 사용하여 Wi-Fi Aware 동종 앱을 추가할 수 있습니다. Wi-Fi Aware 동종 앱 검색에 관한 자세한 내용은 Wi-Fi Aware 도움말을 참조하세요.

범위 설정 요청

앱은 WifiRttManager.startRanging() 메서드를 사용하고 다음을 사용하여 범위 설정 요청을 보냅니다. RangingRequest 로는 작업을 지정하고, Executor로는 콜백 내용을 지정하며, RangingResultCallback으로는 결과를 수신합니다.

예:

Kotlin

val mgr = context.getSystemService(Context.WIFI_RTT_RANGING_SERVICE) as WifiRttManager
val request: RangingRequest = myRequest
mgr.startRanging(request, executor, object : RangingResultCallback() {

    override fun onRangingResults(results: List<RangingResult>) { … }

    override fun onRangingFailure(code: Int) { … }
})

Java

WifiRttManager mgr =
      (WifiRttManager) Context.getSystemService(Context.WIFI_RTT_RANGING_SERVICE);

RangingRequest request ...;
mgr.startRanging(request, executor, new RangingResultCallback() {

  @Override
  public void onRangingFailure(int code) { … }

  @Override
  public void onRangingResults(List<RangingResult> results) { … }
});

범위 설정 작업은 비동기식으로 실행되며 범위 설정 결과는 RangingResultCallback의 콜백 중 하나로 반환됩니다.

  • 전체 범위 설정 작업이 실패하면 RangingResultCallback에서 설명한 상태 코드와 함께 onRangingFailure 콜백이 트리거됩니다. 서비스가 그 시점에 범위 설정 작업을 실행할 수 없을 경우 이런 실패가 발생할 수 있습니다. 예를 들어 Wi-Fi가 비활성화되었거나, 애플리케이션이 너무 많은 범위 설정 작업을 요청해서 사용이 제한되었거나, 권한에 문제가 있는 것이 원인일 수 있습니다.
  • 범위 설정 작업이 완료되면 요청의 목록과 일치하는 결과 목록(요청당 1개)과 함께 onRangingResults 콜백이 트리거됩니다. 결과의 순서는 요청의 순서와 반드시 일치하지는 않습니다. 범위 설정 작업이 완료되더라도 각 결과에서 특정 측정이 실패한 것으로 나타날 수 있습니다.

범위 설정 결과 해석

onRangingResults 콜백이 반환하는 각각의 결과는 RangingResult 객체로 지정됩니다. 각 요청에 관해 다음을 실행합니다.

1. 요청 식별

RangingRequest를 생성할 때 제공된 정보에 기초하여 요청을 식별합니다. 일반적으로 액세스 포인트를 식별하는 ScanResult에 제공된 MAC 주소입니다. MAC 주소는 getMacAddress() 메서드를 사용하여 범위 설정 결과에서 얻을 수 있습니다.

범위 설정 결과의 목록은 범위 설정 요청에서 지정한 동종 앱(액세스 포인트)의 순서와 다를 수 있으므로 MAC 주소를 사용하여 결과의 순서가 아니라 동종 앱을 식별해야 합니다.

2. 각 측정이 성공했는지 확인

측정이 성공했는지 확인하려면 getStatus() 메서드를 사용합니다. STATUS_SUCCESS를 제외한 모든 값은 실패를 나타냅니다. 실패는 이 결과를 제외한 다른 모든 필드(위의 요청 식별 제외)가 무효이고 이 get* 메서드가 IllegalStateException 예외와 함께 실패한다는 것을 의미합니다.

3. 각 성공적인 측정에 대한 결과 가져오기

각 성공적인 측정에 관해 각 get 메서드로 결과 값을 검색할 수 있습니다.

Wi-Fi RTT를 지원하는 Android 기기

아래 표에는 Wi-Fi-RTT를 지원하는 일부 휴대전화, 액세스 포인트, 소매, 창고, 유통 센터 기기가 나와 있습니다. 많은 정보가 포함되어 있지 않습니다. RTT 지원 제품을 확인하려면 Google에 문의하시기 바랍니다.

액세스 포인트

제조업체 및 모델 지원 날짜
Nest Wifi Pro (Wi-Fi 6E) 지원됨
Compulab WILD AP 지원됨
Google Wi-Fi 지원됨
Google Nest Wi-Fi 라우터 지원됨
Google Nest Wi-Fi 포인트 지원됨
아루바 AP-635 지원됨
시스코 9130 지원됨
시스코 9136 지원됨
시스코 9166 지원됨
시스코 9164 지원됨
아루바 AP-505 지원됨
아루바 AP-515 지원됨
아루바 AP-575 지원됨
아루바 AP-518 지원됨
아루바 AP-505H 지원됨
아루바 AP-565 지원됨
아루바 AP-535 지원됨

휴대전화

제조업체 및 모델 Android 버전
Pixel 6 9.0+
Pixel 6 Pro 9.0+
Pixel 5 9.0+
Pixel 5a 9.0+
Pixel 5a(5G) 9.0+
Xiaomi Mi 10 Pro 9.0+
Xiaomi Mi 10 9.0+
Xiaomi Redmi Mi 9T Pro 9.0+
Xiaomi Mi 9T 9.0+
Xiaomi Mi 9 9.0+
Xiaomi Mi Note 10 9.0+
Xiaomi Mi Note 10 Lite 9.0+
Xiaomi Redmi Note 9S 9.0+
Xiaomi Redmi Note 9 Pro 9.0+
Xiaomi Redmi Note 8T 9.0+
Xiaomi Redmi Note 8 9.0+
Xiaomi Redmi K30 Pro 9.0+
Xiaomi Redmi K20 Pro 9.0+
Xiaomi Redmi K20 9.0+
Xiaomi Redmi Note 5 Pro 9.0+
Xiaomi Mi CC9 Pro 9.0+
LG G8X ThinQ 9.0+
LG V50S ThinQ 9.0+
LG V60 ThinQ 9.0+
LG V30 9.0+
삼성 Galaxy Note 10+ 5G 9.0+
삼성 Galaxy S20+ 5G 9.0+
삼성 Galaxy S20+ 9.0+
삼성 Galaxy S20 5G 9.0+
삼성 갤럭시 S20 Ultra 5G 9.0+
삼성 Galaxy S20 9.0+
삼성 Galaxy Note 10+ 9.0+
삼성 Galaxy Note 10 5G 9.0+
Samsung Galaxy Note 10 9.0+
삼성 A9 프로 9.0+
Google Pixel 4 XL 9.0+
Google Pixel 4 9.0+
Google Pixel 4a 9.0+
Google Pixel 3 XL 9.0+
Google Pixel 3 9.0+
Google Pixel 3a XL 9.0+
Google Pixel 3a 9.0+
Google Pixel 2 XL 9.0+
Google Pixel 2 9.0+
Google Pixel 1 XL 9.0+
Google Pixel 1 9.0+
포코 X2 9.0+
Sharp Aquos R3 SH-04L 9.0+

소매, 창고 및 유통 센터 기기

제조업체 및 모델 Android 버전
Zebra PS20 10.0 이상
Zebra TC52/TC52HC 10.0 이상
Zebra TC57 10.0 이상
Zebra TC72 10.0 이상
Zebra TC77 10.0 이상
얼룩말 MC93 10.0 이상
Zebra TC8300 10.0 이상
Zebra VC8300 10.0 이상
Zebra EC30 10.0 이상
Zebra ET51 10.0 이상
Zebra ET56 10.0 이상
얼룩말 L10 10.0 이상
Zebra CC600/CC6000 10.0 이상
Zebra MC3300X 10.0 이상
Zebra MC330X 10.0 이상
Zebra TC52X 10.0 이상
Zebra TC57X 10.0 이상
Zebra EC50 (LAN 및 HC) 10.0 이상
Zebra EC55 (WAN) 10.0 이상
Zebra WT6300 10.0 이상
스코르피오 X5 10.0 이상