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_DEVICES
및 ACCESS_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
메서드로 결과 값을 검색할 수 있습니다.
거리(mm)와 측정값의 표준 편차:
측정에 사용된 RSSI:
측정을 실시한 시간(ms)(부팅 이후부터의 시간):
시도한 측정 횟수 및 성공한(거리 측정값의 기초가 되는) 측정 횟수:
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 이상 |