设备间测距

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" />

限制和局限性

测距模块可能会因多种原因限制测距,包括以下原因

  • 第三方应用仅允许在超宽带设备上进行后台测距,并且仅限于 受支持的设备。不允许使用其他技术进行后台测距。
  • 当设备达到最大并发测距会话数时,不允许进行测距。
  • 测距可能会因系统健康问题(例如电池、性能或内存)而受到限制。

测距模块还存在以下已知局限性

  • 测距模块仅支持向超宽带对等设备传输测距数据。对于其他技术,测距模块仅向发起方设备传输测距数据。
  • 测距模块仅支持在 raw 测距模式下动态添加设备,并且仅适用于超宽带。
  • 测距模块不支持 默认 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();
    }
}

示例应用

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