超寬頻 (UWB) 通訊

超寬頻通訊是一種無線電技術,著重於精確測量裝置之間的距離 (即測量位置的精確度為 10 公分)。這項無線電技術可使用低能量密度測量短程範圍,並針對大部分無線電頻譜執行高頻寬訊號。UWB 的頻寬大於 500 MHz (或超過 20% 小數頻寬)。

控管者/發起人與控制者/回應者

兩部裝置之間發生 UWB 通訊,其中一部裝置為控制器,另一部裝置為 Controlee。控制器會決定兩部裝置共用的複雜通道 (UwbComplexChannel),而控制組是回應者,而後者是啟動者。

一個控制器可以處理多個 Controlee,但一個控制器只能訂閱一個控制器。同時支援控管者/啟動者和控制者/回應者設定。

排序參數

控制器和控制層需要識別彼此並傳送參數,才能開始測定範圍。這種交換會留給應用程式使用自行選擇的安全頻外 (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. 如果範圍涵蓋 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 用戶端的互通性。這個程式庫可讓您取得以可觀測或可流式串流形式取得的結果範圍,並將 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 的行動裝置

自 2024 年 3 月起,以下裝置支援 Android UWB Jetpack 程式庫:

供應商 裝置型號
Google Pixel 6 Pro、7 Pro、8 Pro、Fold、Tablet
Samsung Galaxy Note 20、S21+、S22+、S23+、S24+ Z Fold 2、3、4、5

第三方 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 供應商必須修改您的實作方式,以免反轉這些欄位的位元組順序。