Comunicación de banda ultraancha (UWB)

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

Controlador/iniciador frente a controlador/persona

La comunicación con UWB se produce entre dos dispositivos: uno es un controlador y el otro es un controlador. El controlador determina el canal complejo (UwbComplexChannel) que compartirán los dos dispositivos y es el iniciador, mientras que el controlador es el que responde.

Un controlador puede controlar varios Controles, pero un controlador solo puede suscribirse a un solo controlador. Se admiten las configuraciones del controlador/iniciador y del controlador/responsable.

Parámetros de rango

El controlador y el controlador deben identificarse entre sí y comunicar los parámetros de rango para comenzar a hacerlo. Las aplicaciones deben implementar este intercambio mediante un mecanismo seguro fuera de banda (OOB) de su elección, como Bluetooth de bajo consumo (BLE).

Entre los parámetros de rango, se 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 finalice la sesión de rango y se deben volver a comunicar para reiniciar el rango.

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 sean compatibles con UWB mediante PackageManager#hasSystemFeature("android.hardware.uwb").
  2. Si se compara con dispositivos de IoT, asegúrate de que sean compatibles con FiRa MAC 1.3.
  3. Descubre dispositivos de pares compatibles con UWB usando el mecanismo OOB que prefieras, como BluetoothLeScanner.
  4. Intercambia parámetros de rango mediante el mecanismo de OOB seguro que elijas, como BluetoothGatt.
  5. Si el usuario quiere detenerla, cancela su alcance.

Restricciones de uso

Las siguientes restricciones se aplican al uso de la API de UWB:

  1. La app que inicia nuevas sesiones de rango de UWB debe ser una app o un servicio en primer plano.
  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 a 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 UWB de Jetpack, consulta nuestra aplicación de ejemplo en GitHub. Esta app de ejemplo abarca la validación de la compatibilidad con UWB en un dispositivo Android, la habilitación del proceso de descubrimiento mediante un mecanismo OOB y la configuración de UWB entre dos dispositivos compatibles con UWB. En esta muestra, también se abordan casos de uso de control de dispositivos y uso compartido de contenido multimedia.

Rango de UWB

Esta muestra de código inicia y finaliza el rango de UWB para un destinatario:

// 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 los clientes de Java. Esta biblioteca proporciona una forma de obtener resultados de rango como una transmisión observable o Flowable y recuperar el UwbClientSessionScope como un objeto único.

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 el ecosistema

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

Dispositivos móviles compatibles con UWB

A partir de abril de 2023, estos dispositivos serán compatibles con la biblioteca de Android UWB Jetpack:

Proveedor Modelo del dispositivo
Google Pixel 6 Pro y Pixel 7 Pro
Samsung Galaxy Note 20, S21+, S22+, Z Fold2, 3 y 4

SDK de terceros

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

Problema conocido: Se revirtió el orden de bytes para los campos de ID de proveedor de STS estático y la dirección MAC

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

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

La reversión del orden de bytes se produce porque la pila de Android trata estos campos como valores, no como arrays. Estamos trabajando con FiRa para actualizar la especificación de UCI (CR-1112) a fin de indicar explícitamente que estos campos deben tratarse como arreglos.

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