超寬頻 (UWB) 通訊

超寬頻通訊是一種無線電技術,專門用來在裝置之間精確設限,測量位置的精確度可達 10 公分。這項無線電技術可以使用低功耗密度進行短範圍測量,並針對大多數無線電頻譜執行高頻寬訊號。UWB 的頻寬大於 500 MHz (或超過 20% 小數頻寬)。

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

兩部裝置之間會進行 UWB 通訊,其中一部是控制器,另一部裝置為控制層。控制器會決定兩個裝置要共用的複雜通道 (UwbComplexChannel),且會成為發起人,而控制者是回應者。

控制器可以處理多個控制層,但控制組只能訂閱一個控制器。同時支援控制器/發起者和受控者/回應者設定。

選擇參數

控制器和控制組需要識別彼此,並傳達隨機參數以開始測距。此交換由應用程式透過自行選擇的安全架構外 (OOB) 機制實作,例如藍牙低功耗 (BLE)。

隨機參數包括本機位址、複雜管道和工作階段索引鍵等。請注意,這些參數可能會在測距工作階段結束後旋轉或變更,且需要再次通訊以重新啟動範圍。

操作步驟

如要使用 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 的行動裝置

自 2023 年 4 月起,下列裝置均支援 Android UWB Jetpack 程式庫:

供應商 裝置型號
Google Pixel 6 Pro、Pixel 7 Pro
Samsung 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 更新修正。 從那時起,IOT 供應商必須修改您的實作項目,以免對這些欄位的位元組排序顯示反轉,才能符合 Android 裝置規範。