Ultra-wide band communication

Ultra-wide band (UWB) communication is a radio technology focused on precise ranging (measuring the location to an accuracy of 10 cm) between devices. This radio technology can use a low-energy density for short range measurements and perform high-bandwidth signaling over a large portion of the radio spectrum. UWB’s bandwidth is greater than 500 MHz (or exceeding 20% fractional bandwidth).

Controller vs controlee

UWB communication occurs between two devices, where one is a controller and the other is a controlee. The controller determines the complex channel (UwbComplexChannel) that the two devices will share. A controller can handle multiple controlees, but a controlee can only subscribe to a single controller.

Channel selection

For the controlee to start ranging to the controller, the controlee must identify the controller’s local address and complex channel. The local address provides context on the device of the controller, while the complex channel provides context on the ranging session. Both the local address and complex channel rotate after the ranging session ends.

We recommend using Bluetooth Low Energy (BLE) communication to allow the controlee to learn the controller’s local address and complex channel.

Code sample

This code sample initiates and terminates UWB ranging for a controlee:

// 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 = UwbRangingParamters.UWB_CONFIG_ID_1,
        // SessionKeyInfo is used to encrypt the ranging session.
        sessionKeyInfo = null,
        complexChannel = partnerAddress.second,
        peerDevices = listOf(UwbDevice.createForAddress(partnerAddress.first)),
        updateRateType = UwbRangingParamters.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()
    }
}

UWB API

To use the UWB API, follow these steps:

  1. Ensure the device is running on Android 12 or higher.
  2. Ensure the device supports UWB using PackageManager#hasSystemFeature("android.hardware.uwb").
  3. Recommended: Discover UWB capable peer devices using an out-of-band (OOB) mechanism, such as BluetoothLeScanner for BLE scanning.
  4. Recommended: Exchange the local device's address and peer device's address and complex channel to use for the session using an OOB mechanism, such as BluetoothGatt for a BLE GATT connection.
  5. If the user wants to stop the session, cancel the scope of the session.