summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/uwb/AngleMeasurement.java90
-rw-r--r--core/java/android/uwb/AngleOfArrivalMeasurement.java55
-rw-r--r--core/java/android/uwb/DistanceMeasurement.java90
-rw-r--r--core/java/android/uwb/RangingMeasurement.java126
-rw-r--r--core/java/android/uwb/RangingParams.java238
-rw-r--r--core/java/android/uwb/RangingReport.java59
-rw-r--r--core/java/android/uwb/RangingSession.java148
-rw-r--r--core/java/android/uwb/UwbManager.java29
8 files changed, 805 insertions, 30 deletions
diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java
index 7ef145cfe470..c3e2ecc9e0f3 100644
--- a/core/java/android/uwb/AngleMeasurement.java
+++ b/core/java/android/uwb/AngleMeasurement.java
@@ -27,6 +27,16 @@ import android.annotation.FloatRange;
* @hide
*/
public final class AngleMeasurement {
+ private final double mRadians;
+ private final double mErrorRadians;
+ private final double mConfidenceLevel;
+
+ private AngleMeasurement(double radians, double errorRadians, double confidenceLevel) {
+ mRadians = radians;
+ mErrorRadians = errorRadians;
+ mConfidenceLevel = confidenceLevel;
+ }
+
/**
* Angle measurement in radians
*
@@ -34,7 +44,7 @@ public final class AngleMeasurement {
*/
@FloatRange(from = -Math.PI, to = +Math.PI)
public double getRadians() {
- throw new UnsupportedOperationException();
+ return mRadians;
}
/**
@@ -46,7 +56,7 @@ public final class AngleMeasurement {
*/
@FloatRange(from = 0.0, to = +Math.PI)
public double getErrorRadians() {
- throw new UnsupportedOperationException();
+ return mErrorRadians;
}
/**
@@ -60,6 +70,80 @@ public final class AngleMeasurement {
*/
@FloatRange(from = 0.0, to = 1.0)
public double getConfidenceLevel() {
- throw new UnsupportedOperationException();
+ return mConfidenceLevel;
+ }
+
+ /**
+ * Builder class for {@link AngleMeasurement}.
+ */
+ public static final class Builder {
+ private double mRadians = Double.NaN;
+ private double mErrorRadians = Double.NaN;
+ private double mConfidenceLevel = Double.NaN;
+
+ /**
+ * Set the angle in radians
+ *
+ * @param radians angle in radians
+ * @throws IllegalArgumentException if angle exceeds allowed limits of [-Math.PI, +Math.PI]
+ */
+ public Builder setRadians(double radians) {
+ if (radians < -Math.PI || radians > Math.PI) {
+ throw new IllegalArgumentException("Invalid radians: " + radians);
+ }
+ mRadians = radians;
+ return this;
+ }
+
+ /**
+ * Set the angle error in radians
+ *
+ * @param errorRadians error of the angle in radians
+ * @throws IllegalArgumentException if the error exceeds the allowed limits of [0, +Math.PI]
+ */
+ public Builder setErrorRadians(double errorRadians) {
+ if (errorRadians < 0.0 || errorRadians > Math.PI) {
+ throw new IllegalArgumentException(
+ "Invalid error radians: " + errorRadians);
+ }
+ mErrorRadians = errorRadians;
+ return this;
+ }
+
+ /**
+ * Set the angle confidence level
+ *
+ * @param confidenceLevel level of confidence of the angle measurement
+ * @throws IllegalArgumentException if the error exceeds the allowed limits of [0.0, 1.0]
+ */
+ public Builder setConfidenceLevel(double confidenceLevel) {
+ if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
+ throw new IllegalArgumentException(
+ "Invalid confidence level: " + confidenceLevel);
+ }
+ mConfidenceLevel = confidenceLevel;
+ return this;
+ }
+
+ /**
+ * Build the {@link AngleMeasurement} object
+ *
+ * @throws IllegalStateException if angle, error, or confidence values are missing
+ */
+ public AngleMeasurement build() {
+ if (Double.isNaN(mRadians)) {
+ throw new IllegalStateException("Angle is not set");
+ }
+
+ if (Double.isNaN(mErrorRadians)) {
+ throw new IllegalStateException("Angle error is not set");
+ }
+
+ if (Double.isNaN(mConfidenceLevel)) {
+ throw new IllegalStateException("Angle confidence level is not set");
+ }
+
+ return new AngleMeasurement(mRadians, mErrorRadians, mConfidenceLevel);
+ }
}
}
diff --git a/core/java/android/uwb/AngleOfArrivalMeasurement.java b/core/java/android/uwb/AngleOfArrivalMeasurement.java
index 030d5299d53b..a7b5eae728cb 100644
--- a/core/java/android/uwb/AngleOfArrivalMeasurement.java
+++ b/core/java/android/uwb/AngleOfArrivalMeasurement.java
@@ -25,6 +25,15 @@ import android.annotation.Nullable;
* @hide
*/
public final class AngleOfArrivalMeasurement {
+ private final AngleMeasurement mAzimuthAngleMeasurement;
+ private final AngleMeasurement mAltitudeAngleMeasurement;
+
+ private AngleOfArrivalMeasurement(@NonNull AngleMeasurement azimuthAngleMeasurement,
+ @Nullable AngleMeasurement altitudeAngleMeasurement) {
+ mAzimuthAngleMeasurement = azimuthAngleMeasurement;
+ mAltitudeAngleMeasurement = altitudeAngleMeasurement;
+ }
+
/**
* Azimuth angle measurement
* <p>Azimuth {@link AngleMeasurement} of remote device in horizontal coordinate system, this is
@@ -41,7 +50,7 @@ public final class AngleOfArrivalMeasurement {
*/
@NonNull
public AngleMeasurement getAzimuth() {
- throw new UnsupportedOperationException();
+ return mAzimuthAngleMeasurement;
}
/**
@@ -58,6 +67,48 @@ public final class AngleOfArrivalMeasurement {
*/
@Nullable
public AngleMeasurement getAltitude() {
- throw new UnsupportedOperationException();
+ return mAltitudeAngleMeasurement;
+ }
+
+ /**
+ * Builder class for {@link AngleOfArrivalMeasurement}.
+ */
+ public static final class Builder {
+ private AngleMeasurement mAzimuthAngleMeasurement = null;
+ private AngleMeasurement mAltitudeAngleMeasurement = null;
+
+ /**
+ * Set the azimuth angle
+ *
+ * @param azimuthAngle azimuth angle
+ */
+ public Builder setAzimuthAngleMeasurement(@NonNull AngleMeasurement azimuthAngle) {
+ mAzimuthAngleMeasurement = azimuthAngle;
+ return this;
+ }
+
+ /**
+ * Set the altitude angle
+ *
+ * @param altitudeAngle altitude angle
+ */
+ public Builder setAltitudeAngleMeasurement(@NonNull AngleMeasurement altitudeAngle) {
+ mAltitudeAngleMeasurement = altitudeAngle;
+ return this;
+ }
+
+ /**
+ * Build the {@link AngleOfArrivalMeasurement} object
+ *
+ * @throws IllegalStateException if the required azimuth angle is not provided
+ */
+ public AngleOfArrivalMeasurement build() {
+ if (mAzimuthAngleMeasurement == null) {
+ throw new IllegalStateException("Azimuth angle measurement is not set");
+ }
+
+ return new AngleOfArrivalMeasurement(mAzimuthAngleMeasurement,
+ mAltitudeAngleMeasurement);
+ }
}
}
diff --git a/core/java/android/uwb/DistanceMeasurement.java b/core/java/android/uwb/DistanceMeasurement.java
index f4e6d3ed644b..4cd5d83f4efc 100644
--- a/core/java/android/uwb/DistanceMeasurement.java
+++ b/core/java/android/uwb/DistanceMeasurement.java
@@ -27,13 +27,23 @@ import android.annotation.FloatRange;
* @hide
*/
public final class DistanceMeasurement {
+ private final double mMeters;
+ private final double mErrorMeters;
+ private final double mConfidenceLevel;
+
+ private DistanceMeasurement(double meters, double errorMeters, double confidenceLevel) {
+ mMeters = meters;
+ mErrorMeters = errorMeters;
+ mConfidenceLevel = confidenceLevel;
+ }
+
/**
* Distance measurement in meters
*
* @return distance in meters
*/
public double getMeters() {
- throw new UnsupportedOperationException();
+ return mMeters;
}
/**
@@ -43,7 +53,7 @@ public final class DistanceMeasurement {
* @return error of distance measurement in meters
*/
public double getErrorMeters() {
- throw new UnsupportedOperationException();
+ return mErrorMeters;
}
/**
@@ -56,6 +66,80 @@ public final class DistanceMeasurement {
*/
@FloatRange(from = 0.0, to = 1.0)
public double getConfidenceLevel() {
- throw new UnsupportedOperationException();
+ return mConfidenceLevel;
+ }
+
+ /**
+ * Builder to get a {@link DistanceMeasurement} object.
+ */
+ public static final class Builder {
+ private double mMeters = Double.NaN;
+ private double mErrorMeters = Double.NaN;
+ private double mConfidenceLevel = Double.NaN;
+
+ /**
+ * Set the distance measurement in meters
+ *
+ * @param meters distance in meters
+ * @throws IllegalArgumentException if meters is NaN
+ */
+ public Builder setMeters(double meters) {
+ if (Double.isNaN(meters)) {
+ throw new IllegalArgumentException("meters cannot be NaN");
+ }
+ mMeters = meters;
+ return this;
+ }
+
+ /**
+ * Set the distance error in meters
+ *
+ * @param errorMeters distance error in meters
+ * @throws IllegalArgumentException if error is negative or NaN
+ */
+ public Builder setErrorMeters(double errorMeters) {
+ if (Double.isNaN(errorMeters) || errorMeters < 0.0) {
+ throw new IllegalArgumentException(
+ "errorMeters must be >= 0.0 and not NaN: " + errorMeters);
+ }
+ mErrorMeters = errorMeters;
+ return this;
+ }
+
+ /**
+ * Set the confidence level
+ *
+ * @param confidenceLevel the confidence level in the distance measurement
+ * @throws IllegalArgumentException if confidence level is not in the range of [0.0, 1.0]
+ */
+ public Builder setConfidenceLevel(double confidenceLevel) {
+ if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
+ throw new IllegalArgumentException(
+ "confidenceLevel must be in the range [0.0, 1.0]: " + confidenceLevel);
+ }
+ mConfidenceLevel = confidenceLevel;
+ return this;
+ }
+
+ /**
+ * Builds the {@link DistanceMeasurement} object
+ *
+ * @throws IllegalStateException if meters, error, or confidence are not set
+ */
+ public DistanceMeasurement build() {
+ if (Double.isNaN(mMeters)) {
+ throw new IllegalStateException("Meters cannot be NaN");
+ }
+
+ if (Double.isNaN(mErrorMeters)) {
+ throw new IllegalStateException("Error meters cannot be NaN");
+ }
+
+ if (Double.isNaN(mConfidenceLevel)) {
+ throw new IllegalStateException("Confidence level cannot be NaN");
+ }
+
+ return new DistanceMeasurement(mMeters, mErrorMeters, mConfidenceLevel);
+ }
}
}
diff --git a/core/java/android/uwb/RangingMeasurement.java b/core/java/android/uwb/RangingMeasurement.java
index a249802366e0..33a34e3954c2 100644
--- a/core/java/android/uwb/RangingMeasurement.java
+++ b/core/java/android/uwb/RangingMeasurement.java
@@ -31,6 +31,22 @@ import java.lang.annotation.RetentionPolicy;
* @hide
*/
public final class RangingMeasurement {
+ private final UwbAddress mRemoteDeviceAddress;
+ private final @Status int mStatus;
+ private final long mElapsedRealtimeNanos;
+ private final DistanceMeasurement mDistanceMeasurement;
+ private final AngleOfArrivalMeasurement mAngleOfArrivalMeasurement;
+
+ private RangingMeasurement(@NonNull UwbAddress remoteDeviceAddress, @Status int status,
+ long elapsedRealtimeNanos, @Nullable DistanceMeasurement distanceMeasurement,
+ @Nullable AngleOfArrivalMeasurement angleOfArrivalMeasurement) {
+ mRemoteDeviceAddress = remoteDeviceAddress;
+ mStatus = status;
+ mElapsedRealtimeNanos = elapsedRealtimeNanos;
+ mDistanceMeasurement = distanceMeasurement;
+ mAngleOfArrivalMeasurement = angleOfArrivalMeasurement;
+ }
+
/**
* Get the remote device's {@link UwbAddress}
*
@@ -38,7 +54,7 @@ public final class RangingMeasurement {
*/
@NonNull
public UwbAddress getRemoteDeviceAddress() {
- throw new UnsupportedOperationException();
+ return mRemoteDeviceAddress;
}
@Retention(RetentionPolicy.SOURCE)
@@ -75,7 +91,7 @@ public final class RangingMeasurement {
*/
@Status
public int getStatus() {
- throw new UnsupportedOperationException();
+ return mStatus;
}
/**
@@ -86,7 +102,7 @@ public final class RangingMeasurement {
*/
@SuppressLint("MethodNameUnits")
public long getElapsedRealtimeNanos() {
- throw new UnsupportedOperationException();
+ return mElapsedRealtimeNanos;
}
/**
@@ -97,7 +113,7 @@ public final class RangingMeasurement {
*/
@Nullable
public DistanceMeasurement getDistance() {
- throw new UnsupportedOperationException();
+ return mDistanceMeasurement;
}
/**
@@ -108,6 +124,106 @@ public final class RangingMeasurement {
*/
@Nullable
public AngleOfArrivalMeasurement getAngleOfArrival() {
- throw new UnsupportedOperationException();
+ return mAngleOfArrivalMeasurement;
+ }
+
+ /**
+ * Builder for a {@link RangingMeasurement} object.
+ */
+ public static final class Builder {
+ private UwbAddress mRemoteDeviceAddress = null;
+ private @Status int mStatus = RANGING_STATUS_FAILURE_UNKNOWN_ERROR;
+ private long mElapsedRealtimeNanos = -1L;
+ private DistanceMeasurement mDistanceMeasurement = null;
+ private AngleOfArrivalMeasurement mAngleOfArrivalMeasurement = null;
+
+ /**
+ * Set the remote device address that this measurement is for
+ *
+ * @param remoteDeviceAddress remote device's address
+ */
+ public Builder setRemoteDeviceAddress(@NonNull UwbAddress remoteDeviceAddress) {
+ mRemoteDeviceAddress = remoteDeviceAddress;
+ return this;
+ }
+
+ /**
+ * Set the status of ranging measurement
+ *
+ * @param status the status of the ranging measurement
+ */
+ public Builder setStatus(@Status int status) {
+ mStatus = status;
+ return this;
+ }
+
+ /**
+ * Set the elapsed realtime in nanoseconds when the ranging measurement occurred
+ *
+ * @param elapsedRealtimeNanos time the ranging measurement occurred
+ */
+ public Builder setElapsedRealtimeNanos(long elapsedRealtimeNanos) {
+ if (elapsedRealtimeNanos < 0) {
+ throw new IllegalArgumentException("elapsedRealtimeNanos must be >= 0");
+ }
+ mElapsedRealtimeNanos = elapsedRealtimeNanos;
+ return this;
+ }
+
+ /**
+ * Set the {@link DistanceMeasurement}
+ *
+ * @param distanceMeasurement the distance measurement for this ranging measurement
+ */
+ public Builder setDistanceMeasurement(@NonNull DistanceMeasurement distanceMeasurement) {
+ mDistanceMeasurement = distanceMeasurement;
+ return this;
+ }
+
+ /**
+ * Set the {@link AngleOfArrivalMeasurement}
+ *
+ * @param angleOfArrivalMeasurement the angle of arrival measurement for this ranging
+ * measurement
+ */
+ public Builder setAngleOfArrivalMeasurement(
+ @NonNull AngleOfArrivalMeasurement angleOfArrivalMeasurement) {
+ mAngleOfArrivalMeasurement = angleOfArrivalMeasurement;
+ return this;
+ }
+
+ /**
+ * Build the {@link RangingMeasurement} object
+ *
+ * @throws IllegalStateException if a distance or angle of arrival measurement is provided
+ * but the measurement was not successful, if the
+ * elapsedRealtimeNanos of the measurement is invalid, or
+ * if no remote device address is set
+ */
+ public RangingMeasurement build() {
+ if (mStatus != RANGING_STATUS_SUCCESS) {
+ if (mDistanceMeasurement != null) {
+ throw new IllegalStateException(
+ "Distance Measurement must be null if ranging is not successful");
+ }
+
+ if (mAngleOfArrivalMeasurement != null) {
+ throw new IllegalStateException(
+ "Angle of Arrival must be null if ranging is not successful");
+ }
+ }
+
+ if (mRemoteDeviceAddress == null) {
+ throw new IllegalStateException("No remote device address was set");
+ }
+
+ if (mElapsedRealtimeNanos < 0) {
+ throw new IllegalStateException(
+ "elapsedRealtimeNanos must be >=0: " + mElapsedRealtimeNanos);
+ }
+
+ return new RangingMeasurement(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos,
+ mDistanceMeasurement, mAngleOfArrivalMeasurement);
+ }
}
}
diff --git a/core/java/android/uwb/RangingParams.java b/core/java/android/uwb/RangingParams.java
index 9727696f0391..a50de3e61425 100644
--- a/core/java/android/uwb/RangingParams.java
+++ b/core/java/android/uwb/RangingParams.java
@@ -24,7 +24,11 @@ import android.util.Duration;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* An object used when requesting to open a new {@link RangingSession}.
@@ -33,11 +37,33 @@ import java.util.List;
* @hide
*/
public final class RangingParams {
- /**
- * Standard builder interface as the class is not modifiable
- */
- public static class Builder {
- // TODO implement
+ private final boolean mIsInitiator;
+ private final boolean mIsController;
+ private final Duration mSamplePeriod;
+ private final UwbAddress mLocalDeviceAddress;
+ private final List<UwbAddress> mRemoteDeviceAddresses;
+ private final int mChannelNumber;
+ private final int mTransmitPreambleCodeIndex;
+ private final int mReceivePreambleCodeIndex;
+ private final int mStsPhyPacketType;
+ private final PersistableBundle mSpecificationParameters;
+
+ private RangingParams(boolean isInitiator, boolean isController,
+ @NonNull Duration samplingPeriod, @NonNull UwbAddress localDeviceAddress,
+ @NonNull List<UwbAddress> remoteDeviceAddresses, int channelNumber,
+ int transmitPreambleCodeIndex, int receivePreambleCodeIndex,
+ @StsPhyPacketType int stsPhyPacketType,
+ @NonNull PersistableBundle specificationParameters) {
+ mIsInitiator = isInitiator;
+ mIsController = isController;
+ mSamplePeriod = samplingPeriod;
+ mLocalDeviceAddress = localDeviceAddress;
+ mRemoteDeviceAddresses = remoteDeviceAddresses;
+ mChannelNumber = channelNumber;
+ mTransmitPreambleCodeIndex = transmitPreambleCodeIndex;
+ mReceivePreambleCodeIndex = receivePreambleCodeIndex;
+ mStsPhyPacketType = stsPhyPacketType;
+ mSpecificationParameters = specificationParameters;
}
/**
@@ -46,7 +72,7 @@ public final class RangingParams {
* @return true if the device is the initiator
*/
public boolean isInitiator() {
- throw new UnsupportedOperationException();
+ return mIsInitiator;
}
/**
@@ -55,7 +81,7 @@ public final class RangingParams {
* @return true if the device is the controller
*/
public boolean isController() {
- throw new UnsupportedOperationException();
+ return mIsController;
}
/**
@@ -65,7 +91,7 @@ public final class RangingParams {
*/
@NonNull
public Duration getSamplingPeriod() {
- throw new UnsupportedOperationException();
+ return mSamplePeriod;
}
/**
@@ -78,7 +104,7 @@ public final class RangingParams {
*/
@NonNull
public UwbAddress getLocalDeviceAddress() {
- throw new UnsupportedOperationException();
+ return mLocalDeviceAddress;
}
/**
@@ -88,7 +114,7 @@ public final class RangingParams {
*/
@NonNull
public List<UwbAddress> getRemoteDeviceAddresses() {
- throw new UnsupportedOperationException();
+ return mRemoteDeviceAddresses;
}
/**
@@ -99,7 +125,7 @@ public final class RangingParams {
* @return the channel to use
*/
public int getChannelNumber() {
- throw new UnsupportedOperationException();
+ return mChannelNumber;
}
/**
@@ -110,7 +136,7 @@ public final class RangingParams {
* @return the preamble index to use for transmitting
*/
public int getTxPreambleIndex() {
- throw new UnsupportedOperationException();
+ return mTransmitPreambleCodeIndex;
}
/**
@@ -121,7 +147,7 @@ public final class RangingParams {
* @return the preamble index to use for receiving
*/
public int getRxPreambleIndex() {
- throw new UnsupportedOperationException();
+ return mReceivePreambleCodeIndex;
}
@Retention(RetentionPolicy.SOURCE)
@@ -159,7 +185,7 @@ public final class RangingParams {
*/
@StsPhyPacketType
public int getStsPhyPacketType() {
- throw new UnsupportedOperationException();
+ return mStsPhyPacketType;
}
/**
@@ -167,9 +193,189 @@ public final class RangingParams {
*
* <p>Android reserves the '^android.*' namespace
*
- * @return a {@link PersistableBundle} of protocol specific parameters
+ * @return a {@link PersistableBundle} copy of protocol specific parameters
*/
public @Nullable PersistableBundle getSpecificationParameters() {
- throw new UnsupportedOperationException();
+ return new PersistableBundle(mSpecificationParameters);
+ }
+
+ /**
+ * Builder class for {@link RangingParams}.
+ */
+ public static final class Builder {
+ private boolean mIsInitiator = false;
+ private boolean mIsController = false;
+ private Duration mSamplePeriod = null;
+ private UwbAddress mLocalDeviceAddress = null;
+ private List<UwbAddress> mRemoteDeviceAddresses = new ArrayList<>();
+ private int mChannelNumber = 0;
+ private int mTransmitPreambleCodeIndex = 0;
+ private int mReceivePreambleCodeIndex = 0;
+ private int mStsPhyPacketType = STS_PHY_PACKET_TYPE_SP0;
+ private PersistableBundle mSpecificationParameters = new PersistableBundle();
+
+ /**
+ * Set whether the device is the initiator or responder as defined by IEEE 802.15.4z
+ *
+ * @param isInitiator whether the device is the initiator (true) or responder (false)
+ */
+ public Builder setIsInitiator(boolean isInitiator) {
+ mIsInitiator = isInitiator;
+ return this;
+ }
+
+ /**
+ * Set whether the local device is the controller or controlee as defined by IEEE 802.15.4z
+ *
+ * @param isController whether the device is the controller (true) or controlee (false)
+ */
+ public Builder setIsController(boolean isController) {
+ mIsController = isController;
+ return this;
+ }
+
+ /**
+ * Set the time between ranging samples
+ *
+ * @param samplePeriod the time between ranging samples
+ */
+ public Builder setSamplePeriod(@NonNull Duration samplePeriod) {
+ mSamplePeriod = samplePeriod;
+ return this;
+ }
+
+ /**
+ * Set the local device address
+ *
+ * @param localDeviceAddress the local device's address for the {@link RangingSession}
+ */
+ public Builder setLocalDeviceAddress(@NonNull UwbAddress localDeviceAddress) {
+ mLocalDeviceAddress = localDeviceAddress;
+ return this;
+ }
+
+ /**
+ * Add a remote device's address to the ranging session
+ *
+ * @param remoteDeviceAddress a remote device's address for the {@link RangingSession}
+ * @throws IllegalArgumentException if {@code remoteDeviceAddress} is already present.
+ */
+ public Builder addRemoteDeviceAddress(@NonNull UwbAddress remoteDeviceAddress) {
+ if (mRemoteDeviceAddresses.contains(remoteDeviceAddress)) {
+ throw new IllegalArgumentException(
+ "Remote device address already added: " + remoteDeviceAddress.toString());
+ }
+ mRemoteDeviceAddresses.add(remoteDeviceAddress);
+ return this;
+ }
+
+ /**
+ * Set the IEEE 802.15.4z channel to use for the {@link RangingSession}
+ * <p>Valid values are in the range [-1, 15]
+ *
+ * @param channelNumber the channel to use for the {@link RangingSession}
+ * @throws IllegalArgumentException if {@code channelNumber} is invalid.
+ */
+ public Builder setChannelNumber(int channelNumber) {
+ if (channelNumber < -1 || channelNumber > 15) {
+ throw new IllegalArgumentException("Invalid channel number");
+ }
+ mChannelNumber = channelNumber;
+ return this;
+ }
+
+ private static final Set<Integer> VALID_TX_PREAMBLE_CODES = new HashSet<Integer>(
+ Arrays.asList(0, 13, 14, 15, 16, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32));
+
+ /**
+ * Set the IEEE 802.15.4z preamble code index to use when transmitting
+ *
+ * <p>Valid values are in the ranges: [0], [13-16], [21-32]
+ *
+ * @param transmitPreambleCodeIndex preamble code index to use for transmitting
+ * @throws IllegalArgumentException if {@code transmitPreambleCodeIndex} is invalid.
+ */
+ public Builder setTransmitPreambleCodeIndex(int transmitPreambleCodeIndex) {
+ if (!VALID_TX_PREAMBLE_CODES.contains(transmitPreambleCodeIndex)) {
+ throw new IllegalArgumentException(
+ "Invalid transmit preamble: " + transmitPreambleCodeIndex);
+ }
+ mTransmitPreambleCodeIndex = transmitPreambleCodeIndex;
+ return this;
+ }
+
+ private static final Set<Integer> VALID_RX_PREAMBLE_CODES = new HashSet<Integer>(
+ Arrays.asList(0, 16, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32));
+
+ /**
+ * Set the IEEE 802.15.4z preamble code index to use when receiving
+ *
+ * Valid values are in the ranges: [0], [16-32]
+ *
+ * @param receivePreambleCodeIndex preamble code index to use for receiving
+ * @throws IllegalArgumentException if {@code receivePreambleCodeIndex} is invalid.
+ */
+ public Builder setReceivePreambleCodeIndex(int receivePreambleCodeIndex) {
+ if (!VALID_RX_PREAMBLE_CODES.contains(receivePreambleCodeIndex)) {
+ throw new IllegalArgumentException(
+ "Invalid receive preamble: " + receivePreambleCodeIndex);
+ }
+ mReceivePreambleCodeIndex = receivePreambleCodeIndex;
+ return this;
+ }
+
+ /**
+ * Set the IEEE 802.15.4z PHY packet type when STS is used
+ *
+ * @param stsPhyPacketType PHY packet type when STS is used
+ * @throws IllegalArgumentException if {@code stsPhyPacketType} is invalid.
+ */
+ public Builder setStsPhPacketType(@StsPhyPacketType int stsPhyPacketType) {
+ if (stsPhyPacketType != STS_PHY_PACKET_TYPE_SP0
+ && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP1
+ && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP2
+ && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP3) {
+ throw new IllegalArgumentException("unknown StsPhyPacketType: " + stsPhyPacketType);
+ }
+
+ mStsPhyPacketType = stsPhyPacketType;
+ return this;
+ }
+
+ /**
+ * Set the specification parameters
+ *
+ * <p>Creates a copy of the parameters
+ *
+ * @param parameters specification parameters built from support library
+ */
+ public Builder setSpecificationParameters(@NonNull PersistableBundle parameters) {
+ mSpecificationParameters = new PersistableBundle(parameters);
+ return this;
+ }
+
+ /**
+ * Build the {@link RangingParams} object.
+ *
+ * @throws IllegalStateException if required parameters are missing
+ */
+ public RangingParams build() {
+ if (mSamplePeriod == null) {
+ throw new IllegalStateException("No sample period provided");
+ }
+
+ if (mLocalDeviceAddress == null) {
+ throw new IllegalStateException("Local device address not provided");
+ }
+
+ if (mRemoteDeviceAddresses.size() == 0) {
+ throw new IllegalStateException("No remote device address(es) provided");
+ }
+
+ return new RangingParams(mIsInitiator, mIsController, mSamplePeriod,
+ mLocalDeviceAddress, mRemoteDeviceAddresses, mChannelNumber,
+ mTransmitPreambleCodeIndex, mReceivePreambleCodeIndex, mStsPhyPacketType,
+ mSpecificationParameters);
+ }
}
}
diff --git a/core/java/android/uwb/RangingReport.java b/core/java/android/uwb/RangingReport.java
index 037bdfd5f224..5aca12aaf2cf 100644
--- a/core/java/android/uwb/RangingReport.java
+++ b/core/java/android/uwb/RangingReport.java
@@ -18,6 +18,7 @@ package android.uwb;
import android.annotation.NonNull;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -26,6 +27,12 @@ import java.util.List;
* @hide
*/
public final class RangingReport {
+ private final List<RangingMeasurement> mRangingMeasurements;
+
+ private RangingReport(@NonNull List<RangingMeasurement> rangingMeasurements) {
+ mRangingMeasurements = rangingMeasurements;
+ }
+
/**
* Get a {@link List} of {@link RangingMeasurement} objects in the last measurement interval
* <p>The underlying UWB adapter may choose to do multiple measurements in each ranging
@@ -38,7 +45,57 @@ public final class RangingReport {
*/
@NonNull
public List<RangingMeasurement> getMeasurements() {
- throw new UnsupportedOperationException();
+ return mRangingMeasurements;
+ }
+
+ /**
+ * Builder for {@link RangingReport} object
+ */
+ public static final class Builder {
+ List<RangingMeasurement> mMeasurements = new ArrayList<>();
+
+ /**
+ * Add a single {@link RangingMeasurement}
+ *
+ * @param rangingMeasurement a ranging measurement
+ */
+ public Builder addMeasurement(@NonNull RangingMeasurement rangingMeasurement) {
+ mMeasurements.add(rangingMeasurement);
+ return this;
+ }
+
+ /**
+ * Add a {@link List} of {@link RangingMeasurement}s
+ *
+ * @param rangingMeasurements {@link List} of {@link RangingMeasurement}s to add
+ */
+ public Builder addMeasurements(@NonNull List<RangingMeasurement> rangingMeasurements) {
+ mMeasurements.addAll(rangingMeasurements);
+ return this;
+ }
+
+ /**
+ * Build the {@link RangingReport} object
+ *
+ * @throws IllegalStateException if measurements are not in monotonically increasing order
+ */
+ public RangingReport build() {
+ // Verify that all measurement timestamps are monotonically increasing
+ RangingMeasurement prevMeasurement = null;
+ for (int curIndex = 0; curIndex < mMeasurements.size(); curIndex++) {
+ RangingMeasurement curMeasurement = mMeasurements.get(curIndex);
+ if (prevMeasurement != null
+ && (prevMeasurement.getElapsedRealtimeNanos()
+ > curMeasurement.getElapsedRealtimeNanos())) {
+ throw new IllegalStateException(
+ "Timestamp (" + curMeasurement.getElapsedRealtimeNanos()
+ + ") at index " + curIndex + " is less than previous timestamp ("
+ + prevMeasurement.getElapsedRealtimeNanos() + ")");
+ }
+ prevMeasurement = curMeasurement;
+ }
+ return new RangingReport(mMeasurements);
+ }
}
}
diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java
new file mode 100644
index 000000000000..f4033fe0d29e
--- /dev/null
+++ b/core/java/android/uwb/RangingSession.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2020 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 android.uwb;
+
+import android.annotation.IntDef;
+import android.os.PersistableBundle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * This class provides a way to control an active UWB ranging session.
+ * <p>It also defines the required {@link RangingSession.Callback} that must be implemented
+ * in order to be notified of UWB ranging results and status events related to the
+ * {@link RangingSession}.
+ *
+ * <p>To get an instance of {@link RangingSession}, first use
+ * {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)} to request to open a
+ * session. Once the session is opened, a {@link RangingSession} object is provided through
+ * {@link RangingSession.Callback#onOpenSuccess(RangingSession, PersistableBundle)}. If opening a
+ * session fails, the failure is reported through {@link RangingSession.Callback#onClosed(int)} with
+ * the failure reason.
+ *
+ * @hide
+ */
+public final class RangingSession implements AutoCloseable {
+ /**
+ * Interface for receiving {@link RangingSession} events
+ */
+ public interface Callback {
+ /**
+ * Invoked when {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)}
+ * is successful
+ *
+ * @param session the newly opened {@link RangingSession}
+ * @param sessionInfo session specific parameters from lower layers
+ */
+ void onOpenSuccess(RangingSession session, PersistableBundle sessionInfo);
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ CLOSE_REASON_UNKNOWN,
+ CLOSE_REASON_LOCAL_CLOSE_API,
+ CLOSE_REASON_LOCAL_BAD_PARAMETERS,
+ CLOSE_REASON_LOCAL_GENERIC_ERROR,
+ CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED,
+ CLOSE_REASON_LOCAL_SYSTEM_POLICY,
+ CLOSE_REASON_REMOTE_GENERIC_ERROR,
+ CLOSE_REASON_REMOTE_REQUEST})
+ @interface CloseReason {}
+
+ /**
+ * Indicates that the session was closed or failed to open due to an unknown reason
+ */
+ int CLOSE_REASON_UNKNOWN = 0;
+
+ /**
+ * Indicates that the session was closed or failed to open because
+ * {@link AutoCloseable#close()} or {@link RangingSession#close()} was called
+ */
+ int CLOSE_REASON_LOCAL_CLOSE_API = 1;
+
+ /**
+ * Indicates that the session failed to open due to erroneous parameters passed
+ * to {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)}
+ */
+ int CLOSE_REASON_LOCAL_BAD_PARAMETERS = 2;
+
+ /**
+ * Indicates that the session was closed due to some local error on this device besides the
+ * error code already listed
+ */
+ int CLOSE_REASON_LOCAL_GENERIC_ERROR = 3;
+
+ /**
+ * Indicates that the session failed to open because the number of currently open sessions
+ * is equal to {@link UwbManager#getMaxSimultaneousSessions()}
+ */
+ int CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED = 4;
+
+ /**
+ * Indicates that the session was closed or failed to open due to local system policy, such
+ * as privacy policy, power management policy, permissions, and more.
+ */
+ int CLOSE_REASON_LOCAL_SYSTEM_POLICY = 5;
+
+ /**
+ * Indicates that the session was closed or failed to open due to an error with the remote
+ * device besides error codes already listed.
+ */
+ int CLOSE_REASON_REMOTE_GENERIC_ERROR = 6;
+
+ /**
+ * Indicates that the session was closed or failed to open due to an explicit request from
+ * the remote device.
+ */
+ int CLOSE_REASON_REMOTE_REQUEST = 7;
+
+ /**
+ * Invoked when session is either closed spontaneously, or per user request via
+ * {@link RangingSession#close()} or {@link AutoCloseable#close()}, or when session failed
+ * to open.
+ *
+ * @param reason reason for the session closure
+ */
+ void onClosed(@CloseReason int reason);
+
+ /**
+ * Called once per ranging interval even when a ranging measurement fails
+ *
+ * @param rangingReport ranging report for this interval's measurements
+ */
+ void onReportReceived(RangingReport rangingReport);
+ }
+
+ /**
+ * Close the ranging session
+ * <p>If this session is currently open, it will close and stop the session.
+ * <p>If the session is in the process of being opened, it will attempt to stop the session from
+ * being opened.
+ * <p>If the session is already closed, the registered {@link Callback#onClosed(int)} callback
+ * will still be invoked.
+ *
+ * <p>{@link Callback#onClosed(int)} will be invoked using the same callback
+ * object given to {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)} when
+ * the {@link RangingSession} was opened. The callback will be invoked after each call to
+ * {@link #close()}, even if the {@link RangingSession} is already closed.
+ */
+ @Override
+ public void close() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index 8097dc6dca11..d58d5bfd8de3 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -266,4 +266,33 @@ public final class UwbManager {
public int getMaxRemoteDevicesPerResponderSession() {
throw new UnsupportedOperationException();
}
+
+ /**
+ * Open a {@link RangingSession} with the given parameters
+ * <p>This function is asynchronous and will return before ranging begins. The
+ * {@link RangingSession.Callback#onOpenSuccess(RangingSession, PersistableBundle)} function is
+ * called with a {@link RangingSession} object used to control ranging when the session is
+ * successfully opened.
+ *
+ * <p>If a session cannot be opened, then {@link RangingSession.Callback#onClosed(int)} will be
+ * invoked with the appropriate {@link RangingSession.Callback.CloseReason}.
+ *
+ * <p>An open {@link RangingSession} will be automatically closed if client application process
+ * dies.
+ *
+ * @param params {@link RangingParams} used to initialize this {@link RangingSession}
+ * @param executor {@link Executor} to run callbacks
+ * @param callbacks {@link RangingSession.Callback} to associate with the
+ * {@link RangingSession} that is being opened.
+ *
+ * @return an {@link AutoCloseable} that is able to be used to close or cancel the opening of a
+ * {@link RangingSession} that has been requested through {@link #openRangingSession}
+ * but has not yet been made available by
+ * {@link RangingSession.Callback#onOpenSuccess}.
+ */
+ @NonNull
+ public AutoCloseable openRangingSession(@NonNull RangingParams params,
+ @NonNull Executor executor, @NonNull RangingSession.Callback callbacks) {
+ throw new UnsupportedOperationException();
+ }
}