[go: nahoru, domu]

Adding implementation for Uwb ranging

Relnote: Initial implementation of the uwb library
Test: /gradlew core:uwb:uwb:cAT
Change-Id: Ic9b835e9e88b01856a2e0d2f31ddee4746e10bee
diff --git a/core/uwb/uwb/api/current.txt b/core/uwb/uwb/api/current.txt
index e6f50d0..0b2add9 100644
--- a/core/uwb/uwb/api/current.txt
+++ b/core/uwb/uwb/api/current.txt
@@ -1 +1,165 @@
 // Signature format: 4.0
+package androidx.core.uwb {
+
+  public final class RangingCapabilities {
+    ctor public RangingCapabilities(boolean supportsDistance, boolean supportsAzimuthalAngle, boolean supportsElevationAngle);
+    method public boolean getSupportsAzimuthalAngle();
+    method public boolean getSupportsDistance();
+    method public boolean getSupportsElevationAngle();
+    property public final boolean supportsAzimuthalAngle;
+    property public final boolean supportsDistance;
+    property public final boolean supportsElevationAngle;
+  }
+
+  public final class RangingMeasurement {
+    ctor public RangingMeasurement(float value);
+    method public float getValue();
+    property public final float value;
+  }
+
+  public final class RangingParameters {
+    ctor public RangingParameters(int uwbConfigId, int sessionId, byte[]? sessionKeyInfo, androidx.core.uwb.UwbComplexChannel? complexChannel, java.util.List<androidx.core.uwb.UwbDevice> peerDevices, int updateRate);
+    method public androidx.core.uwb.UwbComplexChannel? getComplexChannel();
+    method public java.util.List<androidx.core.uwb.UwbDevice> getPeerDevices();
+    method public int getSessionId();
+    method public byte[]? getSessionKeyInfo();
+    method public int getUpdateRate();
+    method public int getUwbConfigId();
+    property public final androidx.core.uwb.UwbComplexChannel? complexChannel;
+    property public final java.util.List<androidx.core.uwb.UwbDevice> peerDevices;
+    property public final int sessionId;
+    property public final byte[]? sessionKeyInfo;
+    property public final int updateRate;
+    property public final int uwbConfigId;
+    field public static final androidx.core.uwb.RangingParameters.Companion Companion;
+    field public static final int RANGING_UPDATE_RATE_AUTOMATIC;
+    field public static final int RANGING_UPDATE_RATE_FREQUENT;
+    field public static final int RANGING_UPDATE_RATE_INFREQUENT;
+  }
+
+  public static final class RangingParameters.Companion {
+  }
+
+  public final class RangingPosition {
+    ctor public RangingPosition(androidx.core.uwb.RangingMeasurement distance, androidx.core.uwb.RangingMeasurement? azimuth, androidx.core.uwb.RangingMeasurement? elevation, long elapsedRealtimeNanos);
+    method public androidx.core.uwb.RangingMeasurement? getAzimuth();
+    method public androidx.core.uwb.RangingMeasurement getDistance();
+    method public long getElapsedRealtimeNanos();
+    method public androidx.core.uwb.RangingMeasurement? getElevation();
+    property public final androidx.core.uwb.RangingMeasurement? azimuth;
+    property public final androidx.core.uwb.RangingMeasurement distance;
+    property public final long elapsedRealtimeNanos;
+    property public final androidx.core.uwb.RangingMeasurement? elevation;
+  }
+
+  public abstract class RangingResult {
+    ctor public RangingResult();
+    method public abstract androidx.core.uwb.UwbDevice getDevice();
+    property public abstract androidx.core.uwb.UwbDevice device;
+  }
+
+  public final class RangingResultPeerDisconnected extends androidx.core.uwb.RangingResult {
+    ctor public RangingResultPeerDisconnected(androidx.core.uwb.UwbDevice device);
+    method public androidx.core.uwb.UwbDevice getDevice();
+    property public androidx.core.uwb.UwbDevice device;
+  }
+
+  public final class RangingResultPosition extends androidx.core.uwb.RangingResult {
+    ctor public RangingResultPosition(androidx.core.uwb.UwbDevice device, androidx.core.uwb.RangingPosition position);
+    method public androidx.core.uwb.UwbDevice getDevice();
+    method public androidx.core.uwb.RangingPosition getPosition();
+    property public androidx.core.uwb.UwbDevice device;
+    property public final androidx.core.uwb.RangingPosition position;
+  }
+
+  public final class UwbAddress {
+    ctor public UwbAddress(byte[] address);
+    ctor public UwbAddress(String address);
+    method public byte[] getAddress();
+    property public final byte[] address;
+    field public static final androidx.core.uwb.UwbAddress.Companion Companion;
+  }
+
+  public static final class UwbAddress.Companion {
+  }
+
+  public interface UwbClientSessionScope {
+    method public androidx.core.uwb.UwbAddress getLocalAddress();
+    method public androidx.core.uwb.RangingCapabilities getRangingCapabilities();
+    method public kotlinx.coroutines.flow.Flow<androidx.core.uwb.RangingResult> initSession(androidx.core.uwb.RangingParameters parameters);
+    property public abstract androidx.core.uwb.UwbAddress localAddress;
+    property public abstract androidx.core.uwb.RangingCapabilities rangingCapabilities;
+  }
+
+  public final class UwbComplexChannel {
+    ctor public UwbComplexChannel(int channel, int preambleIndex);
+    method public int getChannel();
+    method public int getPreambleIndex();
+    property public final int channel;
+    property public final int preambleIndex;
+  }
+
+  public interface UwbControleeSessionScope extends androidx.core.uwb.UwbClientSessionScope {
+  }
+
+  public final class UwbDevice {
+    ctor public UwbDevice(androidx.core.uwb.UwbAddress address);
+    method public static androidx.core.uwb.UwbDevice createForAddress(String address);
+    method public static androidx.core.uwb.UwbDevice createForAddress(byte[] address);
+    method public androidx.core.uwb.UwbAddress getAddress();
+    property public final androidx.core.uwb.UwbAddress address;
+    field public static final androidx.core.uwb.UwbDevice.Companion Companion;
+  }
+
+  public static final class UwbDevice.Companion {
+    method public androidx.core.uwb.UwbDevice createForAddress(String address);
+    method public androidx.core.uwb.UwbDevice createForAddress(byte[] address);
+  }
+
+  public interface UwbManager {
+    method public suspend <R> Object? clientSessionScope(kotlin.jvm.functions.Function2<? super androidx.core.uwb.UwbClientSessionScope,? super kotlin.coroutines.Continuation<? super R>,?> sessionHandler, kotlin.coroutines.Continuation<? super R>);
+    method public default static androidx.core.uwb.UwbManager getInstance(android.content.Context context);
+    field public static final androidx.core.uwb.UwbManager.Companion Companion;
+  }
+
+  public static final class UwbManager.Companion {
+    method public androidx.core.uwb.UwbManager getInstance(android.content.Context context);
+  }
+
+}
+
+package androidx.core.uwb.exceptions {
+
+  public class UwbApiException extends java.lang.Exception {
+    ctor public UwbApiException(String message);
+  }
+
+  public final class UwbBackgroundPolicyException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbBackgroundPolicyException(String message);
+  }
+
+  public final class UwbHardwareNotAvailableException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbHardwareNotAvailableException(String message);
+  }
+
+  public final class UwbRangingAlreadyStartedException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbRangingAlreadyStartedException(String message);
+  }
+
+  public final class UwbServiceNotAvailableException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbServiceNotAvailableException(String message);
+  }
+
+  public final class UwbSystemCallbackException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbSystemCallbackException(String message);
+  }
+
+}
+
+package androidx.core.uwb.helper {
+
+  public final class UwbHelperKt {
+  }
+
+}
+
diff --git a/core/uwb/uwb/api/public_plus_experimental_current.txt b/core/uwb/uwb/api/public_plus_experimental_current.txt
index e6f50d0..0b2add9 100644
--- a/core/uwb/uwb/api/public_plus_experimental_current.txt
+++ b/core/uwb/uwb/api/public_plus_experimental_current.txt
@@ -1 +1,165 @@
 // Signature format: 4.0
