Wi-Fi 位置情報: RTT を使用したレンジング(距離測定)

Wi-Fi RTT(ラウンドトリップ時間)API が提供する Wi-Fi 位置情報機能を使用して、付近にある RTT 対応の Wi-Fi アクセス ポイントや Wi-Fi Aware ピア デバイスとの距離を測定できます。

3 つ以上のアクセス ポイントまでの距離を測定する場合は、マルチラテレーション アルゴリズムを使用して、測定に最適なデバイスの位置を推定できます。結果の精度は通常 1~2 メートルの誤差の範囲内です。

この精度を確保できることによって、屋内ナビゲーション、明確な音声操作(例: 「このライトをつけて」)、位置情報をベースとする情報(例: 「このサービスに関してスペシャル オファーはありますか?」)などの精度の高い、位置情報を利用したサービスを開発できます。

リクエスト元のデバイスは、Wi-Fi RTT で距離を測定するためにアクセス ポイントに接続する必要はありません。プライバシーを保護するため、アクセス ポイントまでの距離を測定できるのはリクエスト元のデバイスのみです。アクセス ポイントにはこの情報がありません。Wi-Fi RTT オペレーションでは、フォアグラウンド アプリに対する制限はありませんが、バックグラウンド アプリに対しては制限が課されます。

Wi-Fi RTT と関連する Fine-Time-Measurement(FTM)機能は、IEEE 802.11-2016 規格で規定されています。Wi-Fi RTT では、パケットがデバイス間を往復する時間を測定し、測定された時間に光速を乗じることによって 2 つのデバイス間の距離を計算するため、FTM による正確な時間測定が必要です。

Android 15(API レベル 35)では、IEEE 802.11az のトリガーベース以外の(NTB)測距のサポートが導入されました。

Android のバージョンに基づく実装の違い

Wi-Fi RTT は Android 9(API レベル 28)で導入されました。Android 9 搭載デバイスの場合、このプロトコルを使用してマルチラテレーションでデバイスの位置を特定するには、アプリが事前定義されたアクセス ポイント(AP)の位置情報データにアクセスする必要があります。このデータの保存方法や取得方法は任意に決定できます。

Android 10(API レベル 29)以降を搭載するデバイスでは、AP の位置情報データは緯度、経度、高度を含む ResponderLocation オブジェクトとして表すことができます。Location Configuration Information / Location Civic Report(LCI / LCR データ)をサポートする Wi-Fi RTT AP の場合、プロトコルは距離測定プロセス中に ResponderLocation オブジェクトを返します。

この機能を使用すると、アプリは事前にこの情報を保存する代わりに、AP に直接クエリを実行して位置を問い合わせることができます。そのため、ユーザーがある建物に初めて入ったときなど、AP を事前に把握できていない場合でも、アプリは AP を見つけてその位置を特定できます。

IEEE 802.11az NTB 測距のサポートは、Android 15(API レベル 35)以降を搭載したデバイスで利用できます。つまり、デバイスが IEEE 802.11az NTB イニシエータ モード(WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_NTB_INITIATOR で示されます)をサポートしている場合、アプリは単一の範囲リクエストで IEEE 802.11mc と IEEE 802.11az の両方に対応する AP を検出できます。RangingResult API が拡張され、測定間の間隔に使用できる最小値と最大値に関する情報が提供されるようになりました。正確な間隔については、アプリで管理できます。

要件

  • 距離測定のリクエストを行うデバイスのハードウェアは、802.11-2016 FTM 規格または 802.11az 規格(トリガーベース以外の測定)を実装する必要があります。
  • 測距リクエストを行うデバイスは、Android 9(API レベル 28)以降を搭載している必要があります。IEEE 802.11az のトリガーベース以外の測距は、Android 15(API レベル 35)以降を搭載したデバイスで有効になります。
  • 距離測定のリクエストを行うデバイスでは、[設定] > [位置情報] で位置情報サービスを有効にして Wi-Fi スキャンをオンにする必要があります。
  • 測距リクエストを行うアプリが Android 13(API レベル 33)以降をターゲットとしている場合は、NEARBY_WIFI_DEVICES 権限が必要です。このようなアプリが以前のバージョンの Android をターゲットとしている場合は、代わりに ACCESS_FINE_LOCATION 権限が必要です。
  • アプリは表示中またはフォアグラウンドでの実行中に、アクセス ポイントの距離を照会する必要があります。アプリはバックグラウンドからは位置情報にアクセスできません。
  • アクセス ポイントは、IEEE 802.11-2016 FTM 標準または IEEE 802.11az 標準(非トリガー ベースの距離測定)を実装する必要があります。

