Android 16 推出 Ranging 模組,提供統一且標準化的介面,方便裝置間進行精確測距。您可以使用這個 API 介面測量對等裝置的距離和位置,不必個別處理每項測距技術。
Ranging 模組支援下列技術:
- 超寬頻
- 藍牙聲道發出聲音
- Wi-Fi NAN RTT
- 藍牙 RSSI 測距
測距功能和可用性
RangingManager 類別會向應用程式提供本機裝置支援的測距技術相關資訊,以及每項技術的可用性和功能。應用程式可以註冊 Callback,接收任何支援技術的可用性或功能異動更新。
裝置角色
參與測距工作階段的裝置必須是發起者或回應者。發起端裝置會與一或多個回應端裝置啟動測距工作階段。回應器裝置一次只能回應一個啟動器的測距要求。您可以使用 RangingPreference 類別,在測距工作階段中指定特定裝置的角色。
測距工作階段類型
在裝置之間啟動測距工作階段時,通常需要建立頻外 (OOB) 資料傳輸,才能交換工作階段的參數。
Ranging 模組可以為您處理 OOB 協商,但同時也支援自訂 OOB 實作。

預設 OOB 實作方式
在這種工作階段類型 (RANGING_SESSION_OOB) 中,Ranging 模組會處理 OOB 協商,以啟動測距工作階段。系統會根據應用程式提供的測距偏好設定選取合適的參數,並根據兩部裝置支援的技術,使用適當的技術。這個工作階段類型使用標準化的 OOB specification。
Ranging 模組只會定義用於與對等裝置互動的 OOB 資料格式和序列。不會處理對等裝置探索或連線建立作業。
自訂 OOB 實作
在這種工作階段類型 (RANGING_SESSION_RAW) 中,應用程式會略過 Ranging 模組的 OOB 流程,並處理自己的 OOB 協商和參數。也就是說,應用程式必須判斷對等裝置支援哪些技術、協商測距參數,然後開始測距工作階段。
測距偏好設定
使用 RangingPreference 物件,指定範圍工作階段的所選參數。此時你應該可以執行下列操作:
- 裝置角色。這表示裝置是啟動器還是回應器。
- 測距設定。
RangingConfig物件會指定測距工作階段類型,以及啟動測距工作階段所需的其他參數。 - 工作階段設定:
SessionConfig物件會指定要在測距工作階段強制執行的參數,例如測量限制、感應器融合、地理圍欄設定等。
測距權限
測距模組需要新的統一權限 (android.permission.RANGING),才能存取目前和日後的所有測距技術。這項權限位於 NEARBY_DEVICES_PERMISSIONS 清單中。
<uses-permission android:name="android.permission.RANGING" />
限制
測距模組可能會因多種原因限制測距,包括:
- 第三方應用程式只能使用超寬頻執行背景測距,且僅限支援的裝置。不得使用其他技術在背景測距。
- 裝置的並行測距工作階段數量已達上限,因此無法進行測距。
- 如果系統健康狀態不佳 (例如電池、效能或記憶體問題),測距功能可能會受到限制。
此外,Ranging 模組也有下列已知限制:
- Ranging 模組僅支援將測距資料傳送至同層級裝置,以供超寬頻使用。如果是其他技術,Ranging 模組只會將測距資料傳送至發起端裝置。
- 測距模組僅支援在原始測距模式中動態新增裝置,且僅適用於超寬頻。
- 對於預設 OOB 實作,Ranging 模組不支援一對多超寬頻工作階段。如果傳遞多個裝置控制代碼,模組會為每個支援超寬頻的對等互連裝置建立一對一的會期。
進行測距工作階段
如要使用 Ranging 模組進行測距工作階段,請按照下列步驟操作:
- 確認所有裝置都搭載 Android 16 以上版本。
- 在應用程式資訊清單中要求
android.permission.RANGING權限。 - 評估測距技術的功能和可用性。
- 探索用於測距作業的對等互連裝置。
- 使用「測距工作階段類型」一節所述的任一工作階段類型,建立頻帶外交換的連線。
- 啟動測距功能,並持續取得測距資料。
- 終止測距工作階段。
以下程式碼範例會針對啟動器和回應器角色,示範這些步驟。
Kotlin
class RangingApp {
// Starts a ranging session on the initiator side.
fun startRangingInitiator(
context: Context,
deviceHandle: DeviceHandle,
executor: Executor,
callback: RangingSessionCallback
) {
// Get the RangingManager which is the entry point for ranging module.
val manager = context.getSystemService(RangingManager::class.java)
// Create a new RangingSession using the provided executor and callback.
val session = manager.createRangingSession(executor, callback)
// Create an OobInitiatorRangingConfig, which specifies the ranging parameters for
// the initiator role.
val config = OobInitiatorRangingConfig.Builder()
.setFastestRangingInterval(Duration.ofMillis(100))
.setSlowestRangingInterval(Duration.ofMillis(5000))
.setRangingMode(RANGING_MODE_AUTO)
.setSecurityLevel(SECURITY_LEVEL_BASIC)
.addDeviceHandle(deviceHandle)
.build()
// Create a RangingPreference, which specifies the role (initiator) and
// configuration for the ranging session.
val preference =
RangingPreference.Builder(DEVICE_ROLE_INITIATOR, config).build()
// Start ranging session.
session.start(preference)
// If successful, the ranging data will be sent through callback#onResults
// Stop ranging session
session.stop()
}
// Starts a ranging session on the responder side.
fun startRangingResponder(
context: Context,
deviceHandle: DeviceHandle,
executor: Executor,
callback: RangingSessionCallback
) {
// Get the RangingManager which is the entry point for ranging module.
val manager = context.getSystemService(RangingManager::class.java)
// Create a new RangingSession using the provided executor and callback.
val session = manager.createRangingSession(executor, callback)
// Create an OobResponderRangingConfig, which specifies the ranging parameters for
// the responder role.
val config = OobResponderRangingConfig.Builder(deviceHandle).build()
// Create a RangingPreference, which specifies the role (responder) and
// configuration for the ranging session.
val preference =
RangingPreference.Builder(DEVICE_ROLE_RESPONDER, config).build()
// Start the ranging session.
session.start(preference)
// Stop the ranging session
session.stop()
}
}
Java
public class RangingApp {
// Starts a ranging session on the initiator side.
void startRangingInitiator(Context context, DeviceHandle deviceHandle, Executor executor, RangingSessionCallback callback) {
// Get the RangingManager which is the entry point for ranging module.
RangingManager manager = context.getSystemService(RangingManager.class);
// Create a new RangingSession using the provided executor and callback.
RangingSession session = manager.createRangingSession(executor, callback);
// Create an OobInitiatorRangingConfig, which specifies the ranging parameters for
// the initiator role.
OobInitiatorRangingConfig config = new OobInitiatorRangingConfig.Builder()
.setFastestRangingInterval(Duration.ofMillis(100))
.setSlowestRangingInterval(Duration.ofMillis(5000))
.setRangingMode(RANGING_MODE_AUTO)
.setSecurityLevel(SECURITY_LEVEL_BASIC)
.addDeviceHandle(deviceHandle)
.build();
// Create a RangingPreference, which specifies the role (initiator) and
// configuration for the ranging session.
RangingPreference preference =
new RangingPreference.Builder(DEVICE_ROLE_INITIATOR, config).build();
// Start ranging session.
session.start(preference);
// If successful, the ranging data will be sent through callback#onResults
// Stop ranging session
session.stop();
}
// Starts a ranging session on the responder side.
void startRangingResponder(Context context, DeviceHandle deviceHandle, Executor executor, RangingSessionCallback callback) {
// Get the RangingManager which is the entry point for ranging module.
RangingManager manager = context.getSystemService(RangingManager.class);
// Create a new RangingSession using the provided executor and callback.
RangingSession session = manager.createRangingSession(executor, callback);
// Create an OobResponderRangingConfig, which specifies the ranging parameters for
// the responder role.
OobResponderRangingConfig config = new OobResponderRangingConfig.Builder( deviceHandle).build();
// Create a RangingPreference, which specifies the role (responder) and
// configuration for the ranging session.
RangingPreference preference =
new RangingPreference.Builder(DEVICE_ROLE_RESPONDER, config).build();
// Start the ranging session.
session.start(preference);
// Stop the ranging session
session.stop();
}
}
與 iOS 裝置的 UWB 互通性
測距模組支援使用超寬頻 (UWB) 與 iOS 裝置互通。如要使用 iOS 裝置測距,您必須自訂 OOB 實作,並使用符合 Apple Nearby Interaction Accessory Protocol 的特定參數設定測距工作階段。如需參考,請參閱範例應用程式。
建立 RangingPreference 時,請使用 RawRangingDevice 和 UwbRangingParams 指定設定。下列參數對 iOS 互通性至關重要:
- 設定 ID:使用
UwbRangingParams.CONFIG_UNICAST_DS_TWR。 - 工作階段金鑰資訊:提供包含供應商 ID 和靜態 STS IV 的位元組陣列。
- 複雜通道:設定通道號碼和前置碼索引,與 iOS 裝置的設定相符。
下列程式碼片段示範如何為與 iOS 回應器進行測距的發起端裝置建立 RangingPreference:
Kotlin
// Create UwbRangingParams with iOS-specific configuration
val uwbRangingParams = UwbRangingParams.Builder(
sessionId,
UwbRangingParams.CONFIG_UNICAST_DS_TWR,
sourceAddress,
destinationAddress
)
.setComplexChannel(
UwbComplexChannel.Builder()
.setChannel(channelNumber)
.setPreambleIndex(preambleIndex)
.build()
)
.setRangingUpdateRate(updateRate)
.setSessionKeyInfo(sessionKeyInfo) // Vendor ID + STS IV
.setSlotDuration(slotDuration)
.build()
// Create RawRangingDevice
val rawRangingDevice = RawRangingDevice.Builder()
.setRangingDevice(RangingDevice.Builder().build())
.setUwbRangingParams(uwbRangingParams)
.build()
// Create SessionConfig
val sessionConfig = SessionConfig.Builder()
.setAngleOfArrivalNeeded(true)
.setDataNotificationConfig(DataNotificationConfig.Builder()
.setNotificationConfigType(DataNotificationConfig.NOTIFICATION_CONFIG_ENABLE)
.build())
.build()
// Create RangingPreference for the initiator
val preference = RangingPreference.Builder(
RangingPreference.DEVICE_ROLE_INITIATOR,
RawInitiatorRangingConfig.Builder()
.addRawRangingDevice(rawRangingDevice)
.build()
)
.setSessionConfig(sessionConfig)
.build()
Java
// Create UwbRangingParams with iOS-specific configuration
UwbRangingParams uwbRangingParams = new UwbRangingParams.Builder(
sessionId,
UwbRangingParams.CONFIG_UNICAST_DS_TWR,
sourceAddress,
destinationAddress)
.setComplexChannel(new UwbComplexChannel.Builder()
.setChannel(channelNumber)
.setPreambleIndex(preambleIndex)
.build())
.setRangingUpdateRate(updateRate)
.setSessionKeyInfo(sessionKeyInfo) // Vendor ID + STS IV
.setSlotDuration(slotDuration)
.build();
// Create RawRangingDevice
RawRangingDevice rawRangingDevice = new RawRangingDevice.Builder()
.setRangingDevice(new RangingDevice.Builder().build())
.setUwbRangingParams(uwbRangingParams)
.build();
// Create SessionConfig
SessionConfig sessionConfig = new SessionConfig.Builder()
.setAngleOfArrivalNeeded(true)
.setDataNotificationConfig(new DataNotificationConfig.Builder()
.setNotificationConfigType(DataNotificationConfig.NOTIFICATION_CONFIG_ENABLE)
.build())
.build();
// Create RangingPreference for the initiator
RangingPreference preference = new RangingPreference.Builder(
RangingPreference.DEVICE_ROLE_INITIATOR,
new RawInitiatorRangingConfig.Builder()
.addRawRangingDevice(rawRangingDevice)
.build())
.setSessionConfig(sessionConfig)
.build();
UWB Downlink-TDoA API
如要瞭解 UWB DL-TDoA API,請參閱「Android 17 新功能」。
範例應用程式
如需如何使用 Ranging 模組的端對端範例,請參閱 AOSP 中的範例應用程式。這個範例應用程式涵蓋 Ranging 模組支援的所有測距技術,並包含兩種支援的會期類型流程。
與 iOS 裝置的 UWB 互通性
Android 範例應用程式支援使用 iOS 範例應用程式啟動 UWB 測距工作階段。