+package androidx.core.uwb {
+
+  public final class RangingCapabilities {
+    ctor public RangingCapabilities(boolean supportsDistance, boolean supportsAzimuthalAngle, boolean supportsElevationAngle);
+    method public boolean getSupportsAzimuthalAngle();
+    method public boolean getSupportsDistance();
+    method public boolean getSupportsElevationAngle();
+    property public final boolean supportsAzimuthalAngle;
+    property public final boolean supportsDistance;
+    property public final boolean supportsElevationAngle;
+  }
+
+  public final class RangingMeasurement {
+    ctor public RangingMeasurement(float value);
+    method public float getValue();
+    property public final float value;
+  }
+
+  public final class RangingParameters {
+    ctor public RangingParameters(int uwbConfigId, int sessionId, byte[]? sessionKeyInfo, androidx.core.uwb.UwbComplexChannel? complexChannel, java.util.List<androidx.core.uwb.UwbDevice> peerDevices, int updateRate);
+    method public androidx.core.uwb.UwbComplexChannel? getComplexChannel();
+    method public java.util.List<androidx.core.uwb.UwbDevice> getPeerDevices();
+    method public int getSessionId();
+    method public byte[]? getSessionKeyInfo();
+    method public int getUpdateRate();
+    method public int getUwbConfigId();
+    property public final androidx.core.uwb.UwbComplexChannel? complexChannel;
+    property public final java.util.List<androidx.core.uwb.UwbDevice> peerDevices;
+    property public final int sessionId;
+    property public final byte[]? sessionKeyInfo;
+    property public final int updateRate;
+    property public final int uwbConfigId;
+    field public static final androidx.core.uwb.RangingParameters.Companion Companion;
+    field public static final int RANGING_UPDATE_RATE_AUTOMATIC;
+    field public static final int RANGING_UPDATE_RATE_FREQUENT;
+    field public static final int RANGING_UPDATE_RATE_INFREQUENT;
+  }
+
+  public static final class RangingParameters.Companion {
+  }
+
+  public final class RangingPosition {
+    ctor public RangingPosition(androidx.core.uwb.RangingMeasurement distance, androidx.core.uwb.RangingMeasurement? azimuth, androidx.core.uwb.RangingMeasurement? elevation, long elapsedRealtimeNanos);
+    method public androidx.core.uwb.RangingMeasurement? getAzimuth();
+    method public androidx.core.uwb.RangingMeasurement getDistance();
+    method public long getElapsedRealtimeNanos();
+    method public androidx.core.uwb.RangingMeasurement? getElevation();
+    property public final androidx.core.uwb.RangingMeasurement? azimuth;
+    property public final androidx.core.uwb.RangingMeasurement distance;
+    property public final long elapsedRealtimeNanos;
+    property public final androidx.core.uwb.RangingMeasurement? elevation;
+  }
+
+  public abstract class RangingResult {
+    ctor public RangingResult();
+    method public abstract androidx.core.uwb.UwbDevice getDevice();
+    property public abstract androidx.core.uwb.UwbDevice device;
+  }
+
+  public final class RangingResultPeerDisconnected extends androidx.core.uwb.RangingResult {
+    ctor public RangingResultPeerDisconnected(androidx.core.uwb.UwbDevice device);
+    method public androidx.core.uwb.UwbDevice getDevice();
+    property public androidx.core.uwb.UwbDevice device;
+  }
+
+  public final class RangingResultPosition extends androidx.core.uwb.RangingResult {
+    ctor public RangingResultPosition(androidx.core.uwb.UwbDevice device, androidx.core.uwb.RangingPosition position);
+    method public androidx.core.uwb.UwbDevice getDevice();
+    method public androidx.core.uwb.RangingPosition getPosition();
+    property public androidx.core.uwb.UwbDevice device;
+    property public final androidx.core.uwb.RangingPosition position;
+  }
+
+  public final class UwbAddress {
+    ctor public UwbAddress(byte[] address);
+    ctor public UwbAddress(String address);
+    method public byte[] getAddress();
+    property public final byte[] address;
+    field public static final androidx.core.uwb.UwbAddress.Companion Companion;
+  }
+
+  public static final class UwbAddress.Companion {
+  }
+
+  public interface UwbClientSessionScope {
+    method public androidx.core.uwb.UwbAddress getLocalAddress();
+    method public androidx.core.uwb.RangingCapabilities getRangingCapabilities();
+    method public kotlinx.coroutines.flow.Flow<androidx.core.uwb.RangingResult> initSession(androidx.core.uwb.RangingParameters parameters);
+    property public abstract androidx.core.uwb.UwbAddress localAddress;
+    property public abstract androidx.core.uwb.RangingCapabilities rangingCapabilities;
+  }
+
+  public final class UwbComplexChannel {
+    ctor public UwbComplexChannel(int channel, int preambleIndex);
+    method public int getChannel();
+    method public int getPreambleIndex();
+    property public final int channel;
+    property public final int preambleIndex;
+  }
+
+  public interface UwbControleeSessionScope extends androidx.core.uwb.UwbClientSessionScope {
+  }
+
+  public final class UwbDevice {
+    ctor public UwbDevice(androidx.core.uwb.UwbAddress address);
+    method public static androidx.core.uwb.UwbDevice createForAddress(String address);
+    method public static androidx.core.uwb.UwbDevice createForAddress(byte[] address);
+    method public androidx.core.uwb.UwbAddress getAddress();
+    property public final androidx.core.uwb.UwbAddress address;
+    field public static final androidx.core.uwb.UwbDevice.Companion Companion;
+  }
+
+  public static final class UwbDevice.Companion {
+    method public androidx.core.uwb.UwbDevice createForAddress(String address);
+    method public androidx.core.uwb.UwbDevice createForAddress(byte[] address);
+  }
+
+  public interface UwbManager {
+    method public suspend <R> Object? clientSessionScope(kotlin.jvm.functions.Function2<? super androidx.core.uwb.UwbClientSessionScope,? super kotlin.coroutines.Continuation<? super R>,?> sessionHandler, kotlin.coroutines.Continuation<? super R>);
+    method public default static androidx.core.uwb.UwbManager getInstance(android.content.Context context);
+    field public static final androidx.core.uwb.UwbManager.Companion Companion;
+  }
+
+  public static final class UwbManager.Companion {
+    method public androidx.core.uwb.UwbManager getInstance(android.content.Context context);
+  }
+
+}
+
+package androidx.core.uwb.exceptions {
+
+  public class UwbApiException extends java.lang.Exception {
+    ctor public UwbApiException(String message);
+  }
+
+  public final class UwbBackgroundPolicyException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbBackgroundPolicyException(String message);
+  }
+
+  public final class UwbHardwareNotAvailableException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbHardwareNotAvailableException(String message);
+  }
+
+  public final class UwbRangingAlreadyStartedException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbRangingAlreadyStartedException(String message);
+  }
+
+  public final class UwbServiceNotAvailableException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbServiceNotAvailableException(String message);
+  }
+
+  public final class UwbSystemCallbackException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbSystemCallbackException(String message);
+  }
+
+}
+
+package androidx.core.uwb.helper {
+
+  public final class UwbHelperKt {
+  }
+
+}
+
diff --git a/core/uwb/uwb/api/restricted_current.txt b/core/uwb/uwb/api/restricted_current.txt
index e6f50d0..0b2add9 100644
--- a/core/uwb/uwb/api/restricted_current.txt
+++ b/core/uwb/uwb/api/restricted_current.txt
@@ -1 +1,165 @@
 // Signature format: 4.0