設定

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

詳細については、ブロードキャストをご覧ください。

距離測定のリクエストを作成する

距離測定のリクエスト(RangingRequest)は、距離をリクエストする対象の AP または Wi-Fi Aware ピアのリストを指定して作成します。1 つの距離測定リクエストで複数のアクセス ポイントまたは 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<ScanResult>) を使用すると、複数のアクセス ポイントを一括で追加できます。

ScanResult オブジェクトには、IEEE 802.11mc(is80211mcResponder())と IEEE 802.11az のトリガーベース以外の測距(is80211azNtbResponder())をサポートする AP の両方を指定できます。IEEE 802.11az NTB 測距をサポートするデバイスは、AP の機能に応じて 802.11mc または 802.11az 測距を行います。AP が両方をサポートしている場合は、デフォルトで 802.11az になります。IEEE 802.11az をサポートしていないデバイスでは、IEEE 802.11mc プロトコルを使用してすべてのレンジングを実行します。

同様に、圏内のリクエストでは、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 のコールバックの 1 つで返されます。

  • 距離測定オペレーション全体が失敗すると、RangingResultCallback で説明されているステータス コードで onRangingFailure コールバックがトリガーされます。このような失敗は、Wi-Fi が無効になっている、アプリがリクエストした距離測定オペレーションが多すぎるため制限が課されている、または権限に問題があるなどの理由により、サービスで距離測定オペレーションが実行できない場合に発生します。
  • 距離測定オペレーションが完了すると、onRangingResults コールバックが、リクエストのリストに一致する結果(各リクエストに対して 1 つの結果)のリストでトリガーされます。結果の順序は、リクエストの順序と一致しない場合があります。距離測定オペレーションが完了した場合でも、各結果が依然として特定の測定の失敗を示している可能性がある点に留意してください。

距離測定の結果を解釈する

onRangingResults コールバックから返される各結果は、RangingResult オブジェクトによって指定されます。各リクエストで、次の操作を行います。

1. リクエストを特定する

RangingRequest の作成時に提供された情報(ほとんどの場合は、ScanResult で提供され、アクセス ポイントを特定する MAC アドレス)に基づいてリクエストを特定します。MAC アドレスは、getMacAddress() メソッドを使用して距離測定の結果から取得できます。

距離測定の結果のリストにおける並び順は、距離測定リクエストで指定されたピア(アクセス ポイント)とは異なる場合があるため、結果の順序ではなく MAC アドレスを使用してピアを特定する必要があります。

2. 各測定が成功したかどうかを判断する

測定が成功したかどうかを判断するには、getStatus() メソッドを使用します。STATUS_SUCCESS 以外の値は失敗を示します。失敗した場合、この結果の他のすべてのフィールド(上記のリクエスト ID を除く)は無効になり、対応する get* メソッドは IllegalStateException 例外で失敗します。

3. 成功した各測定の結果を取得する

