超宽带 (UWB) 通信

超宽带通信是一种无线电技术,专注于设备之间的精确测距(定位精度可达 10 厘米)。这种无线电技术可以使用低能量密度进行短距离测量,并在无线电频谱的很大一部分上执行高带宽信号传输。UWB 的带宽大于 500 MHz(或超过 20% 的分数带宽)。

控制器/发起者与受控器/响应者

UWB 通信在两个设备之间进行,其中一个是控制器 (Controller),另一个是受控器 (Controlee)。控制器决定两个设备将共享的复杂信道 (UwbComplexChannel),并作为发起者 (initiator),而受控器则作为响应者 (responder)。

一个控制器可以处理多个受控器,但一个受控器只能订阅一个控制器。控制器/发起者和受控器/响应者配置均受支持。

测距参数

控制器和受控器需要相互识别,并通信测距参数以开始测距。此交换过程由应用自行选择安全的带外 (OOB) 机制来实现,例如低功耗蓝牙 (BLE)。

测距参数包括本地地址、复杂信道和会话密钥等。请注意,这些参数在测距会话结束后可能会轮换或以其他方式更改,需要重新通信才能重新开始测距。

后台测距

如果设备支持,在后台运行的应用可以启动 UWB 测距会话。要检查您的设备功能,请参阅 RangingCapabilities

应用在后台运行时不会收到测距报告;应用移至前台时才会收到测距报告。

STS 配置

应用或服务为每个会话使用扰码时间戳序列 (STS) 提供会话密钥。已提供 STS 比静态 STS 配置更安全。Android 14 或更高版本的所有启用 UWB 的设备都支持已提供 STS。

威胁类别 静态 STS 已提供 STS
空中:被动观察者 已缓解 已缓解
空中:信号放大 已缓解 已缓解
空中:重放/中继攻击 易受攻击 已缓解

对于已提供 STS

  1. 在支持已提供 STS 的 RangingParameters 中使用 uwbConfigType

  2. sessionKeyInfo 字段中提供 16 字节密钥。

对于静态 STS

  1. 在支持静态 STS 的 RangingParameters 中使用 uwbConfigType

  2. sessionKeyInfo 字段中提供 8 字节密钥。

步骤

要使用 UWB API,请按照以下步骤操作

  1. 确保 Android 设备运行的是 Android 12 或更高版本,并且使用 PackageManager#hasSystemFeature("android.hardware.uwb") 支持 UWB。
  2. 如果对物联网设备进行测距,请确保它们符合 FiRa MAC 1.3 标准。
  3. 使用您选择的 OOB 机制(例如 BluetoothLeScanner)发现支持 UWB 的对等设备。
  4. 使用您选择的安全 OOB 机制(例如 BluetoothGatt)交换测距参数。
  5. 如果用户想停止会话,请取消会话范围。

使用限制

以下限制适用于 UWB API 的使用

  1. 除非如前所述支持后台测距,否则发起新 UWB 测距会话的应用必须是前台应用或服务。
  2. 当应用移至后台(会话正在进行中)时,应用可能不再接收测距报告。但是,UWB 会话将继续在较低层维护。当应用返回前台时,测距报告将恢复。

代码示例

示例应用

有关如何使用 UWB Jetpack 库的端到端示例,请查看我们 在 Github 上的示例应用。此示例应用涵盖在 Android 设备上验证 UWB 兼容性、使用 OOB 机制启用发现过程以及在两个支持 UWB 的设备之间设置 UWB 测距。该示例还涵盖设备控制和媒体共享用例。

UWB 测距

此代码示例针对受控器启动和终止 UWB 测距

// The coroutineScope responsible for handling uwb ranging.
// This will be initialized when startRanging is called.
var job: Job?

// A code snippet that initiates uwb ranging for a Controlee.
suspend fun startRanging() {

    // Get the ranging parameter of a partnering Controller using an OOB mechanism of choice.
    val partnerAddress : Pair<UwbAddress, UwbComplexChannel> = listenForPartnersAddress()

    // Create the ranging parameters.
    val partnerParameters = RangingParameters(
        uwbConfigType = UwbRangingParameters.UWB_CONFIG_ID_1,
        // SessionKeyInfo is used to encrypt the ranging session.
        sessionKeyInfo = null,
        complexChannel = partnerAddress.second,
        peerDevices = listOf(UwbDevice.createForAddress(partnerAddress.first)),
        updateRateType = UwbRangingParameters.RANGING_UPDATE_RATE_AUTOMATIC
    )

    // Initiate a session that will be valid for a single ranging session.
    val clientSession = uwbManager.clientSessionScope()

    // Share the localAddress of the current session to the partner device.
    broadcastMyParameters(clientSession.localAddress)

    val sessionFlow = clientSession.prepareSession(partnerParameters)

    // Start a coroutine scope that initiates ranging.
    CoroutineScope(Dispatchers.Main.immediate).launch {
        sessionFlow.collect {
            when(it) {
                is RangingResultPosition -> doSomethingWithPosition(it.position)
                is RangingResultPeerDisconnected -> peerDisconnected(it)
            }
        }
    }
}

