Comunicación de banda ultraancha (UWB)

La comunicación de banda ultraancha es una tecnología de radio enfocada en el rango preciso (medir la ubicación con una precisión de 10 cm) entre dispositivos. Esta tecnología de radio puede usar una densidad de energía baja para mediciones de corto alcance y realizar señalización de gran ancho de banda en una gran parte del espectro de radio. El ancho de banda de UWB es superior a 500 MHz (o supera el 20% del ancho de banda fraccional).

Controlador/iniciador frente a controlado/respondedor

La comunicación UWB se produce entre dos dispositivos, uno de los cuales es un controlador y el otro es un dispositivo controlado. El controlador determina el canal complejo (UwbComplexChannel) que compartirán los dos dispositivos y es el iniciador, mientras que el dispositivo controlado es el responsable de responder.

Un controlador puede controlar varios objetos controlados, pero un objeto controlado solo puede suscribirse a un solo controlador. Se admiten las configuraciones de controlador/iniciador y de controlado/respondedor.

Parámetros de rango

El controlador y el dispositivo controlado deben identificarse entre sí y comunicar los parámetros de rango para comenzar a establecer el rango. Este intercambio se deja a las aplicaciones para que las implementen con un mecanismo fuera de banda (OOB) seguro que elijan, como Bluetooth de bajo consumo (BLE).

Los parámetros de rango incluyen la dirección local, el canal complejo y la clave de sesión, entre otros. Ten en cuenta que estos parámetros pueden rotar o cambiar después de que finaliza la sesión de rango y deben volver a comunicarse para reiniciar el rango.

Rango en segundo plano

Una app que se ejecuta en segundo plano puede iniciar una sesión de medición de UWB si el dispositivo la admite. Para verificar las capacidades de tu dispositivo, consulta RangingCapabilities.

La app no recibe informes de rango cuando se ejecuta en segundo plano, sino cuando pasa a primer plano.

Configuraciones de STS

La app o el servicio aprovisionan una clave de sesión para cada sesión con una secuencia de marcas de tiempo desordenadas (STS). El STS aprovisionado es más seguro que una configuración de STS estática. El STS aprovisionado es compatible con todos los dispositivos compatibles con UWB que ejecutan Android 14 o versiones posteriores.

Categoría de amenaza STS estático STS aprovisionado
Aire: Observador pasivo Mitigado Mitigado
Aire: Ampliación de la señal Mitigado Mitigado
Aire: Ataque de repetición o retransmisión Susceptible Mitigado

Para STS aprovisionado:

  1. Usa el uwbConfigType en RangingParameters que admite STS aprovisionado.

  2. Proporciona la clave de 16 bytes en el campo sessionKeyInfo.

Para STS estáticos:

  1. Usa el uwbConfigType en RangingParameters que admite STS estáticos.

  2. Proporciona la clave de 8 bytes en el campo sessionKeyInfo.

Pasos

Para usar la API de UWB, sigue estos pasos:

  1. Asegúrate de que los dispositivos Android se ejecuten en Android 12 o versiones posteriores, y de que admitan UWB con PackageManager#hasSystemFeature("android.hardware.uwb").
  2. Si realizas mediciones de rango en dispositivos de la IoT, asegúrate de que cumplan con FiRa MAC 1.3.
  3. Descubre dispositivos pares compatibles con UWB con un mecanismo fuera de banda que elijas, como BluetoothLeScanner.
  4. Intercambia los parámetros de rango con un mecanismo seguro fuera de banda que elijas, como BluetoothGatt.
  5. Si el usuario quiere detener la sesión, cancela su alcance.

Restricciones de uso

Se aplican las siguientes restricciones al uso de la API de UWB:

  1. La app que inicia nuevas sesiones de medición de rango UWB debe ser una app o un servicio en primer plano, a menos que se admita el rango en segundo plano, como se ilustró anteriormente.
  2. Cuando la app pasa a segundo plano (mientras la sesión está en curso), es posible que ya no reciba informes de rango. Sin embargo, la sesión de UWB se seguirá manteniendo en las capas inferiores. Cuando la app vuelva al primer plano, se reanudarán los informes de rango.

Muestras de código

App de ejemplo

Para ver un ejemplo de extremo a extremo sobre cómo usar la biblioteca de Jetpack de UWB, consulta nuestra aplicación de ejemplo en GitHub. En esta app de ejemplo, se abarca la validación de la compatibilidad con UWB en un dispositivo Android, la habilitación del proceso de descubrimiento con un mecanismo fuera de banda y la configuración del rango de UWB entre dos dispositivos compatibles con UWB. En la muestra, también se incluyen casos de uso de control de dispositivos y uso compartido de contenido multimedia.

Rango de UWB

En esta muestra de código, se inicia y finaliza el rango UWB para un dispositivo controlado:

// 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()
    }
}

Compatibilidad con RxJava3

La compatibilidad con RxJava3 ahora está disponible para ayudar a lograr la interoperabilidad con clientes de Java. Esta biblioteca proporciona una forma de obtener resultados de rango como un flujo Observable o Flowable, y de recuperar UwbClientSessionScope como un objeto Single.

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();

Compatibilidad con ecosistemas

Estos son los dispositivos de socios y los SDKs de terceros compatibles.

Dispositivos móviles compatibles con UWB

A partir de enero de 2025, estos dispositivos admiten la biblioteca de Jetpack de UWB de Android:

Proveedor Modelo del dispositivo
Google Pixel Pro (6 Pro y modelos posteriores), Fold y Tablet
Motorola Edge 50 Ultra
Samsung Galaxy Note 20, Galaxy Plus y Ultra (S21 y modelos posteriores), Galaxy Z Fold (Fold2 y modelos posteriores)

Nota: El rango de UWB en segundo plano es compatible con todos los dispositivos, excepto los siguientes:

  • Pixel 6 Pro y Pixel 7 Pro.
  • Teléfonos Samsung con Android 13 o versiones anteriores
  • Teléfonos Samsung chinos con Android 14 o versiones anteriores

SDK de terceros

A partir de abril de 2023, estas soluciones de socios son compatibles con la biblioteca actual de Jetpack.

Problema conocido: El orden de bytes se invierte para la dirección MAC y los campos de ID de proveedor de STS estáticos

En Android 13 y versiones anteriores, la pila de UWB de Android invierte de forma incorrecta el orden de bytes de los siguientes campos:

  • Dirección MAC del dispositivo
  • Dirección MAC de destino
  • ID de proveedor de STS estático

La inversión del orden de bytes ocurre porque la pila de Android trata estos campos como valores, no como arrays. Estamos trabajando con FiRa para actualizar la especificación de la UCI (CR-1112) para indicar de forma explícita que estos campos deben tratarse como arrays.

Este problema se solucionará con la actualización de GMS Core en la versión 2320XXXX. Para cumplir con los dispositivos Android a partir de ese momento, los proveedores de IOT deben modificar tu implementación para evitar revertir el orden de bytes de estos campos.