blob: 50e5f0d8d554be010061d452046590d055c502d5 [file] [log] [blame]
/*
* 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.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
* Representation of a ranging measurement between the local device and a remote device
*
* @hide
*/
public final class RangingMeasurement implements Parcelable {
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}
*
* @return the remote device's {@link UwbAddress}
*/
@NonNull
public UwbAddress getRemoteDeviceAddress() {
return mRemoteDeviceAddress;
}
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
RANGING_STATUS_SUCCESS,
RANGING_STATUS_FAILURE_OUT_OF_RANGE,
RANGING_STATUS_FAILURE_UNKNOWN_ERROR})
public @interface Status {}
/**
* Ranging attempt was successful for this device
*/
public static final int RANGING_STATUS_SUCCESS = 0;
/**
* Ranging failed for this device because it is out of range
*/
public static final int RANGING_STATUS_FAILURE_OUT_OF_RANGE = 1;
/**
* Ranging failed for this device because of unknown error
*/
public static final int RANGING_STATUS_FAILURE_UNKNOWN_ERROR = -1;
/**
* Get the status of this ranging measurement
*
* <p>Possible values are
* {@link #RANGING_STATUS_SUCCESS},
* {@link #RANGING_STATUS_FAILURE_OUT_OF_RANGE},
* {@link #RANGING_STATUS_FAILURE_UNKNOWN_ERROR}.
*
* @return the status of the ranging measurement
*/
@Status
public int getStatus() {
return mStatus;
}
/**
* Timestamp of this ranging measurement in time since boot nanos in the same namespace as
* {@link SystemClock#elapsedRealtimeNanos()}
*
* @return timestamp of ranging measurement in nanoseconds
*/
@SuppressLint("MethodNameUnits")
public long getElapsedRealtimeNanos() {
return mElapsedRealtimeNanos;
}
/**
* Get the distance measurement
*
* @return a {@link DistanceMeasurement} or null if {@link #getStatus()} !=
* {@link #RANGING_STATUS_SUCCESS}
*/
@Nullable
public DistanceMeasurement getDistanceMeasurement() {
return mDistanceMeasurement;
}
/**
* Get the angle of arrival measurement
*
* @return an {@link AngleOfArrivalMeasurement} or null if {@link #getStatus()} !=
* {@link #RANGING_STATUS_SUCCESS}
*/
@Nullable
public AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() {
return mAngleOfArrivalMeasurement;
}
/**
* @hide
*/
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof RangingMeasurement) {
RangingMeasurement other = (RangingMeasurement) obj;
return mRemoteDeviceAddress.equals(other.getRemoteDeviceAddress())
&& mStatus == other.getStatus()
&& mElapsedRealtimeNanos == other.getElapsedRealtimeNanos()
&& mDistanceMeasurement.equals(other.getDistanceMeasurement())
&& mAngleOfArrivalMeasurement.equals(other.getAngleOfArrivalMeasurement());
}
return false;
}
/**
* @hide
*/
@Override
public int hashCode() {
return Objects.hash(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos,
mDistanceMeasurement, mAngleOfArrivalMeasurement);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mRemoteDeviceAddress, flags);
dest.writeInt(mStatus);
dest.writeLong(mElapsedRealtimeNanos);
dest.writeParcelable(mDistanceMeasurement, flags);
dest.writeParcelable(mAngleOfArrivalMeasurement, flags);
}
public static final @android.annotation.NonNull Creator<RangingMeasurement> CREATOR =
new Creator<RangingMeasurement>() {
@Override
public RangingMeasurement createFromParcel(Parcel in) {
Builder builder = new Builder();
builder.setRemoteDeviceAddress(
in.readParcelable(UwbAddress.class.getClassLoader()));
builder.setStatus(in.readInt());
builder.setElapsedRealtimeNanos(in.readLong());
builder.setDistanceMeasurement(
in.readParcelable(DistanceMeasurement.class.getClassLoader()));
builder.setAngleOfArrivalMeasurement(
in.readParcelable(AngleOfArrivalMeasurement.class.getClassLoader()));
return builder.build();
}
@Override
public RangingMeasurement[] newArray(int size) {
return new RangingMeasurement[size];
}
};
/**
* 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
*/
@NonNull
public Builder setRemoteDeviceAddress(@NonNull UwbAddress remoteDeviceAddress) {
mRemoteDeviceAddress = remoteDeviceAddress;
return this;
}
/**
* Set the status of ranging measurement
*
* @param status the status of the ranging measurement
*/
@NonNull
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
*/
@NonNull
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
*/
@NonNull
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
*/
@NonNull
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
*/
@NonNull
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);
}
}
}