通过 RTT 确定 WLAN 位置信息

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

如果您测量与三个或更多接入点的距离,可以使用多点定位算法来预估与这些测量值最相符的设备位置。结果通常可以精确到 1 至 2 米。

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

请求发出设备无需连接到接入点即可通过 Wi-Fi RTT 测量距离。为了保护隐私,只有发出请求的设备能够确定距接入点的距离,接入点没有此类信息。前台应用执行 Wi-Fi RTT 操作不受限制,但后台应用执行此类操作时会受限。

Wi-Fi RTT 和相关的精确时间测量 (FTM) 功能由 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(例如用户进入新建筑物时),您的应用也可以找到 AP 并确定其位置。

要求

  • 测距请求发出设备的硬件必须实现 802.11-2016 FTM 标准。
  • 测距请求发出设备必须搭载 Android 9(API 级别 28)或更高版本的操作系统。
  • 测距请求发出设备必须启用位置信息服务并打开 WLAN 扫描(位于设置 > 位置信息下)。
  • 如果发出测距请求的应用以 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,但由于用户已停用 WLAN,该功能目前或不可用。如果 SoftAP 或网络共享处于使用状态,某些设备可能不支持 Wi-Fi RTT,具体视设备的硬件和固件功能而定。如需检查 Wi-Fi RTT 当前是否可用,请调用 isAvailable()

Wi-Fi RTT 的可用性随时都可能发生变化。您的应用应注册 BroadcastReceiver 才能收到当可用性发生变化时发送的 ACTION_WIFI_RTT_STATE_CHANGED。应用收到该广播 intent 后,应检查可用性的当前状态并相应地调整其行为。

例如:

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 感知对等设备的列表,即可创建测距请求 (RangingRequest)。您可以在单个测距请求中指定多个接入点或 Wi-Fi 感知对等设备,然后测量并返回与所有设备的距离。

例如,请求可以使用 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) 批量添加多个接入点。

同样,测距请求可以通过以下两种途径添加 Wi-Fi 感知对等设备:使用 addWifiAwarePeer(MacAddress peer) 方法利用相应设备的 MAC 地址,或者使用 addWifiAwarePeer(PeerHandle peer) 方法利用相应设备的 PeerHandle。如需详细了解如何发现 Wi-Fi 感知对等设备,请参阅 Wi-Fi 感知文档

请求测距

应用使用 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 中描述的状态代码。如果该服务当时出于某些原因(例如 WLAN 已停用、应用请求的测距操作过多并受到限制,或者存在权限问题)无法执行测距操作,可能会发生此类失败。
  • 测距操作完成后,会触发 onRangingResults 回调,并返回与请求列表匹配的结果列表(每个请求对应一个结果)。结果的顺序不一定与请求的顺序一致。请注意,测距操作可能已经完成,但每个结果仍有可能提示该特定测量失败。

解读测距结果

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

1. 识别请求

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

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

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

如需确定测量是否成功,请使用 getStatus() 方法。STATUS_SUCCESS 以外的任何值都表示失败。失败意味着此结果的所有其他字段(上述请求标识除外)均为无效字段,相应的 get* 方法也将失败,并出现 IllegalStateException 异常。

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

对于每个成功的测量,您可以使用相应的 get 方法检索结果值:

支持 Wi-Fi RTT 的 Android 设备

下表列出了一些支持 Wi-Fi RTT 的手机接入点以及零售、仓储和配送中心设备。这些远远不够全面。建议您与我们联系,在此处列出支持 RTT 的产品。

接入点

制造商和型号 支持日期
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 及更高版本
小米 Mi 10 Pro 9.0 及更高版本
小米 Mi 10 9.0 及更高版本
小米红米 Mi 9T Pro 9.0 及更高版本
小米 Mi 9T 9.0 及更高版本
小米 Mi 9 9.0 及更高版本
小米 Mi Note 10 9.0 及更高版本
小米 Mi Note 10 Lite 9.0 及更高版本
小米红米 Note 9S 9.0 及更高版本
小米红米 Note 9 Pro 9.0 及更高版本
小米红米 Note 8T 9.0 及更高版本
小米红米 NOTE 8 9.0 及更高版本
小米红米 K30 Pro 9.0 及更高版本
小米红米 K20 Pro 9.0 及更高版本
小米红米 K20 9.0 及更高版本
Xiaomi 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 及更高版本
Samsung 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 及更高版本
Samsung Galaxy Note 10 及更高版本 9.0 及更高版本
三星 Galaxy Note 10 5G 9.0 及更高版本
三星 Galaxy Note 10 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 及更高版本
斑马 TC52/TC52HC 10.0 及更高版本
斑马纹 TC57 10.0 及更高版本
斑马纹 TC72 10.0 及更高版本
斑马 TC77 10.0 及更高版本
斑马纹 MC93 10.0 及更高版本
斑马纹 TC8300 10.0 及更高版本
斑马纹 VC8300 10.0 及更高版本
斑马纹 EC30 10.0 及更高版本
斑马 ET51 10.0 及更高版本
斑马 ET56 10.0 及更高版本
斑马纹 L10 10.0 及更高版本
斑马纹 CC600/CC6000 10.0 及更高版本
斑马纹 MC3300x 10.0 及更高版本
斑马纹 MC330x 10.0 及更高版本
斑马 TC52x 10.0 及更高版本
斑马 TC57x 10.0 及更高版本
Zebra EC50(LAN 和 HC) 10.0 及更高版本
Zebra EC55 (WAN) 10.0 及更高版本
斑马纹 WT6300 10.0 及更高版本
Skorpio X5 10.0 及更高版本