Android 16 では、デバイス間の正確な測距のための統一された標準化されたインターフェースを提供する測距モジュールが導入されています。この API サーフェスを使用すると、各測距技術を個別に処理しなくても、ピアデバイスの距離と位置を測定できます。
測距モジュールは、次のテクノロジーをサポートしています。
- 超広帯域無線
- Bluetooth チャンネルの測定
- Wi-Fi NAN RTT
- Bluetooth RSSI 測距
測距機能と可用性
RangingManager クラスは、ローカル デバイスでサポートされている測距技術に関する情報と、各技術の可用性と機能をアプリに提供します。アプリは Callback に登録して、サポートされているテクノロジーの可用性や機能の変更に関する最新情報を受け取ることができます。
デバイスのロール
測距セッションに参加するデバイスは、イニシエーターまたはレスポンダーのいずれかである必要があります。開始デバイスは、1 つ以上の応答デバイスとの測距セッションを開始します。レスポンダー デバイスは、一度に 1 つのイニシエータからの測距リクエストにのみ応答します。測距セッションで特定のデバイスのロールを指定する場合は、RangingPreference クラスを使用します。
測位セッションのタイプ
デバイス間の測距セッションを開始する場合は、多くの場合、セッションのパラメータを交換するために、アウトオブバンド(OOB)データ転送を確立する必要があります。
測距モジュールは OOB ネゴシエーションを処理できますが、カスタム 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" />
制限事項
測距モジュールは、次のような理由で測距を制限することがあります。
- サードパーティ製アプリは、超広帯域無線通信によるバックグラウンド測定を、サポートされているデバイスでのみ行うことができます。他の技術でバックグラウンドで測距することはできません。
- デバイスあたりの同時実行レンジング セッションの最大数に達している場合、測距は許可されません。
- バッテリー、パフォーマンス、メモリなどのシステムの健全性に関する懸念から、測距が制限される場合があります。
また、Ranging モジュールには次の既知の制限事項があります。
- 測距モジュールは、ウルトラワイドバンドのピアデバイスへの測距データの配信のみをサポートしています。他のテクノロジーの場合、測距モジュールは測距データをイニシエーター デバイスにのみ提供します。
- 測距モジュールは、未加工の測距モードでデバイスの動的追加のみをサポートし、超広帯域でのみサポートします。
- 測距モジュールは、デフォルトの OOB 実装の 1 対多の超広帯域無線セッションをサポートしていません。複数のデバイス ハンドルを渡すと、モジュールは超広帯域をサポートするピアデバイスごとに 1 対 1 セッションを作成します。
測距セッションを実施する
Ranging モジュールを使用して測距セッションを行う手順は次のとおりです。
- すべてのデバイスが Android 16 以降で動作していることを確認します。
- アプリ マニフェストで android.permission.RANGING権限をリクエストします。
- 測距技術の機能と可用性を評価する。
- 測距オペレーション用のピアデバイスを検出します。
- 測距セッション タイプで説明されているいずれかのセッション タイプを使用して、アウトオブバンド交換の接続を確立します。
- 測距を開始し、測距データを継続的に取得します。
- 測距セッションを終了します。
次のコードサンプルは、イニシエーター ロールとレスポンダー ロールの両方について、これらの手順を示しています。
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 のサンプルアプリをご覧ください。このサンプルアプリは、測距モジュールでサポートされているすべての測距技術を網羅しており、サポートされている両方のセッション タイプのフローを含んでいます。
