/* * 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.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; import android.os.SystemClock; import android.uwb.util.PersistableBundleUtils; import com.android.uwb.flags.Flags; 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 */ @SystemApi public final class RangingMeasurement implements Parcelable { public static final int RSSI_UNKNOWN = -128; public static final int RSSI_MIN = -127; public static final int RSSI_MAX = -1; @FlaggedApi(Flags.FLAG_UWB_FIRA_3_0_25Q4) public static final int NON_HYBRID_UWB_SESSION_ID = 0; private final UwbAddress mRemoteDeviceAddress; private final @Status int mStatus; private final long mElapsedRealtimeNanos; private final DistanceMeasurement mDistanceMeasurement; private final AngleOfArrivalMeasurement mAngleOfArrivalMeasurement; private final AngleOfArrivalMeasurement mDestinationAngleOfArrivalMeasurement; private final @LineOfSight int mLineOfSight; private final @MeasurementFocus int mMeasurementFocus; private final int mRssiDbm; private final long mHusPrimarySessionId; private final PersistableBundle mRangingMeasurementMetadata; private RangingMeasurement(@NonNull UwbAddress remoteDeviceAddress, @Status int status, long elapsedRealtimeNanos, @Nullable DistanceMeasurement distanceMeasurement, @Nullable AngleOfArrivalMeasurement angleOfArrivalMeasurement, @Nullable AngleOfArrivalMeasurement destinationAngleOfArrivalMeasurement, @LineOfSight int lineOfSight, @MeasurementFocus int measurementFocus, @IntRange(from = RSSI_UNKNOWN, to = RSSI_MAX) int rssiDbm, long husPrimarySessionId, PersistableBundle rangingMeasurementMetadata) { mRemoteDeviceAddress = remoteDeviceAddress; mStatus = status; mElapsedRealtimeNanos = elapsedRealtimeNanos; mDistanceMeasurement = distanceMeasurement; mAngleOfArrivalMeasurement = angleOfArrivalMeasurement; mDestinationAngleOfArrivalMeasurement = destinationAngleOfArrivalMeasurement; mLineOfSight = lineOfSight; mMeasurementFocus = measurementFocus; mRssiDbm = rssiDbm; mHusPrimarySessionId = husPrimarySessionId; mRangingMeasurementMetadata = rangingMeasurementMetadata; } /** * 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 * *
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; } /** * Get the angle of arrival measurement at the destination. * * @return an {@link AngleOfArrivalMeasurement} or null if {@link #getStatus()} != * {@link #RANGING_STATUS_SUCCESS} */ @Nullable public AngleOfArrivalMeasurement getDestinationAngleOfArrivalMeasurement() { return mDestinationAngleOfArrivalMeasurement; } /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { LOS, NLOS, LOS_UNDETERMINED}) public @interface LineOfSight {} /** * If measurement was in line of sight. */ public static final int LOS = 0; /** * If measurement was not in line of sight. */ public static final int NLOS = 1; /** * Unable to determine whether the measurement was in line of sight or not. */ public static final int LOS_UNDETERMINED = 0xFF; /** * Get whether the measurement was in Line of sight or non-line of sight. * * @return whether the measurement was in line of sight or not */ public @LineOfSight int getLineOfSight() { return mLineOfSight; } /** * Get the measured RSSI in dBm */ public @IntRange(from = RSSI_UNKNOWN, to = RSSI_MAX) int getRssiDbm() { return mRssiDbm; } /** * Gets the Hybrid UWB Session(HUS) Primary Session ID. * *
This API is only available on FIRA 3.0 compatible devices.
* *This field contains the Session ID of the HUS Primary Session. It is applicable only * if the session type is part of Hybrid UWB Scheduling (HUS).
* *If the UWB session is not part of Hybrid UWB Scheduling, this field will be * {@link #NON_HYBRID_UWB_SESSION_ID}.
* * @return The primary session ID for the Hybrid UWB Session (HUS). * @see Flags#FLAG_UWB_FIRA_3_0_25Q4 */ @FlaggedApi(Flags.FLAG_UWB_FIRA_3_0_25Q4) public long getHusPrimarySessionId() { return mHusPrimarySessionId; } /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { MEASUREMENT_FOCUS_NONE, MEASUREMENT_FOCUS_RANGE, MEASUREMENT_FOCUS_ANGLE_OF_ARRIVAL_AZIMUTH, MEASUREMENT_FOCUS_ANGLE_OF_ARRIVAL_ELEVATION}) public @interface MeasurementFocus {} /** * Ranging measurement was done with no particular focus in terms of antenna selection. */ public static final int MEASUREMENT_FOCUS_NONE = 0; /** * Ranging measurement was done with a focus on range calculation in terms of antenna * selection. */ public static final int MEASUREMENT_FOCUS_RANGE = 1; /** * Ranging measurement was done with a focus on Angle of arrival azimuth calculation in terms of * antenna selection. */ public static final int MEASUREMENT_FOCUS_ANGLE_OF_ARRIVAL_AZIMUTH = 2; /** * Ranging measurement was done with a focus on Angle of arrival elevation calculation in terms * of antenna selection. */ public static final int MEASUREMENT_FOCUS_ANGLE_OF_ARRIVAL_ELEVATION = 3; /** * Gets the measurement focus in terms of antenna used for this measurement. * * @return focus of this measurement. */ public @MeasurementFocus int getMeasurementFocus() { return mMeasurementFocus; } /** * Gets ranging measurement metadata passed by vendor * * @return vendor data for ranging measurement */ @NonNull public PersistableBundle getRangingMeasurementMetadata() { return mRangingMeasurementMetadata; } /** * @hide */ @Override public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } if (obj instanceof RangingMeasurement) { RangingMeasurement other = (RangingMeasurement) obj; boolean isHusPrimarySessionIdEqual = !Flags.uwbFira3025q4() || (mHusPrimarySessionId == other.getHusPrimarySessionId()); return Objects.equals(mRemoteDeviceAddress, other.getRemoteDeviceAddress()) && mStatus == other.getStatus() && mElapsedRealtimeNanos == other.getElapsedRealtimeNanos() && Objects.equals(mDistanceMeasurement, other.getDistanceMeasurement()) && Objects.equals( mAngleOfArrivalMeasurement, other.getAngleOfArrivalMeasurement()) && Objects.equals( mDestinationAngleOfArrivalMeasurement, other.getDestinationAngleOfArrivalMeasurement()) && mLineOfSight == other.getLineOfSight() && mMeasurementFocus == other.getMeasurementFocus() && mRssiDbm == other.getRssiDbm() && isHusPrimarySessionIdEqual && PersistableBundleUtils.isEqual(mRangingMeasurementMetadata, other.mRangingMeasurementMetadata); } return false; } /** * @hide */ @Override public int hashCode() { return Objects.hash(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos, mDistanceMeasurement, mAngleOfArrivalMeasurement, mDestinationAngleOfArrivalMeasurement, mLineOfSight, mMeasurementFocus, mRssiDbm, mHusPrimarySessionId, PersistableBundleUtils.getHashCode( mRangingMeasurementMetadata)); } @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); dest.writeParcelable(mDestinationAngleOfArrivalMeasurement, flags); dest.writeInt(mLineOfSight); dest.writeInt(mMeasurementFocus); dest.writeInt(mRssiDbm); dest.writeLong(mHusPrimarySessionId); dest.writePersistableBundle(mRangingMeasurementMetadata); } public static final @android.annotation.NonNull CreatorThis API is only available on FIRA 3.0 compatible devices.
* *This field represents the Session ID of the HUS Primary Session and is applicable * only when the session type is part of a Hybrid UWB Session.
* *If the UWB session is not part of Hybrid UWB Scheduling, this field is set to * {@link #NON_HYBRID_UWB_SESSION_ID}.
* * @param husPrimarySessionId The primary session ID for the Hybrid UWB Session (HUS). * @return The updated {@link Builder} instance. * @see Flags#FLAG_UWB_FIRA_3_0_25Q4 */ @NonNull @FlaggedApi(Flags.FLAG_UWB_FIRA_3_0_25Q4) public Builder setHusPrimarySessionId(long husPrimarySessionId) { mHusPrimarySessionId = husPrimarySessionId; return this; } /** * Set Ranging measurement metadata * * @param rangingMeasurementMetadata vendor data per ranging measurement * * @throws IllegalStateException if rangingMeasurementMetadata is null */ @NonNull public Builder setRangingMeasurementMetadata(@NonNull PersistableBundle rangingMeasurementMetadata) { if (rangingMeasurementMetadata == null) { throw new IllegalStateException("Expected non-null rangingMeasurementMetadata"); } mRangingMeasurementMetadata = rangingMeasurementMetadata; 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"); } // Destination AOA is optional according to the spec. } 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, mDestinationAngleOfArrivalMeasurement, mLineOfSight, mMeasurementFocus, mRssiDbm, mHusPrimarySessionId, mRangingMeasurementMetadata); } } }