成功した測定(RangingResult)ごとに、それぞれの get メソッドを使用して結果値を取得できます。

  • 距離(mm)と測定の標準偏差:

    getDistanceMm()

    getDistanceStdDevMm()

  • 測定に使用されたパケットの RSSI:

    getRssi()

  • 測定が行われた時間(ミリ秒。起動からの経過時間を示します):

    getRangingTimestampMillis()

  • 試行された測定回数と成功した測定回数(距離測定はこれに基づきます):

    getNumAttemptedMeasurements()

    getNumSuccessfulMeasurements()

  • クライアント デバイスが 11az NTB 測定の間に待機する最小時間と最大時間:

    getMinTimeBetweenNtbMeasurementsMicros()getMaxTimeBetweenNtbMeasurementsMicros() は、最小時間と最大時間を返します。最小時間が経過する前に次の距離測定がリクエストされた場合、API はキャッシュに保存された距離測定の結果を返します。最大時間が経過した後に次の測距測定がリクエストされた場合、API はトリガー以外の測距セッションを終了し、応答ステーションで新しい測距セッションをネゴシエートします。距離測定に新たなオーバーヘッドが加わるため、新しい距離測定セッションをリクエストしないようにしてください。802.11az の非トリガーベースの距離測定を最大限に活用するには、前の RangingResult 測定で指定された最小測定時間と最大測定時間の間に次の距離測定リクエストをトリガーします。

  • レスポンダ ステーションとイニシエーター ステーションが IEEE 802.11az NTB の結果のプリアンブルで使用した Long Training Field(LTF)の繰り返し:

    get80211azResponderTxLtfRepetitionsCount()

    get80211azInitiatorTxLtfRepetitionsCount()

  • イニシエーター ステーションによって IEEE 802.11az NTB の結果に使用された送信空間時間ストリーム(STS)と受信空間時間ストリーム(STS)の数:

    get80211azNumberOfTxSpatialStreams()

    get80211azNumberOfRxSpatialStreams()

Wi-Fi-RTT 対応の Android デバイス

次の表に、Wi-Fi-RTT をサポートするスマートフォンアクセス ポイント小売店、倉庫、配送センターのデバイスを示します。これらはごく一部を示したものです。こちらに RTT 対応プロダクトを掲載される場合は、お問い合わせいただくことをおすすめします。

アクセス ポイント

メーカーとモデル サポート日
Google Nest Wifi Pro (Wi-Fi 6E) サポート対象
Compulab WILD AP サポート対象
Google WiFi サポート対象
Google Nest Wifi ルーター サポート対象
Google Nest Wifi 拡張ポイント サポート対象
Aruba AP-635 サポート対象
Cisco 9130 サポート対象
Cisco 9136 サポート対象
Cisco 9166 サポート対象
Cisco 9164 サポート対象
Aruba AP-505 サポート対象
Aruba AP-515 サポート対象
Aruba AP-575 サポート対象
Aruba AP-518 サポート対象
Aruba AP-505H サポート対象
Aruba AP-565 サポート対象
Aruba AP-535 サポート対象

スマートフォン

メーカーとモデル Android バージョン
Google Pixel 6 9.0+
Google Pixel 6 Pro 9.0+
Google Pixel 5 9.0+
Google Pixel 5a 9.0+
Google 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+
Samsung Galaxy Note 10+ 5G 9.0+
Samsung Galaxy S20+ 5G 9.0+
Samsung Galaxy S20+ 9.0+
Samsung Galaxy S20 5G 9.0+
Samsung Galaxy S20 Ultra 5G 9.0+
Samsung Galaxy S20 9.0+
Samsung Galaxy Note 10+ 9.0+
Samsung Galaxy Note 10 5G 9.0+
Samsung Galaxy Note10 9.0+
Samsung A9 Pro 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+
Poco X2 9.0+
シャープ Aquos R3 SH-04L 9.0+

小売店、倉庫、配送センター向けデバイス

メーカーとモデル Android バージョン
ゼブラ PS20 10.0 以降
Zebra TC52、TC52HC 10.0 以降
Zebra TC57 10.0 以降
Zebra TC72 10.0 以降
Zebra TC77 10.0 以降
Zebra MC93 10.0 以降
ゼブラ TC8300 10.0 以降
Zebra VC8300 10.0 以降
Zebra EC30 10.0 以降
Zebra ET51 10.0 以降
Zebra ET56 10.0 以降
Zebra L10 10.0 以降
Zebra CC600/CC6000 10.0 以降
Zebra MC3300x 10.0 以降
ゼブラ 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 以降
Skorpio X5 10.0 以降