Wi-Fi 定位:使用 RTT 进行测距

您可以使用 Wi-Fi RTT(往返时间)API 提供的 Wi-Fi 定位功能来测量与附近的支持 RTT 的 Wi-Fi 接入点和对等 Wi-Fi Aware 设备的距离。

如果您测量到三个或更多接入点的距离,则可以使用多边测量算法来估计最符合这些测量的设备位置。结果通常精确到 1-2 米。

凭借这种精度,您可以开发细粒度的基于位置的服务,例如室内导航、消除歧义的语音控制(例如,“打开这盏灯”)和基于位置的信息(例如,“此产品是否有特价优惠?”)。

请求设备无需连接到接入点即可使用 Wi-Fi RTT 测量距离。为了维护隐私,只有请求设备能够确定到接入点的距离;接入点没有此信息。对于前台应用,Wi-Fi RTT 操作不受限制,但对于后台应用则会受到限制。

Wi-Fi RTT 和相关的 *精细时间测量* (FTM) 功能由 IEEE 802.11-2016 标准指定。Wi-Fi RTT 需要 FTM 提供的精确时间测量,因为它通过测量数据包在设备之间往返所需的时间并将其乘以光速来计算两台设备之间的距离。

Android 15(API 级别 35)引入了对 IEEE 802.11az 非触发式 (NTB) 测距的支持。

基于 Android 版本的实现差异

WiFi RTT 功能在 Android 9(API 级别 28)中引入。使用此协议通过多边定位法确定运行 Android 9 的设备的位置时,您需要访问应用中预先确定的接入点 (AP) 位置数据。如何存储和检索此数据由您决定。

在运行 Android 10(API 级别 29)及更高版本的设备上,AP 位置数据可以表示为 ResponderLocation 对象,其中包含纬度、经度和海拔高度。对于支持位置配置信息/位置公民报告 (LCI/LCR 数据) 的 WiFi 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)或更高版本。在运行 Android 15(API 级别 35)及更高版本的设备上启用了 IEEE 802.11az 非触发式测距。
  • 发出测距请求的设备必须启用位置服务并打开 WiFi 扫描(在**设置 > 位置**下)。
  • 如果发出测距请求的应用面向 Android 13(API 级别 33)或更高版本,则必须具有 NEARBY_WIFI_DEVICES 权限。如果此类应用面向早期版本的 Android,则必须具有 ACCESS_FINE_LOCATION 权限。
  • 应用必须在应用可见或处于前台服务时查询接入点的范围。应用不能 从后台访问位置信息
  • 接入点必须实现 IEEE 802.11-2016 FTM 标准或 IEEE 802.11az 标准(非触发式测距)。

设置

要设置您的应用以使用 WiFi 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. 检查设备是否支持 WiFi RTT

要检查设备是否支持 WiFi RTT,请使用 PackageManager API

Kotlin

context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)

Java

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

3. 检查 WiFi RTT 是否可用

设备上可能存在 WiFi RTT,但它可能不可用,因为用户已禁用 WiFi。根据其硬件和固件功能,如果使用 SoftAP 或网络共享,某些设备可能不支持 WiFi RTT。要检查 WiFi RTT 是否可用,请调用 isAvailable()

WiFi 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 或 WiFi Aware 对等点来创建。可以在单个测距请求中指定多个接入点或 WiFi 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 添加 WiFi Aware 对等点,分别使用 addWifiAwarePeer(MacAddress peer)addWifiAwarePeer(PeerHandle peer) 方法。有关发现 WiFi Aware 对等点的更多信息,请参阅 WiFi 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 的回调之一中返回

  • 如果整个测距操作失败,则 onRangingFailure 回调将使用 RangingResultCallback 中描述的状态代码触发。如果服务此时无法执行测距操作,则可能会发生此类失败,例如,因为 WiFi 已禁用,因为应用程序请求了过多的测距操作并被限制,或者因为权限问题。
  • 当测距操作完成时,onRangingResults 回调将使用与请求列表匹配的结果列表触发——每个请求一个结果。结果的顺序不一定与请求的顺序匹配。请注意,测距操作可能已完成,但每个结果仍可能表示该特定测量的失败。

解释测距结果

onRangingResults 回调返回的每个结果都由 RangingResult 对象指定。对于每个请求,请执行以下操作。

1. 识别请求

根据创建 RangingRequest 时提供的信息识别请求:最常见的是 ScanResult 中提供的标识接入点的 MAC 地址。可以使用 getMacAddress() 方法从测距结果中获取 MAC 地址。

测距结果列表的顺序可能与测距请求中指定的对等点(接入点)顺序不同,因此您应该使用 MAC 地址来识别对等点,而不是结果的顺序。

2. 确定每次测量是否成功

要确定测量是否成功,请使用 getStatus() 方法。任何非 STATUS_SUCCESS 的值都表示失败。失败意味着此结果的所有其他字段(除了上面的请求标识)均无效,并且相应的 get* 方法将引发 IllegalStateException 异常。

3. 获取每次成功测量的结果

对于每次成功的测量(RangingResult),您可以使用相应的get方法检索结果值。

支持WiFi-RTT的Android设备

下表列出了一些支持WiFi-RTT的手机接入点零售、仓储和配送中心设备。这些远非详尽无遗。我们鼓励您联系我们,在此处列出您支持RTT的产品。

接入点

制造商和型号 支持日期
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 版本
Pixel 6 9.0+
Pixel 6 Pro 9.0+
Pixel 5 9.0+
Pixel 5a 9.0+
Pixel 5a 5G 9.0+
小米 Mi 10 Pro 9.0+
小米 Mi 10 9.0+
小米 Redmi Mi 9T Pro 9.0+
小米 Mi 9T 9.0+
小米 Mi 9 9.0+
小米 Mi Note 10 9.0+
小米 Mi Note 10 Lite 9.0+
小米 Redmi Note 9S 9.0+
小米 Redmi Note 9 Pro 9.0+
小米 Redmi Note 8T 9.0+
小米 Redmi Note 8 9.0+
小米 Redmi K30 Pro 9.0+
小米 Redmi K20 Pro 9.0+
小米 Redmi K20 9.0+
小米 Redmi Note 5 Pro 9.0+
小米 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+
三星 Galaxy S20 Ultra 5G 9.0+
三星 Galaxy S20 9.0+
三星 Galaxy Note 10+ 9.0+
三星 Galaxy Note 10 5G 9.0+
三星 Galaxy Note 10 9.0+
三星 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 版本
Zebra PS20 10.0+
Zebra TC52/TC52HC 10.0+
Zebra TC57 10.0+
Zebra TC72 10.0+
Zebra TC77 10.0+
Zebra MC93 10.0+
Zebra 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+
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+
Skorpio X5 10.0+