设备之间的范围

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

测距模块支持以下技术:

测距功能和可用性

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

设备角色

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

范围会话类型

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

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

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

默认 OOB 实现

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

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

自定义 OOB 实现

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

测距偏好设置

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

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

测距权限

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

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

限制和局限

测距模块可能会出于多种原因限制测距,其中包括:

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

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

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

进行测距会话

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

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

示例应用

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