+package androidx.core.uwb {
+
+  public final class RangingCapabilities {
+    ctor public RangingCapabilities(boolean supportsDistance, boolean supportsAzimuthalAngle, boolean supportsElevationAngle);
+    method public boolean getSupportsAzimuthalAngle();
+    method public boolean getSupportsDistance();
+    method public boolean getSupportsElevationAngle();
+    property public final boolean supportsAzimuthalAngle;
+    property public final boolean supportsDistance;
+    property public final boolean supportsElevationAngle;
+  }
+
+  public final class RangingMeasurement {
+    ctor public RangingMeasurement(float value);
+    method public float getValue();
+    property public final float value;
+  }
+
+  public final class RangingParameters {
+    ctor public RangingParameters(int uwbConfigId, int sessionId, byte[]? sessionKeyInfo, androidx.core.uwb.UwbComplexChannel? complexChannel, java.util.List<androidx.core.uwb.UwbDevice> peerDevices, int updateRate);
+    method public androidx.core.uwb.UwbComplexChannel? getComplexChannel();
+    method public java.util.List<androidx.core.uwb.UwbDevice> getPeerDevices();
+    method public int getSessionId();
+    method public byte[]? getSessionKeyInfo();
+    method public int getUpdateRate();
+    method public int getUwbConfigId();
+    property public final androidx.core.uwb.UwbComplexChannel? complexChannel;
+    property public final java.util.List<androidx.core.uwb.UwbDevice> peerDevices;
+    property public final int sessionId;
+    property public final byte[]? sessionKeyInfo;
+    property public final int updateRate;
+    property public final int uwbConfigId;
+    field public static final androidx.core.uwb.RangingParameters.Companion Companion;
+    field public static final int RANGING_UPDATE_RATE_AUTOMATIC;
+    field public static final int RANGING_UPDATE_RATE_FREQUENT;
+    field public static final int RANGING_UPDATE_RATE_INFREQUENT;
+  }
+
+  public static final class RangingParameters.Companion {
+  }
+
+  public final class RangingPosition {
+    ctor public RangingPosition(androidx.core.uwb.RangingMeasurement distance, androidx.core.uwb.RangingMeasurement? azimuth, androidx.core.uwb.RangingMeasurement? elevation, long elapsedRealtimeNanos);
+    method public androidx.core.uwb.RangingMeasurement? getAzimuth();
+    method public androidx.core.uwb.RangingMeasurement getDistance();
+    method public long getElapsedRealtimeNanos();
+    method public androidx.core.uwb.RangingMeasurement? getElevation();
+    property public final androidx.core.uwb.RangingMeasurement? azimuth;
+    property public final androidx.core.uwb.RangingMeasurement distance;
+    property public final long elapsedRealtimeNanos;
+    property public final androidx.core.uwb.RangingMeasurement? elevation;
+  }
+
+  public abstract class RangingResult {
+    ctor public RangingResult();
+    method public abstract androidx.core.uwb.UwbDevice getDevice();
+    property public abstract androidx.core.uwb.UwbDevice device;
+  }
+
+  public final class RangingResultPeerDisconnected extends androidx.core.uwb.RangingResult {
+    ctor public RangingResultPeerDisconnected(androidx.core.uwb.UwbDevice device);
+    method public androidx.core.uwb.UwbDevice getDevice();
+    property public androidx.core.uwb.UwbDevice device;
+  }
+
+  public final class RangingResultPosition extends androidx.core.uwb.RangingResult {
+    ctor public RangingResultPosition(androidx.core.uwb.UwbDevice device, androidx.core.uwb.RangingPosition position);
+    method public androidx.core.uwb.UwbDevice getDevice();
+    method public androidx.core.uwb.RangingPosition getPosition();
+    property public androidx.core.uwb.UwbDevice device;
+    property public final androidx.core.uwb.RangingPosition position;
+  }
+
+  public final class UwbAddress {
+    ctor public UwbAddress(byte[] address);
+    ctor public UwbAddress(String address);
+    method public byte[] getAddress();
+    property public final byte[] address;
+    field public static final androidx.core.uwb.UwbAddress.Companion Companion;
+  }
+
+  public static final class UwbAddress.Companion {
+  }
+
+  public interface UwbClientSessionScope {
+    method public androidx.core.uwb.UwbAddress getLocalAddress();
+    method public androidx.core.uwb.RangingCapabilities getRangingCapabilities();
+    method public kotlinx.coroutines.flow.Flow<androidx.core.uwb.RangingResult> initSession(androidx.core.uwb.RangingParameters parameters);
+    property public abstract androidx.core.uwb.UwbAddress localAddress;
+    property public abstract androidx.core.uwb.RangingCapabilities rangingCapabilities;
+  }
+
+  public final class UwbComplexChannel {
+    ctor public UwbComplexChannel(int channel, int preambleIndex);
+    method public int getChannel();
+    method public int getPreambleIndex();
+    property public final int channel;
+    property public final int preambleIndex;
+  }
+
+  public interface UwbControleeSessionScope extends androidx.core.uwb.UwbClientSessionScope {
+  }
+
+  public final class UwbDevice {
+    ctor public UwbDevice(androidx.core.uwb.UwbAddress address);
+    method public static androidx.core.uwb.UwbDevice createForAddress(String address);
+    method public static androidx.core.uwb.UwbDevice createForAddress(byte[] address);
+    method public androidx.core.uwb.UwbAddress getAddress();
+    property public final androidx.core.uwb.UwbAddress address;
+    field public static final androidx.core.uwb.UwbDevice.Companion Companion;
+  }
+
+  public static final class UwbDevice.Companion {
+    method public androidx.core.uwb.UwbDevice createForAddress(String address);
+    method public androidx.core.uwb.UwbDevice createForAddress(byte[] address);
+  }
+
+  public interface UwbManager {
+    method public suspend <R> Object? clientSessionScope(kotlin.jvm.functions.Function2<? super androidx.core.uwb.UwbClientSessionScope,? super kotlin.coroutines.Continuation<? super R>,?> sessionHandler, kotlin.coroutines.Continuation<? super R>);
+    method public default static androidx.core.uwb.UwbManager getInstance(android.content.Context context);
+    field public static final androidx.core.uwb.UwbManager.Companion Companion;
+  }
+
+  public static final class UwbManager.Companion {
+    method public androidx.core.uwb.UwbManager getInstance(android.content.Context context);
+  }
+
+}
+
+package androidx.core.uwb.exceptions {
+
+  public class UwbApiException extends java.lang.Exception {
+    ctor public UwbApiException(String message);
+  }
+
+  public final class UwbBackgroundPolicyException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbBackgroundPolicyException(String message);
+  }
+
+  public final class UwbHardwareNotAvailableException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbHardwareNotAvailableException(String message);
+  }
+
+  public final class UwbRangingAlreadyStartedException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbRangingAlreadyStartedException(String message);
+  }
+
+  public final class UwbServiceNotAvailableException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbServiceNotAvailableException(String message);
+  }
+
+  public final class UwbSystemCallbackException extends androidx.core.uwb.exceptions.UwbApiException {
+    ctor public UwbSystemCallbackException(String message);
+  }
+
+}
+
+package androidx.core.uwb.helper {
+
+  public final class UwbHelperKt {
+  }
+
+}
+
diff --git a/core/uwb/uwb/build.gradle b/core/uwb/uwb/build.gradle
index 3793f28..da9b837 100644
--- a/core/uwb/uwb/build.gradle
+++ b/core/uwb/uwb/build.gradle
@@ -24,7 +24,23 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    // Add dependencies here
+    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.core:core-ktx:1.2.0")
+    api(libs.kotlinCoroutinesAndroid)
+    implementation(libs.guavaAndroid)
+    implementation('com.google.android.gms:play-services-base:18.0.1')
+    implementation('org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.0')
+    implementation('com.google.android.gms:play-services-nearby:18.1.0', {
+        exclude group: "androidx.core"
+    })
+
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.espressoCore)
 }
 
 androidx {
diff --git a/core/uwb/uwb/src/androidTest/java/androidx/core/uwb/impl/UwbClientSessionScopeImplTest.kt b/core/uwb/uwb/src/androidTest/java/androidx/core/uwb/impl/UwbClientSessionScopeImplTest.kt
new file mode 100644
index 0000000..3fa5e47
--- /dev/null
+++ b/core/uwb/uwb/src/androidTest/java/androidx/core/uwb/impl/UwbClientSessionScopeImplTest.kt
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb.impl
+
+import androidx.core.uwb.RangingParameters
+import androidx.core.uwb.RangingResult
+import androidx.core.uwb.RangingResultPeerDisconnected
+import androidx.core.uwb.RangingResultPosition
+import androidx.core.uwb.UwbDevice
+import androidx.core.uwb.exceptions.UwbRangingAlreadyStartedException
+import androidx.core.uwb.mock.TestUwbClient
+import com.google.android.gms.nearby.uwb.RangingCapabilities
+import com.google.android.gms.nearby.uwb.UwbAddress
+import com.google.android.gms.nearby.uwb.UwbComplexChannel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.cancellable
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert
+import org.junit.Test
+
+class UwbClientSessionScopeImplTest {
+    private val complexChannel = UwbComplexChannel.Builder()
+        .setPreambleIndex(0)
+        .setChannel(0)
+        .build()
+    private val localAddress = UwbAddress(ByteArray(0))
+    private val rangingCapabilities = RangingCapabilities(true, false, false)
+    private val uwbClient = TestUwbClient(complexChannel, localAddress, rangingCapabilities, true)
+    private val uwbClientSessionScopeImpl = UwbClientSessionScopeImpl(
+        uwbClient,
+        androidx.core.uwb.RangingCapabilities(
+            rangingCapabilities.supportsDistance(),
+            rangingCapabilities.supportsAzimuthalAngle(),
+            rangingCapabilities.supportsElevationAngle()),
+        androidx.core.uwb.UwbAddress(localAddress.address))
+    private val uwbDevice = UwbDevice.createForAddress(ByteArray(0))
+    private val rangingParameters = RangingParameters(
+        0,
+        0,
+        null,
+        null,
+        listOf(uwbDevice),
+        0
+    )
+
+    @Test
+    public fun testInitSession_singleConsumer() {
+        val sessionFlow = uwbClientSessionScopeImpl.initSession(rangingParameters)
+        var rangingResult: RangingResult? = null
+        val job = sessionFlow
+            .cancellable()
+            .onEach { rangingResult = it }
+            .launchIn(CoroutineScope(Dispatchers.Main.immediate))
+
+        runBlocking {
+            // wait for the coroutines for UWB to get launched.
+            delay(500)
+        }
+        // a non-null RangingResult should return from the TestUwbClient.
+        if (rangingResult != null) {
+            assertThat(rangingResult is RangingResultPosition).isTrue()
+        } else {
+            job.cancel()
+            Assert.fail()
+        }
+
+        // cancel and wait for the job to terminate.
+        job.cancel()
+        runBlocking {
+            job.join()
+        }
+        // StopRanging should have been called after the coroutine scope completed.
+        assertThat(uwbClient.stopRangingCalled).isTrue()
+    }
+
+    @Test
+    public fun testInitSession_multipleSharedConsumers() {
+        var passed1 = false
+        var passed2 = false
+        val sharedFlow = uwbClientSessionScopeImpl.initSession(rangingParameters)
+            .shareIn(CoroutineScope(Dispatchers.Main.immediate), SharingStarted.WhileSubscribed(),
+                replay = 1)
+        val job = CoroutineScope(Dispatchers.Main.immediate).launch {
+            sharedFlow
+                .onEach {
+                    if (it is RangingResultPosition) {
+                        passed1 = true
+                    }
+                }
+                .collect()
+        }
+        val job2 = CoroutineScope(Dispatchers.Main.immediate).launch {
+            sharedFlow
+                .onEach {
+                    if (it is RangingResultPosition) {
+                        passed2 = true
+                    }
+                }
+                .collect()
+        }
+
+        runBlocking {
+            // wait for coroutines for flow to start.
+            delay(500)
+        }
+
+        // a non-null RangingResult should return from the TestUwbClient.
+        assertThat(passed1).isTrue()
+        assertThat(passed2).isTrue()
+
+        // cancel and wait for the first job to terminate.
+        job.cancel()
+        runBlocking {
+            job.join()
+        }
+        // StopRanging should not have been called because not all consumers have finished.
+        assertThat(uwbClient.stopRangingCalled).isFalse()
+
+        // cancel and wait for the second job to terminate.
+        job2.cancel()
+        runBlocking {
+            job2.join()
+        }
+        // StopRanging should have been called because all consumers have finished.
+        assertThat(uwbClient.stopRangingCalled).isTrue()
+    }
+
+    @Test
+    public fun testInitSession_singleConsumer_disconnectPeerDevice() {
+        val sessionFlow = uwbClientSessionScopeImpl.initSession(rangingParameters)
+        var peerDisconnected = false
+        val job = CoroutineScope(Dispatchers.Main.immediate).launch {
+            sessionFlow
+                .cancellable()
+                .onEach {
+                    if (it is RangingResultPeerDisconnected) {
+                        peerDisconnected = true
+                    }
+                }
+                .collect()
+        }
+
+        runBlocking {
+            // wait for coroutines for flow to start.
+            delay(500)
+            uwbClient.disconnectPeer(com.google.android.gms.nearby.uwb.UwbDevice.createForAddress(
+                uwbDevice.address.address))
+
+            // wait for rangingResults to get filled.
+            delay(500)
+        }
+
+        // a peer disconnected event should have occurred.
+        assertThat(peerDisconnected).isTrue()
+
+        // cancel and wait for the job to terminate.
+        job.cancel()
+        runBlocking {
+            job.join()
+        }
+        // StopRanging should have been called after the coroutine scope completed.
+        assertThat(uwbClient.stopRangingCalled).isTrue()
+    }
+
+    @Test
+    public fun testInitSession_multipleSharedConsumers_disconnectPeerDevice() {
+        val sharedFlow = uwbClientSessionScopeImpl.initSession(rangingParameters)
+            .shareIn(CoroutineScope(Dispatchers.Main.immediate), SharingStarted.WhileSubscribed())
+
+        var peerDisconnected = false
+        var peerDisconnected2 = false
+        val job = CoroutineScope(Dispatchers.Main.immediate).launch {
+            sharedFlow
+                .onEach {
+                    if (it is RangingResultPeerDisconnected) {
+                        peerDisconnected = true
+                    }
+                }
+                .collect()
+        }
+        val job2 = CoroutineScope(Dispatchers.Main.immediate).launch {
+            sharedFlow
+                .onEach {
+                    if (it is RangingResultPeerDisconnected) {
+                        peerDisconnected2 = true
+                    }
+                }
+                .collect()
+        }
+
+        runBlocking {
+            // wait for coroutines for flow to start.
+            delay(500)
+            uwbClient.disconnectPeer(com.google.android.gms.nearby.uwb.UwbDevice.createForAddress(
+                uwbDevice.address.address))
+
+            // wait for rangingResults to get filled.
+            delay(500)
+        }
+
+        // a peer disconnected event should have occurred.
+        assertThat(peerDisconnected).isTrue()
+        assertThat(peerDisconnected2).isTrue()
+
+        // cancel and wait for the job to terminate.
+        job.cancel()
+        runBlocking {
+            job.join()
+        }
+        // StopRanging should not have been called because not all consumers have finished.
+        assertThat(uwbClient.stopRangingCalled).isFalse()
+
+        // cancel and wait for the job to terminate.
+        job2.cancel()
+        runBlocking {
+            job2.join()
+        }
+        // StopRanging should have been called because all consumers have finished.
+        assertThat(uwbClient.stopRangingCalled).isTrue()
+    }
+
+    @Test
+    public fun testInitSession_multipleSessions_throwsUwbApiException() {
+        val sessionFlow = uwbClientSessionScopeImpl.initSession(rangingParameters)
+        val sessionFlow2 = uwbClientSessionScopeImpl.initSession(rangingParameters)
+
+        val job = CoroutineScope(Dispatchers.Main.immediate).launch {
+            sessionFlow.collect()
+        }
+        runBlocking {
+            // wait for coroutines for flow to start.
+            delay(500)
+        }
+        val job2 = CoroutineScope(Dispatchers.Main.immediate).launch {
+            try {
+                sessionFlow2.collect()
+                Assert.fail()
+            } catch (e: UwbRangingAlreadyStartedException) {
+                // verified the exception was thrown.
+            }
+        }
+        job.cancel()
+        job2.cancel()
+    }
+
+    @Test
+    public fun testInitSession_reusingSession_throwsUwbApiException() {
+        val sessionFlow = uwbClientSessionScopeImpl.initSession(rangingParameters)
+
+        val job = CoroutineScope(Dispatchers.Main.immediate).launch {
+            sessionFlow.collect()
+        }
+        runBlocking {
+            // wait for coroutines for flow to start.
+            delay(500)
+        }
+        // cancel and wait for the job to terminate.
+        job.cancel()
+        runBlocking {
+            job.join()
+        }
+        // StopRanging should not have been called because not all consumers have finished.
+        assertThat(uwbClient.stopRangingCalled).isTrue()
+        val job2 = CoroutineScope(Dispatchers.Main.immediate).launch {
+            try {
+                // Reuse the same session after it was closed.
+                sessionFlow.collect()
+                Assert.fail()
+            } catch (e: UwbRangingAlreadyStartedException) {
+                // verified the exception was thrown.
+            }
+        }
+        job2.cancel()
+    }
+}
\ No newline at end of file
diff --git a/core/uwb/uwb/src/androidTest/java/androidx/core/uwb/mock/TestUwbClient.kt b/core/uwb/uwb/src/androidTest/java/androidx/core/uwb/mock/TestUwbClient.kt
new file mode 100644
index 0000000..afe7418
--- /dev/null
+++ b/core/uwb/uwb/src/androidTest/java/androidx/core/uwb/mock/TestUwbClient.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb.mock
+
+import com.google.android.gms.common.api.ApiException
+import com.google.android.gms.common.api.Status
+import com.google.android.gms.common.api.internal.ApiKey
+import com.google.android.gms.nearby.uwb.RangingCapabilities
+import com.google.android.gms.nearby.uwb.RangingMeasurement
+import com.google.android.gms.nearby.uwb.RangingParameters
+import com.google.android.gms.nearby.uwb.RangingPosition
+import com.google.android.gms.nearby.uwb.RangingSessionCallback
+import com.google.android.gms.nearby.uwb.UwbAddress
+import com.google.android.gms.nearby.uwb.UwbClient
+import com.google.android.gms.nearby.uwb.UwbComplexChannel
+import com.google.android.gms.nearby.uwb.UwbDevice
+import com.google.android.gms.nearby.uwb.UwbStatusCodes
+import com.google.android.gms.nearby.uwb.zze
+import com.google.android.gms.tasks.Task
+import com.google.android.gms.tasks.Tasks
+
+/** A default implementation of [UwbClient] used in testing. */
+class TestUwbClient(
+    val complexChannel: UwbComplexChannel,
+    val localAddress: UwbAddress,
+    val rangingCapabilities: RangingCapabilities,
+    val isAvailable: Boolean
+) : UwbClient {
+    var stopRangingCalled = false
+        private set
+    private lateinit var callback: RangingSessionCallback
+    private var startedRanging = false
+    companion object {
+        val rangingPosition = RangingPosition(
+            RangingMeasurement(1, 1.0F), null, null, 20)
+    }
+    override fun getApiKey(): ApiKey<zze> {
+        TODO("Not yet implemented")
+    }
+
+    override fun getComplexChannel(): Task<UwbComplexChannel> {
+        return Tasks.forResult(complexChannel)
+    }
+
+    override fun getLocalAddress(): Task<UwbAddress> {
+        return Tasks.forResult(localAddress)
+    }
+
+    override fun getRangingCapabilities(): Task<RangingCapabilities> {
+        return Tasks.forResult(rangingCapabilities)
+    }
+
+    override fun isAvailable(): Task<Boolean> {
+        return Tasks.forResult(isAvailable)
+    }
+
+    override fun startRanging(
+        parameters: RangingParameters,
+        sessionCallback: RangingSessionCallback
+    ): Task<Void> {
+        if (startedRanging) {
+            throw ApiException(Status(UwbStatusCodes.RANGING_ALREADY_STARTED))
+        }
+        callback = sessionCallback
+        val peer = parameters.peerDevices.first()
+        callback.onRangingResult(peer, rangingPosition)
+        startedRanging = true
+        return Tasks.forResult(null)
+    }
+
+    override fun stopRanging(callback: RangingSessionCallback): Task<Void> {
+        if (stopRangingCalled) {
+            throw RuntimeException("Stop Ranging has already been called.")
+        }
+        stopRangingCalled = true
+        return Tasks.forResult(null)
+    }
+
+    fun disconnectPeer(device: UwbDevice) {
+        callback.onRangingSuspended(device, 0)
+    }
+}
\ No newline at end of file
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingCapabilities.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingCapabilities.kt
new file mode 100644
index 0000000..1366a64
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingCapabilities.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb
+
+/**
+ * Describes UWB ranging capabilities for the current device.
+ * @property supportsDistance - Whether distance ranging is supported
+ * @property supportsAzimuthalAngle - Whether azimuthal angle of arrival is supported
+ * @property supportsElevationAngle - Whether elevation angle of arrival is supported
+ **/
+class RangingCapabilities(
+    val supportsDistance: Boolean,
+    val supportsAzimuthalAngle: Boolean,
+    val supportsElevationAngle: Boolean
+)
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingMeasurement.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingMeasurement.kt
new file mode 100644
index 0000000..424669c
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingMeasurement.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb
+
+/** Measurement providing the value and confidence of the ranging.
+ *
+ * @property value the value of this measurement.
+ */
+class RangingMeasurement(val value: Float)
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingParameters.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingParameters.kt
new file mode 100644
index 0000000..a6aa775
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingParameters.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb
+
+/**
+ * Set of parameters which should be passed to the UWB chip to start ranging.
+ *
+ * @property uwbConfigId
+ * The UWB configuration ID. One ID specifies one fixed set of pre-defined parameters.
+ *
+ * @property sessionId
+ * The ID of the ranging session. If the value is SESSION_ID_UNSET (0), it will
+ * be created from the hash of controller address and complex channel values.
+ *
+ * The same session IDs should be used at both ends (Controller and controlee).
+ *
+ * @property sessionKeyInfo
+ * The session key info to use for the ranging.
+ * If the profile uses STATIC
+ * STS, this byte array is 8-byte long with first two bytes as
+ * Vendor_ID and next six bytes as STATIC_STS_IV.
+ *
+ * The same session keys should be used at both ends (Controller and controlee).
+ *
+ * @property complexChannel
+ * Optional. If device type is ROLE_CONTROLEE then complex channel should be set.
+ *
+ * @property peerDevices
+ * The peers to perform ranging with. If using unicast, length should be 1.
+ *
+ * @property updateRate
+ * The update rate of the ranging data
+ */
+class RangingParameters(
+    val uwbConfigId: Int,
+    val sessionId: Int,
+    val sessionKeyInfo: ByteArray?,
+    val complexChannel: UwbComplexChannel?,
+    val peerDevices: List<UwbDevice>,
+    val updateRate: Int
+) {
+
+    companion object {
+
+        /**
+         * Pre-defined unicast STATIC STS DS-TWR ranging, deferred mode, ranging
+         * interval 240 ms.
+         *
+         * <p> Typical use case: device tracking tags
+         */
+        @JvmField
+        internal val UWB_CONFIG_ID_1 = 1
+
+        /**
+         * Pre-defined one-to-many STATIC STS DS-TWR ranging, deferred mode, ranging
+         * interval 200 ms.
+         *
+         * <p> Typical use case: smart phone interacts with many smart devices
+         */
+        @JvmField
+        internal val UWB_CONFIG_ID_2 = 2
+
+        /** Same as CONFIG_ID_1, except AoA data is not reported. */
+        @JvmField
+        internal val UWB_CONFIG_ID_3 = 3
+
+        /**
+         * When the screen is on, the reporting interval is hundreds of milliseconds.
+         * When the screen is off, the reporting interval is a few seconds.
+         */
+        @JvmField
+        val RANGING_UPDATE_RATE_AUTOMATIC = 1
+
+        /**
+         * The reporting interval is the same as in the AUTOMATIC screen-off case. The
+         * The power consumption is optimized by turning off the radio between ranging
+         * reports. (The implementation is hardware and software dependent and it may
+         * change between different versions.)
+         */
+        @JvmField
+        val RANGING_UPDATE_RATE_INFREQUENT = 2
+
+        /**
+         * The reporting interval is the same as in the AUTOMATIC screen-on case.
+         *
+         * The actual reporting interval is UwbConfigId related. Different
+         * configuration may use different values. (The default reporting interval at INFREQUENT mode is 4 seconds)
+         */
+        @JvmField
+        val RANGING_UPDATE_RATE_FREQUENT = 3
+    }
+}
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingPosition.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingPosition.kt
new file mode 100644
index 0000000..5ae737b
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingPosition.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb
+
+/**
+ * Position of a device during ranging.
+ *
+ * @property distance
+ * The line-of-sight distance in meters of the ranging device, or null if not
+ * available.
+ *
+ * @property azimuth
+ * The azimuth angle in degrees of the ranging device, or null if not available.
+ * The range is [-90, 90].
+ *
+ * @property elevation
+ * The elevation angle in degrees of the ranging device, or null if not
+ * available. The range is [-90, 90].
+ *
+ * @property elapsedRealtimeNanos
+ * The elapsed realtime in nanos from when the system booted up to this position
+ * measurement.
+ */
+class RangingPosition(
+    val distance: RangingMeasurement,
+    val azimuth: RangingMeasurement?,
+    val elevation: RangingMeasurement?,
+    val elapsedRealtimeNanos: Long
+)
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingResult.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingResult.kt
new file mode 100644
index 0000000..e43c336
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/RangingResult.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb
+
+/** A data class for ranging result update. */
+abstract class RangingResult {
+    abstract val device: UwbDevice
+}
+
+/** A ranging result with the device position update. */
+class RangingResultPosition(
+    override val device: UwbDevice,
+    val position: RangingPosition
+) : RangingResult()
+
+/** A ranging result with peer disconnected status update. */
+class RangingResultPeerDisconnected(override val device: UwbDevice) : RangingResult()
\ No newline at end of file
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbAddress.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbAddress.kt
new file mode 100644
index 0000000..f1db3ca
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbAddress.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb
+
+import com.google.common.io.BaseEncoding
+
+/**
+ * Represents a UWB address.
+ *
+ * @property address the device address (eg, MAC address).
+ */
+class UwbAddress(val address: ByteArray) {
+
+    /** @throws an [IllegalArgumentException] if address is invalid. */
+    constructor(address: String) : this(BASE_16_SEPARATOR.decode(address))
+
+    companion object {
+        private val BASE_16_SEPARATOR: BaseEncoding = BaseEncoding.base16().withSeparator(":", 2)
+    }
+
+    /** Checks that two UwbAddresses are equal. */
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as UwbAddress
+
+        if (!address.contentEquals(other.address)) return false
+
+        return true
+    }
+
+    /** Returns the hashcode. */
+    override fun hashCode(): Int {
+        return address.contentHashCode()
+    }
+
+    /** Returns the string format of [UwbAddress]. */
+    override fun toString(): String {
+        return BASE_16_SEPARATOR.encode(address)
+    }
+}
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbClientSessionScope.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbClientSessionScope.kt
new file mode 100644
index 0000000..0151426
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbClientSessionScope.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb
+
+import kotlinx.coroutines.flow.Flow
+
+/** Interface for client session that is established between nearby UWB devices. */
+interface UwbClientSessionScope {
+    /**
+     * Returns a flow of [RangingResult]. Consuming the flow will initiate the UWB ranging and only
+     * one flow can be initiated. To consume the flow from multiple consumers,
+     * convert the flow to a SharedFlow.
+     *
+     * @throws [UwbRangingAlreadyStartedException] if a new flow was consumed again after the UWB
+     * ranging is already initiated.
+     *
+     * @throws [UwbSystemCallbackException] if the backend UWB system has resulted in an error.
+     *
+     * @throws [SecurityException] if ranging does not have the
+     * android.permission.UWB_RANGING permission. Apps must
+     * have requested and been granted this permission before calling this method.
+     *
+     * @throws [IllegalArgumentException] if the client starts a controlee session
+     * without setting complex channel and peer address.
+     */
+    fun initSession(parameters: RangingParameters): Flow<RangingResult>
+
+    /** Returns the [RangingCapabilities] which the device supports. */
+    val rangingCapabilities: RangingCapabilities
+
+    /**
+     * A local address can only be used for a single ranging session. After
+     * a ranging session is ended, a new address will be allocated.
+     *
+     * Ranging session duration may also be limited to prevent addresses
+     * from being used for too long. In this case, your ranging session would be
+     * suspended and clients would need to exchange the new address with their peer
+     * before starting again.
+     */
+    val localAddress: UwbAddress
+}
\ No newline at end of file
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbComplexChannel.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbComplexChannel.kt
new file mode 100644
index 0000000..644dd5b
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbComplexChannel.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb
+
+/**
+ * Represents the channel which a UWB device is currently active on.
+ *
+ * @property channel the current channel for the device.
+ * @property preambleIndex the current preamble index for the device.
+ */
+class UwbComplexChannel(val channel: Int, val preambleIndex: Int)
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbControleeSessionScope.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbControleeSessionScope.kt
new file mode 100644
index 0000000..5e02a8e
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbControleeSessionScope.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb
+
+/** Interface for controlee client session that is established between nearby UWB devices. */
+interface UwbControleeSessionScope : UwbClientSessionScope
\ No newline at end of file
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbDevice.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbDevice.kt
new file mode 100644
index 0000000..da8a982
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbDevice.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb
+
+/**
+ * Represents a UWB device.
+ *
+ * @property address the device address (e.g., MAC address).
+ */
+class UwbDevice(val address: UwbAddress) {
+
+    companion object {
+        /**
+         * Creates a new UwbDevice for a given address.
+         *
+         * @throws an [IllegalArgumentException] if address is invalid.
+         */
+        @JvmStatic
+        fun createForAddress(address: String): UwbDevice {
+            return UwbDevice(UwbAddress(address))
+        }
+
+        /** Creates a new UwbDevice for a given address. */
+        @JvmStatic
+        fun createForAddress(address: ByteArray): UwbDevice {
+            return UwbDevice(UwbAddress(address))
+        }
+    }
+}
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbManager.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbManager.kt
new file mode 100644
index 0000000..cf21af3
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/UwbManager.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb
+
+import android.content.Context
+import androidx.core.uwb.impl.UwbManagerImpl
+
+/**
+ * Interface for getting UWB capabilities and interacting with nearby UWB devices to perform
+ * ranging.
+ */
+interface UwbManager {
+    companion object {
+
+        /** Creates a new UwbManager that is used for creating controlee client sessions. */
+        @JvmStatic
+        fun getInstance(context: Context): UwbManager {
+            return UwbManagerImpl(context)
+        }
+    }
+
+    /**
+     * Establishes a new [UwbClientSessionScope] that tracks the lifecycle of a UWB connection.
+     *
+     * @throws [UwbServiceNotAvailableException] if the UWB is turned off.
+     * @throws [UwbHardwareNotAvailableException] if the hardware is not available on the device.
+     */
+    suspend fun <R> clientSessionScope(sessionHandler: suspend UwbClientSessionScope.() -> R): R
+}
\ No newline at end of file
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbApiException.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbApiException.kt
new file mode 100644
index 0000000..0dc12f7
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbApiException.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb.exceptions
+
+/** Exception class for Uwb service errors. */
+open class UwbApiException(message: String) : Exception(message)
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbBackgroundPolicyException.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbBackgroundPolicyException.kt
new file mode 100644
index 0000000..1d8d7e2
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbBackgroundPolicyException.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb.exceptions
+
+/**
+ * Calls to the UWB service fail for background systems. Only foreground
+ * systems can call the UWB service.
+ */
+class UwbBackgroundPolicyException(message: String) : UwbApiException(message)
\ No newline at end of file
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbHardwareNotAvailableException.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbHardwareNotAvailableException.kt
new file mode 100644
index 0000000..72ad22d
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbHardwareNotAvailableException.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb.exceptions
+
+/** The uwb hardware is not available on the device. */
+class UwbHardwareNotAvailableException(message: String) : UwbApiException(message)
\ No newline at end of file
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbRangingAlreadyStartedException.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbRangingAlreadyStartedException.kt
new file mode 100644
index 0000000..8a300b0
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbRangingAlreadyStartedException.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb.exceptions
+
+/**
+ * The ranging has already started for the [UwbClientSessionScope], but still received another
+ * request to start ranging. You cannot reuse a [UwbClientSessionScope] for multiple ranging
+ * sessions. To have multiple consumers for a single ranging session, use a [SharedFlow].
+ */
+class UwbRangingAlreadyStartedException(message: String) : UwbApiException(message)
\ No newline at end of file
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbServiceNotAvailableException.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbServiceNotAvailableException.kt
new file mode 100644
index 0000000..ad1db5b
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbServiceNotAvailableException.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb.exceptions
+
+/** The service was not available on the device. */
+class UwbServiceNotAvailableException(message: String) : UwbApiException(message)
\ No newline at end of file
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbSystemCallbackException.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbSystemCallbackException.kt
new file mode 100644
index 0000000..32546ff
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/exceptions/UwbSystemCallbackException.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb.exceptions
+
+/**
+ * Unusual failures happened in UWB system callback, such as stopping
+ * ranging or removing a known controlee failed.
+ */
+class UwbSystemCallbackException(message: String) : UwbApiException(message)
\ No newline at end of file
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/helper/UwbHelper.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/helper/UwbHelper.kt
new file mode 100644
index 0000000..9c5a93e
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/helper/UwbHelper.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb.helper
+
+import android.content.Context
+import androidx.core.uwb.exceptions.UwbHardwareNotAvailableException
+import androidx.core.uwb.exceptions.UwbRangingAlreadyStartedException
+import androidx.core.uwb.exceptions.UwbServiceNotAvailableException
+import androidx.core.uwb.exceptions.UwbSystemCallbackException
+import com.google.android.gms.common.api.ApiException
+import com.google.android.gms.nearby.uwb.UwbStatusCodes
+
+internal const val UWB_FEATURE = "android.hardware.uwb"
+
+/** Returns whether the Uwb System Feature is available on the device. */
+internal fun isSystemFeatureAvailable(context: Context): Boolean {
+    return context.packageManager.hasSystemFeature(UWB_FEATURE)
+}
+
+/** Checks if the uwb system feature is supported and throws an UwbApiException otherwise. */
+internal fun checkSystemFeature(context: Context) {
+    if (!isSystemFeatureAvailable(context)) {
+        throw UwbHardwareNotAvailableException("UWB Hardware is not available on this device.")
+    }
+}
+
+internal fun handleApiException(e: ApiException) {
+    when (e.statusCode) {
+        UwbStatusCodes.INVALID_API_CALL ->
+            throw IllegalArgumentException("Illegal api call was received.")
+        UwbStatusCodes.MISSING_PERMISSION_UWB_RANGING ->
+            throw SecurityException("Missing permission to start ranging.")
+        UwbStatusCodes.RANGING_ALREADY_STARTED ->
+            throw UwbRangingAlreadyStartedException("Ranging has already started for the" +
+                " clientSessionScope.")
+        UwbStatusCodes.SERVICE_NOT_AVAILABLE ->
+            throw UwbServiceNotAvailableException("UWB Service is not available.")
+        UwbStatusCodes.UWB_SYSTEM_CALLBACK_FAILURE ->
+            throw UwbSystemCallbackException("UWB system has failed to deliver ")
+        else ->
+            throw RuntimeException("Unexpected error. This indicates that the library is not " +
+                "up-to-date with the service backend.")
+    }
+}
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbClientSessionScopeImpl.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbClientSessionScopeImpl.kt
new file mode 100644
index 0000000..1dada71
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbClientSessionScopeImpl.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb.impl
+
+import android.util.Log
+import androidx.core.uwb.RangingCapabilities
+import androidx.core.uwb.RangingMeasurement
+import androidx.core.uwb.RangingParameters
+import androidx.core.uwb.RangingResultPeerDisconnected
+import androidx.core.uwb.RangingResultPosition
+import androidx.core.uwb.UwbAddress
+import androidx.core.uwb.UwbControleeSessionScope
+import androidx.core.uwb.exceptions.UwbRangingAlreadyStartedException
+import com.google.android.gms.common.api.ApiException
+import com.google.android.gms.nearby.uwb.RangingPosition
+import com.google.android.gms.nearby.uwb.RangingSessionCallback
+import com.google.android.gms.nearby.uwb.UwbClient
+import com.google.android.gms.nearby.uwb.UwbComplexChannel
+import com.google.android.gms.nearby.uwb.UwbDevice
+import kotlinx.coroutines.flow.callbackFlow
+import androidx.core.uwb.helper.handleApiException
+import kotlinx.coroutines.channels.awaitClose
+
+internal class UwbClientSessionScopeImpl(
+    private val uwbClient: UwbClient,
+    override val rangingCapabilities: RangingCapabilities,
+    override val localAddress: UwbAddress
+) : UwbControleeSessionScope {
+    companion object {
+        private const val TAG = "UwbClientSessionScope"
+    }
+    private var sessionStarted = false
+
+    override fun initSession(parameters: RangingParameters) = callbackFlow {
+        if (sessionStarted) {
+            throw UwbRangingAlreadyStartedException("Ranging has already started. To initiate " +
+                "a new ranging session, create a new client session scope.")
+        }
+
+        val configId = when (parameters.uwbConfigId) {
+            RangingParameters.UWB_CONFIG_ID_1 ->
+                com.google.android.gms.nearby.uwb.RangingParameters.UwbConfigId.CONFIG_ID_1
+            RangingParameters.UWB_CONFIG_ID_3 ->
+                com.google.android.gms.nearby.uwb.RangingParameters.UwbConfigId.CONFIG_ID_3
+            else ->
+                com.google.android.gms.nearby.uwb.RangingParameters.UwbConfigId.UNKNOWN
+        }
+        val updateRate = when (parameters.updateRate) {
+            RangingParameters.RANGING_UPDATE_RATE_AUTOMATIC ->
+                com.google.android.gms.nearby.uwb.RangingParameters.RangingUpdateRate.AUTOMATIC
+            RangingParameters.RANGING_UPDATE_RATE_FREQUENT ->
+                com.google.android.gms.nearby.uwb.RangingParameters.RangingUpdateRate.FREQUENT
+            RangingParameters.RANGING_UPDATE_RATE_INFREQUENT ->
+                com.google.android.gms.nearby.uwb.RangingParameters.RangingUpdateRate.INFREQUENT
+            else ->
+                com.google.android.gms.nearby.uwb.RangingParameters.RangingUpdateRate.UNKNOWN
+        }
+        val parametersBuilder = com.google.android.gms.nearby.uwb.RangingParameters.Builder()
+            .setSessionId(parameters.sessionId)
+            .setUwbConfigId(configId)
+            .setRangingUpdateRate(updateRate)
+            .setSessionKeyInfo(parameters.sessionKeyInfo)
+            .setUwbConfigId(parameters.uwbConfigId)
+            .setComplexChannel(
+                parameters.complexChannel?.let {
+                    UwbComplexChannel.Builder()
+                        .setChannel(it.channel)
+                        .setPreambleIndex(it.preambleIndex)
+                        .build()
+                })
+        for (peer in parameters.peerDevices) {
+            parametersBuilder.addPeerDevice(UwbDevice.createForAddress(peer.address.address))
+        }
+        val callback =
+            object : RangingSessionCallback {
+                override fun onRangingInitialized(device: UwbDevice) {
+                    Log.i(TAG, "Started UWB ranging.")
+                }
+
+                override fun onRangingResult(device: UwbDevice, position: RangingPosition) {
+                    trySend(
+                        RangingResultPosition(
+                            androidx.core.uwb.UwbDevice(UwbAddress(device.address.address)),
+                            androidx.core.uwb.RangingPosition(
+                                RangingMeasurement(position.distance.value),
+                                position.azimuth?.let {
+                                    RangingMeasurement(it.value)
+                                },
+                                position.elevation?.let {
+                                    RangingMeasurement(it.value)
+                                },
+                                position.elapsedRealtimeNanos
+                            )
+                        )
+                    )
+                }
+
+                override fun onRangingSuspended(device: UwbDevice, reason: Int) {
+                    trySend(
+                        RangingResultPeerDisconnected(
+                            androidx.core.uwb.UwbDevice(UwbAddress(device.address.address))
+                        )
+                    )
+                }
+            }
+
+        try {
+            uwbClient.startRanging(parametersBuilder.build(), callback)
+            sessionStarted = true
+        } catch (e: ApiException) {
+            handleApiException(e)
+        }
+
+        awaitClose {
+            try {
+                uwbClient.stopRanging(callback)
+            } catch (e: ApiException) {
+                handleApiException(e)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbManagerImpl.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbManagerImpl.kt
new file mode 100644
index 0000000..09de8dc
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbManagerImpl.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb.impl
+
+import android.content.Context
+import androidx.core.uwb.RangingCapabilities
+import androidx.core.uwb.UwbAddress
+import androidx.core.uwb.UwbClientSessionScope
+import androidx.core.uwb.UwbManager
+import com.google.android.gms.common.api.ApiException
+import com.google.android.gms.nearby.Nearby
+import kotlinx.coroutines.tasks.await
+import androidx.core.uwb.helper.checkSystemFeature
+import androidx.core.uwb.helper.handleApiException
+
+internal class UwbManagerImpl(val context: Context) : UwbManager {
+    override suspend fun <R> clientSessionScope(
+        sessionHandler: suspend UwbClientSessionScope.() -> R
+    ): R {
+        // Check whether UWB hardware is available on the device.
+        checkSystemFeature(context)
+        val uwbClient = Nearby.getUwbControleeClient(context)
+        val localAddress: com.google.android.gms.nearby.uwb.UwbAddress
+        val rangingCapabilities: com.google.android.gms.nearby.uwb.RangingCapabilities
+        try {
+            localAddress = uwbClient.localAddress.await()
+            rangingCapabilities = uwbClient.rangingCapabilities.await()
+        } catch (e: ApiException) {
+            handleApiException(e)
+            throw RuntimeException("Unexpected error. This indicates that the library is not " +
+                "up-to-date with the service backend.")
+        }
+        return UwbClientSessionScopeImpl(
+            uwbClient,
+            RangingCapabilities(
+                rangingCapabilities.supportsDistance(),
+                rangingCapabilities.supportsAzimuthalAngle(),
+                rangingCapabilities.supportsElevationAngle()),
+            UwbAddress(localAddress.address)
+        ).sessionHandler()
+    }
+}
\ No newline at end of file