超寬頻通訊是一種無線電技術,著重於精確測量裝置之間的距離 (即測量位置的精確度為 10 公分)。這項無線電技術可使用低能量密度測量短程範圍,並針對大部分無線電頻譜執行高頻寬訊號。UWB 的頻寬大於 500 MHz (或超過 20% 小數頻寬)。
控管者/發起人與控制者/回應者
兩部裝置之間發生 UWB 通訊,其中一部裝置為控制器,另一部裝置為 Controlee。控制器會決定兩部裝置共用的複雜通道 (UwbComplexChannel
),而控制組是回應者,而後者是啟動者。
一個控制器可以處理多個 Controlee,但一個控制器只能訂閱一個控制器。同時支援控管者/啟動者和控制者/回應者設定。
排序參數
控制器和控制層需要識別彼此並傳送參數,才能開始測定範圍。這種交換會留給應用程式使用自行選擇的安全頻外 (OOB) 機制實作,例如藍牙低功耗 (BLE)。
隨機參數包括本機位址、複雜管道和工作階段鍵等。請注意,這些參數可能會在範圍工作階段結束之後旋轉或變更,因此需要重新通訊才能重新啟動範圍。
背景範圍
在背景執行的應用程式可在裝置支援的情況下啟動 UWB 範圍工作階段。如要查看裝置功能,請參閱 RangingCapabilities
。
應用程式在背景執行時不會收到範圍報告;應用程式移至前景時會收到許多報告。
STS 設定
應用程式或服務使用經學習的時間戳記序列 (STS),為每個工作階段佈建工作階段金鑰。已佈建的 STS 比靜態 STS 設定更安全。所有搭載 Android 14 以上版本且支援 UWB 的裝置都支援佈建的 STS。
威脅類別 | 靜態 STS | 建議的 STS |
---|---|---|
空氣:被動觀察器 | 已處理 | 已處理 |
空氣:訊號放大 | 已處理 | 已處理 |
空中:重播/轉發攻擊 | 容易理解 | 已處理 |
針對已佈建的 STS:
在支援佈建 STS 的
RangingParameters
中使用uwbConfigType
。在
sessionKeyInfo
欄位中提供 16 位元組的金鑰。
靜態 STS:
在支援靜態 STS 的
RangingParameters
中使用uwbConfigType
。在
sessionKeyInfo
欄位中提供 8 位元組的金鑰。
操作步驟
如要使用 UWB API,請按照下列步驟操作:
- 請確保 Android 裝置在 Android 12 以上版本中執行,且支援使用
PackageManager#hasSystemFeature("android.hardware.uwb")
執行 UWB。 - 如果範圍涵蓋 IoT 裝置,請確認這些裝置符合 FiRa MAC 1.3 標準。
- 使用您選擇的 OOB 機制 (例如
BluetoothLeScanner
),探索支援 UWB 的同類裝置。 - 使用您選擇的安全 OOB 機制交換參數,例如
BluetoothGatt
。 - 如果使用者想要停止工作階段,請取消工作階段範圍。
使用限制
以下限制適用於 UWB API 的使用:
- 應用程式啟動新的 UWB 範圍的工作階段必須是前景應用程式或服務,除非系統支援背景範圍 (如上圖所示)。
- 當應用程式移至背景 (工作階段執行時) 時,應用程式可能無法再接收範圍報告。不過,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 程式庫:
供應商 | 裝置型號 |
---|---|
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 程式庫相容。
- Estimote UWB Development Kit 的依據。
- Mobile Knowledge MK UWB Kit Mobile Edition 2.0。
已知問題:MAC 位址和靜態 STS 供應商 ID 欄位的位元組順序顛倒
在 Android 13 以下版本中,Android UWB 堆疊會錯誤反轉下列欄位的位元組順序:
- 裝置的 MAC 位址
- 目的地 MAC 位址
- 靜態 STS 供應商 ID
由於 Android 堆疊會將這些欄位視為值,而不是陣列,因此位元組順序相反。我們正在與 FiRa 合作更新 UCI 規格 (CR-1112),明確指出這些欄位應視為陣列。
這個問題會透過 2320XXXX
版本的 GMS Core 更新來修正。
從那時起,為確保與 Android 裝置相容,IoT 供應商必須修改您的實作方式,以免反轉這些欄位的位元組順序。