裝置之間的距離

Android 16 引進了測距模組,可提供統一且標準化的介面,用於裝置之間的測距。您可以使用這個 API 途徑來測量對等裝置的距離和位置,而不需要個別處理每種測距技術。

測距模組支援下列技術:

測距功能和可用性

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 實作的一對多超寬頻工作階段。如果您傳入多個裝置句柄,模組會為每部支援超寬頻的對等裝置建立一對一的工作階段。

進行測距工作階段

如要使用測距模組進行測距工作階段,請按照下列步驟操作:

  1. 確認所有裝置皆搭載 Android 16 以上版本。
  2. 在應用程式資訊清單中要求 android.permission.RANGING 權限
  3. 評估各種技術的功能和可用性。
  4. 探索用於測距作業的對等裝置。
  5. 使用「Ranging 工作階段類型」一節所述的任一工作階段類型,為頻外交換建立連線。
  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 中的範例應用程式。這個範例應用程式涵蓋 Ranging 模組支援的所有測距技術,並包含兩種支援的工作階段類型的流程。