Alcance entre dispositivos

O Android 16 introduz o módulo Ranging, que fornece uma interface unificada e padronizada para alcance preciso entre dispositivos. Você pode usar essa superfície de API para medir a distância e a posição de dispositivos semelhantes sem precisar processar cada tecnologia de alcance individualmente.

O módulo de alcance é compatível com as seguintes tecnologias:

Recursos e disponibilidade de intervalos

A classe RangingManager fornece aos apps informações sobre as tecnologias de alcance compatíveis com o dispositivo local, além da disponibilidade e dos recursos de cada tecnologia. Os apps podem se registrar para receber um Callback e receber atualizações sobre mudanças na disponibilidade ou nos recursos de qualquer tecnologia compatível.

Funções do dispositivo

Um dispositivo que participa de uma sessão de alcance precisa ser um iniciador ou um respondente. O dispositivo iniciador começa a sessão de alcance com um ou mais dispositivos de resposta. Um dispositivo de resposta responde a solicitações de alcance de apenas um iniciador por vez. É possível especificar a função de um determinado dispositivo em uma sessão de intervalo com a classe RangingPreference.

Tipos de sessão de intervalo

Ao iniciar uma sessão de alcance entre dispositivos, muitas vezes é necessário estabelecer um transporte de dados fora da banda (OOB) para trocar parâmetros da sessão.

O módulo Ranging pode processar negociações fora da banda para você, mas também oferece suporte a implementações personalizadas.

Figura 1. Fluxo OOB para tipos de sessão.

Implementação padrão de OOB

Nesse tipo de sessão (RANGING_SESSION_OOB), o módulo de alcance processa negociações fora de banda para iniciar uma sessão de alcance. Ele seleciona parâmetros adequados com base nas preferências de alcance fornecidas pelo app e usa as tecnologias apropriadas com base no que os dois dispositivos oferecem suporte. Esse tipo de sessão usa um OOB specification padronizado.

O módulo Ranging define apenas o formato de dados e a sequência OOB a serem usados para interagir com um dispositivo pareado. Ele não processa a descoberta de dispositivos pareados nem o estabelecimento de conexão.

Implementação personalizada de OOB

Nesse tipo de sessão (RANGING_SESSION_RAW), o app ignora o fluxo OOB do módulo Ranging e processa a própria negociação e os parâmetros OOB. Isso significa que o app precisa determinar quais tecnologias o dispositivo pareado aceita, negociar parâmetros de alcance e iniciar a sessão de alcance.

Preferências de intervalo

Use um objeto RangingPreference para especificar os parâmetros selecionados de uma sessão de intervalo. Isso inclui o seguinte:

  • Função do dispositivo. Isso indica se o dispositivo será o iniciador ou o respondente.
  • Configuração de alcance. Um objeto RangingConfig especifica o tipo de sessão de alcance e outros parâmetros necessários para iniciar uma sessão.
  • Configuração da sessão. Um objeto SessionConfig especifica parâmetros a serem aplicados na sessão de alcance, como limite de medição, fusão de sensores, configuração de fronteira geográfica virtual e muito mais.

Permissão de intervalo

O módulo de alcance requer uma nova permissão unificada (android.permission.RANGING) para acessar todas as tecnologias de alcance atuais e futuras. Essa permissão está na lista NEARBY_DEVICES_PERMISSIONS.

<uses-permission android:name="android.permission.RANGING" />

Restrições e limitações

O módulo de alcance pode restringir o alcance por vários motivos, incluindo os seguintes:

  • Os apps de terceiros só podem realizar a estimativa de alcance em segundo plano com banda ultralarga e apenas em dispositivos compatíveis. Não é permitido fazer medições em segundo plano com outras tecnologias.
  • Não é possível fazer medições quando o número máximo de sessões simultâneas por dispositivo foi atingido.
  • A medição de distância pode ser restrita devido a problemas de integridade do sistema, como bateria, desempenho ou memória.

O módulo Ranging também tem as seguintes limitações conhecidas:

  • O módulo de alcance só é compatível com a entrega de dados de alcance a dispositivos pareados para banda ultralarga. Para outras tecnologias, o módulo Ranging só entrega dados de alcance ao dispositivo iniciador.
  • O módulo de alcance só aceita a adição dinâmica de dispositivos no modo de alcance bruto e apenas para banda ultralarga.
  • O módulo de alcance não oferece suporte a sessões de banda ultralarga de um para muitos para implementações OOB padrão. Se você transmitir vários identificadores de dispositivo, o módulo vai criar uma sessão individual para cada dispositivo pareado que seja compatível com banda ultralarga.

Fazer uma sessão de alcance

Para realizar uma sessão de alcance usando o módulo Ranging, siga estas etapas:

  1. Verifique se todos os dispositivos estão executando o Android 16 ou uma versão mais recente.
  2. Solicite a permissão android.permission.RANGING no manifesto do app.
  3. Avalie os recursos e a disponibilidade das tecnologias de alcance.
  4. Descobre um dispositivo pareado para operações de alcance.
  5. Estabeleça uma conexão para uma troca fora de banda usando um dos tipos de sessão descritos em Tipos de sessão de alcance.
  6. Inicie a medição e adquira dados de medição continuamente.
  7. Encerre a sessão de alcance.

O exemplo de código a seguir demonstra essas etapas para as funções de iniciador e de respondente.

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

Interoperabilidade da UWB com dispositivos iOS

O módulo de alcance oferece suporte à interoperabilidade com dispositivos iOS usando banda ultralarga (UWB). Para usar um dispositivo iOS, é necessário usar uma implementação OOB personalizada e configurar a sessão de alcance com parâmetros específicos que correspondam ao Protocolo de acessórios de interação por proximidade da Apple. Consulte o app de exemplo para referência.

Ao criar o RangingPreference, use RawRangingDevice e UwbRangingParams para especificar a configuração. Os parâmetros a seguir são cruciais para a interoperabilidade do iOS:

  • ID da configuração:use UwbRangingParams.CONFIG_UNICAST_DS_TWR.
  • Informações da chave de sessão:forneça uma matriz de bytes que contenha o ID do fornecedor e o IV estático do STS.
  • Canal complexo:defina o número do canal e o índice de pré-âmbulo para corresponder à configuração do dispositivo iOS.

O snippet de código a seguir demonstra como criar um RangingPreference para um dispositivo iniciador que varia com um responder iOS:

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

API UWB Downlink-TDoA

Para a API UWB DL-TDoA, consulte Novos recursos do Android 17.

App de exemplo

Para um exemplo completo de como usar o módulo de alcance, consulte o app de exemplo no AOSP. Este app de exemplo abrange todas as tecnologias de alcance compatíveis com o módulo Ranging e inclui fluxos para os dois tipos de sessão compatíveis.

Interoperabilidade da UWB com dispositivos iOS

O app de exemplo do Android permite iniciar uma sessão de alcance de UWB com o app de exemplo do iOS.

Figura 2. Uso de UWB no Android e iOS.