// A code snippet that cancels uwb ranging.
fun cancelRanging() {

    // Canceling the CoroutineScope will stop the ranging.
    job?.let {
        it.cancel()
    }
}

RxJava3 支持

Rxjava3 支持现已推出,有助于实现与 Java 客户端的互操作性。此库提供了一种将测距结果作为 Observable 或 Flowable 流获取的方式,并将 UwbClientSessionScope 作为 Single 对象检索的方式。

private final UwbManager uwbManager;

// Retrieve uwbManager.clientSessionScope as a Single object
Single<UwbClientSessionScope> clientSessionScopeSingle =
                UwbManagerRx.clientSessionScopeSingle(uwbManager);
UwbClientSessionScope uwbClientSessionScope = clientSessionScopeSingle.blockingGet();

// Retrieve uwbClientSessionScope.prepareSession Flow as an Observable object
Observable<RangingResult> rangingResultObservable =
                UwbClientSessionScopeRx.rangingResultsObservable(clientSessionScope,
                        rangingParameters);

// Consume ranging results from Observable
rangingResultObservable.subscribe(
   rangingResult -> doSomethingWithRangingResult(result), // onNext
   (error) -> doSomethingWithError(error), // onError
   () -> doSomethingOnResultEventsCompleted(), //onCompleted
);
// Unsubscribe
rangingResultObservable.unsubscribe();
   

// Retrieve uwbClientSessionScope.prepareSession Flow as a Flowable object
Flowable<RangingResult> rangingResultFlowable =
                UwbClientSessionScopeRx.rangingResultsFlowable(clientSessionScope,
                        rangingParameters);

// Consume ranging results from Flowable using Disposable
Disposable disposable = rangingResultFlowable
   .delay(1, TimeUnit.SECONDS)
   .subscribeWith(new DisposableSubscriber<RangingResult> () {
      @Override public void onStart() {
          request(1);
      }
      
      @Override public void onNext(RangingResult rangingResult) {
             doSomethingWithRangingResult(rangingResult);
             request(1);
      }


      @Override public void onError(Throwable t) {
             t.printStackTrace();
      }


         @Override public void onComplete() {
            doSomethingOnEventsCompleted();
         }
   });

// Stop subscription
disposable.dispose();

生态系统支持

以下是受支持的合作伙伴设备和第三方 SDK。

支持 UWB 的移动设备

截至 2025 年 1 月,以下设备支持 Android UWB Jetpack 库

供应商 设备型号
Google Pixel Pro(6 Pro 及更新型号)、Fold、平板电脑
Motorola Edge 50 Ultra
Samsung Galaxy Note 20、Galaxy Plus 和 Ultra(S21 及更新型号)、Galaxy Z Fold(Fold2 及更新型号)

注意:除以下设备外,所有设备均支持后台 UWB 测距

  • Pixel 6 Pro、Pixel 7 Pro 和 Pixel Fold。
  • 运行 Android 13 或更低版本的 Samsung 手机。
  • 运行 Android 14 或更低版本的 Samsung 中国手机。

第三方 SDK

截至 2023 年 4 月,以下合作伙伴解决方案与当前 Jetpack 库兼容。

已知问题:MAC 地址和静态 STS 供应商 ID 字段的字节顺序颠倒

在 Android 13 及更低版本中,Android UWB 堆栈错误地颠倒了以下字段的字节顺序

  • 设备 MAC 地址
  • 目标 MAC 地址
  • 静态 STS 供应商 ID

字节顺序颠倒发生是因为 Android 堆栈将这些字段视为值而非数组。我们正在与 FiRa 合作更新 UCI 规范(CR-1112),以明确指出这些字段应视为数组。

此问题将在 2320XXXX 版本中的 GMS Core 更新中修复。为了从那时起符合 Android 设备的要求,物联网供应商需要修改您的实现,以避免颠倒这些字段的字节顺序。