设备之间的范围

Android 16 引入了测距模块,该模块可为设备之间的精确测距提供统一且标准化的接口。您可以使用此 API 表面来测量对等设备的距离和位置,而无需单独处理每种测距技术。

测距模块支持以下技术:

测距功能和可用性

RangingManager 类可为应用提供有关本地设备支持的测距技术的信息,以及每种技术的可用性和功能。应用可以注册 Callback,以接收有关任何受支持技术的可用性或功能发生任何变化的更新。

设备角色

参与测距会话的设备必须是发起方响应方。发起方设备会与一个或多个响应方设备开始测距会话。一个响应器设备一次只能响应一个发起方的测距请求。您可以使用 RangingPreference 类在测距会话中为给定设备指定角色。

测距会话类型

在设备之间开始测距会话时,通常需要建立带外 (OOB) 数据传输来交换会话的参数。

测距模块可以为您处理 OOB 协商,但它也支持自定义 OOB 实现。

图 1. 会话类型的 OOB 流程。

默认 OOB 实现

在此会话类型 (RANGING_SESSION_OOB) 中,测距模块会处理 OOB 协商,以启动测距会话。它会根据应用提供的测距偏好设置选择合适的形参,并根据两款设备都支持的技术使用相应的技术。此类会话使用标准化的 OOB specification

测距模块仅定义用于与对等设备交互的 OOB 数据格式和序列。它不处理对等设备发现或连接建立。

自定义 OOB 实现

在此会话类型 (RANGING_SESSION_RAW) 中,应用会绕过测距模块的 OOB 流程,并处理自己的 OOB 协商和参数。这意味着应用必须确定对等设备支持哪些技术、协商测距参数,然后开始测距会话。

测距偏好设置

使用 RangingPreference 对象指定测距会话的所选参数。您可以执行以下操作:

  • 设备角色。这表示设备将是发起方还是响应方。
  • 测距配置。RangingConfig 对象用于指定测距会话类型以及启动测距会话所需的其他参数。
  • 会话配置。SessionConfig 对象用于指定要对测距会话强制执行的参数,例如测量限制、传感器融合、地理围栏配置等。

测距权限

测距模块需要新的统一权限 (android.permission.RANGING) 才能访问当前和未来的所有测距技术。此权限位于 NEARBY_DEVICES_PERMISSIONS 列表中。

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

限制和局限

测距模块可能会因多种原因(包括以下原因)而限制测距:

  • 第三方应用只能使用超宽带在后台进行测距,且只能在支持的设备上进行。不允许在后台与其他技术一起进行测距。
  • 当设备已达到并发测距会话数量上限时,不允许进行测距。
  • 由于电池、性能或内存等系统健康问题,测距功能可能会受到限制。

测距模块还存在以下已知限制:

  • 测距模块仅支持将测距数据传递给超宽带对等设备。对于其他技术,测距模块仅向发起方设备传送测距数据。
  • 测距模块仅支持在原始测距模式下动态添加设备,且仅适用于超宽带。
  • 对于默认 OOB 实现,测距模块不支持一对多超宽带会话。如果您传入多个设备句柄,模块会为每个支持超宽带的对等设备创建一个一对一会话。

执行测距会话

如需使用测距模块进行测距会话,请按以下步骤操作:

  1. 验证所有设备是否搭载 Android 16 或更高版本。
  2. 在应用清单中请求 android.permission.RANGING 权限
  3. 评估测距技术的功能和可用性。
  4. 发现用于测距操作的对等设备。
  5. 使用测距会话类型中所述的任一会话类型,建立带外交换的连接。
  6. 启动测距并持续获取测距数据。
  7. 终止测距会话。

以下代码示例演示了发起者角色和响应者角色的相应步骤。

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

UWB 与 iOS 设备的互操作性

测距模块支持使用超宽带 (UWB) 与 iOS 设备进行互操作。如需使用 iOS 设备进行测距,您必须使用自定义 OOB 实现,并使用与 Apple Nearby Interaction Accessory Protocol 相匹配的特定参数来配置测距会话。如需查看参考信息,请参阅示例应用

创建 RangingPreference 时,请使用 RawRangingDeviceUwbRangingParams 指定配置。以下参数对于实现 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 下行链路 TDoA API

对于 UWB DL-TDoA API,请参阅 Android 17 新功能

示例应用

如需查看有关如何使用测距模块的端到端示例,请参阅 AOSP 中的示例应用。此示例应用涵盖了 Ranging 模块支持的所有测距技术,并包含两种受支持会话类型的流程。

UWB 与 iOS 设备的互操作性

Android 示例应用支持与 iOS 示例应用一起启动 UWB 测距会话。

图 2. Android 和 iOS UWB 使用情况。