超宽带 (UWB) 通信

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

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

UWB 通信发生在两台设备之间,其中一个是控制器,另一个是受控设备。控制器会确定两台设备将共用的复杂频道 (UwbComplexChannel),并且是发起方,而被控制方则是响应方。

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

参数范围

控制器和被控制者需要互相识别并传达测距参数,以开始测距。这种交换交由应用使用自己选择的安全带外 (OOB) 机制(例如蓝牙低功耗 [BLE])来实现。

范围参数包括本地地址、复杂渠道和会话键等。请注意,这些参数可能会在测距会话结束后旋转或以其他方式发生变化,并且需要重新通信以重启测距。

步骤

如需使用 UWB API,请按以下步骤操作:

  1. 使用 PackageManager#hasSystemFeature("android.hardware.uwb") 确保 Android 设备搭载 Android 12 或更高版本,并且支持 UWB。
  2. 如果要针对 IoT 设备测距,请确保它们符合 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 客户端的互操作性。此库提供了一种方法来获取可观察或 Flowable 流形式的测距结果,并将 UwbClientSessionScope 检索为单个对象。

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 的移动设备

自 2023 年 4 月起,以下设备支持 Android UWB Jetpack 库:

供应商 设备型号
Google Pixel 6 Pro、Pixel 7 Pro
三星 Galaxy Note 20、S21+、S22+、Z Fold 2、3、4

第三方 SDK

从 2023 年 4 月开始,这些合作伙伴解决方案与当前的 Jetpack 库兼容。

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

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

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

之所以会发生字节顺序反转,是因为 Android 堆栈将这些字段视为值,而不是数组。我们正在与 FiRa 合作,更新 UCI 规范 (CR-1112),明确声明这些字段应被视为数组。

此问题将通过 2320XXXX 版本中的 GMS Core 更新来修复。从那以后,为了与 Android 设备兼容,IoT 供应商需要修改您的实现,以免颠倒这些字段的字节顺序。