/* * Copyright (C) 2024 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.net.wifi; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; import com.android.wifi.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** * Additional QoS parameters as defined in Section 9.4.2.316 * of the IEEE P802.11be/D4.0 Standard. * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public final class QosCharacteristics implements Parcelable { private static final String TAG = "QosCharacteristics"; /** * Maximum size of an MSDU. * @hide */ public static final int MAX_MSDU_SIZE = 1 << 0; /** * Anticipated time and link ID indicating when traffic starts for the associated TID. * @hide */ public static final int SERVICE_START_TIME = 1 << 1; /** * Data rate specified for transport of MSDUs or A-MSDUs belonging to the traffic flow. * @hide */ public static final int MEAN_DATA_RATE = 1 << 2; /** * Maximum burst of the MSDUs or A-MSDUs belonging to the traffic flow. * @hide */ public static final int BURST_SIZE = 1 << 3; /** * Maximum amount of time beyond which the MSDU is not useful. * @hide */ public static final int MSDU_LIFETIME = 1 << 4; /** * MSDU delivery information. * @hide */ public static final int MSDU_DELIVERY_INFO = 1 << 5; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, value = { MAX_MSDU_SIZE, SERVICE_START_TIME, MEAN_DATA_RATE, BURST_SIZE, MSDU_LIFETIME, MSDU_DELIVERY_INFO, }) public @interface QosCharacteristicsOptionalField {} /** * Indicates that 95% of MSDUs are expected to be delivered. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int DELIVERY_RATIO_95 = 0; /** * Indicates that 96% of MSDUs are expected to be delivered. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int DELIVERY_RATIO_96 = 1; /** * Indicates that 97% of MSDUs are expected to be delivered. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int DELIVERY_RATIO_97 = 2; /** * Indicates that 98% of MSDUs are expected to be delivered. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int DELIVERY_RATIO_98 = 3; /** * Indicates that 99% of MSDUs are expected to be delivered. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int DELIVERY_RATIO_99 = 5; /** * Indicates that 99.9% of MSDUs are expected to be delivered. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int DELIVERY_RATIO_99_9 = 6; /** * Indicates that 99.99% of MSDUs are expected to be delivered. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int DELIVERY_RATIO_99_99 = 7; /** * Indicates that 99.999% of MSDUs are expected to be delivered. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int DELIVERY_RATIO_99_999 = 8; /** * Indicates that 99.9999% of MSDUs are expected to be delivered. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int DELIVERY_RATIO_99_9999 = 9; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { DELIVERY_RATIO_95, DELIVERY_RATIO_96, DELIVERY_RATIO_97, DELIVERY_RATIO_98, DELIVERY_RATIO_99, DELIVERY_RATIO_99_9, DELIVERY_RATIO_99_99, DELIVERY_RATIO_99_999, DELIVERY_RATIO_99_9999, }) public @interface DeliveryRatio {} private final int mMinServiceIntervalMicros; private final int mMaxServiceIntervalMicros; private final int mMinDataRateKbps; private final int mDelayBoundMicros; private final int mOptionalFieldBitmap; private final int mMaxMsduSizeOctets; private final int mServiceStartTimeMicros; private final int mServiceStartTimeLinkId; private final int mMeanDataRateKbps; private final int mBurstSizeOctets; private final int mMsduLifetimeMillis; private final @DeliveryRatio int mDeliveryRatio; private final int mCountExponent; private QosCharacteristics(int minServiceIntervalMicros, int maxServiceIntervalMicros, int minDataRateKbps, int delayBoundMicros, int optionalFieldBitmap, int maxMsduSizeOctets, int serviceStartTimeMicros, int serviceStartTimeLinkId, int meanDataRateKbps, int burstSizeOctets, int msduLifetimeMillis, @DeliveryRatio int deliveryRatio, int countExponent) { mMinServiceIntervalMicros = minServiceIntervalMicros; mMaxServiceIntervalMicros = maxServiceIntervalMicros; mMinDataRateKbps = minDataRateKbps; mDelayBoundMicros = delayBoundMicros; mOptionalFieldBitmap = optionalFieldBitmap; mMaxMsduSizeOctets = maxMsduSizeOctets; mServiceStartTimeMicros = serviceStartTimeMicros; mServiceStartTimeLinkId = serviceStartTimeLinkId; mMeanDataRateKbps = meanDataRateKbps; mBurstSizeOctets = burstSizeOctets; mMsduLifetimeMillis = msduLifetimeMillis; mDeliveryRatio = deliveryRatio; mCountExponent = countExponent; } /** * Check whether this instance contains the indicated optional field. * @hide */ public boolean containsOptionalField(@QosCharacteristicsOptionalField int field) { return (mOptionalFieldBitmap & field) != 0; } /** * Check that the value can be cast to an unsigned integer of size |expectedSizeInBits|. */ private static boolean checkSizeInBits(int value, int expectedSizeInBits) { int bitmask = (0x1 << expectedSizeInBits) - 1; return (bitmask & value) == value; } /** * Check that the value falls within the provided inclusive range. */ private static boolean checkIntRange(int value, int min, int max) { return (value >= min) && (value <= max); } /** * Validate the parameters in this instance. * @hide */ public boolean validate() { if (mMinServiceIntervalMicros <= 0 || mMaxServiceIntervalMicros <= 0 || mMinDataRateKbps <= 0 || mDelayBoundMicros <= 0) { Log.e(TAG, "All mandatory fields must be positive integers"); return false; } if (mMinServiceIntervalMicros > mMaxServiceIntervalMicros) { Log.e(TAG, "Minimum service interval must be less than or equal to" + " the maximum service interval"); return false; } if (containsOptionalField(MAX_MSDU_SIZE) && !checkIntRange(mMaxMsduSizeOctets, 1, Short.MAX_VALUE)) { Log.e(TAG, "Invalid value provided for maxMsduSize"); return false; } if (containsOptionalField(SERVICE_START_TIME) && (mServiceStartTimeMicros < 0 || !checkSizeInBits(mServiceStartTimeLinkId, 4))) { Log.e(TAG, "serviceStartTime information is invalid"); return false; } if (containsOptionalField(MEAN_DATA_RATE) && mMeanDataRateKbps <= 0) { Log.e(TAG, "meanDataRateKbps must be a positive integer"); return false; } if (containsOptionalField(BURST_SIZE) && mBurstSizeOctets == 0) { Log.e(TAG, "burstSizeOctets must be non-zero"); return false; } if (containsOptionalField(MSDU_LIFETIME) && !checkIntRange(mMsduLifetimeMillis, 1, Short.MAX_VALUE)) { Log.e(TAG, "Invalid value provided for msduLifetimeMillis"); return false; } if (containsOptionalField(MSDU_LIFETIME) && (mMsduLifetimeMillis * 1000) < mDelayBoundMicros) { Log.e(TAG, "MSDU lifetime must be greater than or equal to the delay bound"); return false; } if (containsOptionalField(MSDU_DELIVERY_INFO) && !checkIntRange(mDeliveryRatio, DELIVERY_RATIO_95, DELIVERY_RATIO_99_9999)) { Log.e(TAG, "MSDU delivery ratio must be a valid enum value"); return false; } if (containsOptionalField(MSDU_DELIVERY_INFO) && !checkIntRange(mCountExponent, 0, 15)) { Log.e(TAG, "MSDU count exponent must be between 0 and 15 (inclusive)"); return false; } return true; } /** * Get the minimum service interval in microseconds. * * See {@link Builder#Builder(int, int, int, int)} for more information. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @IntRange(from = 1, to = Integer.MAX_VALUE) public int getMinServiceIntervalMicros() { return mMinServiceIntervalMicros; } /** * Get the maximum service interval in microseconds. * * See {@link Builder#Builder(int, int, int, int)} for more information. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @IntRange(from = 1, to = Integer.MAX_VALUE) public int getMaxServiceIntervalMicros() { return mMaxServiceIntervalMicros; } /** * Get the minimum data rate in kilobits per second. * * See {@link Builder#Builder(int, int, int, int)} for more information. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @IntRange(from = 1, to = Integer.MAX_VALUE) public int getMinDataRateKbps() { return mMinDataRateKbps; } /** * Get the delay bound in microseconds. * * See {@link Builder#Builder(int, int, int, int)} for more information. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @IntRange(from = 1, to = Integer.MAX_VALUE) public int getDelayBoundMicros() { return mDelayBoundMicros; } /** * Get the maximum MSDU size in octets. See {@link Builder#setMaxMsduSizeOctets(int)} * for more information. * * @throws IllegalStateException if this field was not set in the Builder. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @IntRange(from = 1, to = Short.MAX_VALUE) public int getMaxMsduSizeOctets() { if (!containsOptionalField(MAX_MSDU_SIZE)) { throw new IllegalStateException("maxMsduSize was not provided in the builder"); } return mMaxMsduSizeOctets; } /** * Get the service start time in microseconds. * See {@link Builder#setServiceStartTimeInfo(int, int)} for more information. * * @throws IllegalStateException if this field was not set in the Builder. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @IntRange(from = 0, to = Integer.MAX_VALUE) public int getServiceStartTimeMicros() { if (!containsOptionalField(SERVICE_START_TIME)) { throw new IllegalStateException("serviceStartTime was not provided in the builder"); } return mServiceStartTimeMicros; } /** * Get the service start time link ID. See {@link Builder#setServiceStartTimeInfo(int, int)} * for more information. * * @throws IllegalStateException if this field was not set in the Builder. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public int getServiceStartTimeLinkId() { if (!containsOptionalField(SERVICE_START_TIME)) { throw new IllegalStateException("serviceStartTime was not provided in the builder"); } return mServiceStartTimeLinkId; } /** * Get the mean data rate in kilobits per second. See {@link Builder#setMeanDataRateKbps(int)} * for more information. * * @throws IllegalStateException if this field was not set in the Builder. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @IntRange(from = 1, to = Integer.MAX_VALUE) public int getMeanDataRateKbps() { if (!containsOptionalField(MEAN_DATA_RATE)) { throw new IllegalStateException("meanDataRate was not provided in the builder"); } return mMeanDataRateKbps; } /** * Get the burst size in octets. See {@link Builder#setBurstSizeOctets(int)} for * more information. * * @throws IllegalStateException if this field was not set in the Builder. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @IntRange(from = 1, to = Integer.MAX_VALUE) public int getBurstSizeOctets() { if (!containsOptionalField(BURST_SIZE)) { throw new IllegalStateException("burstSize was not provided in the builder"); } return mBurstSizeOctets; } /** * Get the MSDU lifetime in milliseconds. See {@link Builder#setMsduLifetimeMillis(int)} * for more information. * * @throws IllegalStateException if this field was not set in the Builder. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @IntRange(from = 1, to = Short.MAX_VALUE) public int getMsduLifetimeMillis() { if (!containsOptionalField(MSDU_LIFETIME)) { throw new IllegalStateException("msduLifetime was not provided in the builder"); } return mMsduLifetimeMillis; } /** * Get the delivery ratio enum. See {@link Builder#setMsduDeliveryInfo(int, int)} for * more information. * * @throws IllegalStateException if this field was not set in the Builder. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public @DeliveryRatio int getDeliveryRatio() { if (!containsOptionalField(MSDU_DELIVERY_INFO)) { throw new IllegalStateException("msduDeliveryInfo was not provided in the builder"); } return mDeliveryRatio; } /** * Get the count exponent. See {@link Builder#setMsduDeliveryInfo(int, int)} for * more information. * * @throws IllegalStateException if this field was not set in the Builder. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @IntRange(from = 0, to = 15) public int getCountExponent() { if (!containsOptionalField(MSDU_DELIVERY_INFO)) { throw new IllegalStateException("msduDeliveryInfo was not provided in the builder"); } return mCountExponent; } @Override @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; QosCharacteristics that = (QosCharacteristics) o; return mMinServiceIntervalMicros == that.mMinServiceIntervalMicros && mMaxServiceIntervalMicros == that.mMaxServiceIntervalMicros && mMinDataRateKbps == that.mMinDataRateKbps && mDelayBoundMicros == that.mDelayBoundMicros && mOptionalFieldBitmap == that.mOptionalFieldBitmap && mMaxMsduSizeOctets == that.mMaxMsduSizeOctets && mServiceStartTimeMicros == that.mServiceStartTimeMicros && mServiceStartTimeLinkId == that.mServiceStartTimeLinkId && mMeanDataRateKbps == that.mMeanDataRateKbps && mBurstSizeOctets == that.mBurstSizeOctets && mMsduLifetimeMillis == that.mMsduLifetimeMillis && mDeliveryRatio == that.mDeliveryRatio && mCountExponent == that.mCountExponent; } @Override @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public int hashCode() { return Objects.hash(mMinServiceIntervalMicros, mMaxServiceIntervalMicros, mMinDataRateKbps, mDelayBoundMicros, mOptionalFieldBitmap, mMaxMsduSizeOctets, mServiceStartTimeMicros, mServiceStartTimeLinkId, mMeanDataRateKbps, mBurstSizeOctets, mMsduLifetimeMillis, mDeliveryRatio, mCountExponent); } @Override @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public String toString() { StringBuilder sb = new StringBuilder("{"); sb.append("mMinServiceIntervalMicros=").append(mMinServiceIntervalMicros); sb.append(", mMaxServiceIntervalMicros=").append(mMaxServiceIntervalMicros); sb.append(", mMinDataRateKbps=").append(mMinDataRateKbps); sb.append(", mDelayBoundMicros=").append(mDelayBoundMicros); sb.append(", mOptionalFieldBitmap=").append(String.format("%02x", mOptionalFieldBitmap)); if (containsOptionalField(MAX_MSDU_SIZE)) { sb.append(", mMaxMsduSizeOctets=").append(mMaxMsduSizeOctets); } if (containsOptionalField(SERVICE_START_TIME)) { sb.append(", mServiceStartTimeMicros=").append(mServiceStartTimeMicros); sb.append(", mServiceStartTimeLinkId="); sb.append(String.format("%01x", mServiceStartTimeLinkId)); } if (containsOptionalField(MEAN_DATA_RATE)) { sb.append(", mMeanDataRateKbps=").append(mMeanDataRateKbps); } if (containsOptionalField(BURST_SIZE)) { sb.append(", mBurstSizeOctets=").append(mBurstSizeOctets); } if (containsOptionalField(MSDU_LIFETIME)) { sb.append(", mMsduLifetimeMillis=").append(mMsduLifetimeMillis); } if (containsOptionalField(MSDU_DELIVERY_INFO)) { sb.append(", mDeliveryRatio=").append(mDeliveryRatio); sb.append(", mCountExponent=").append(mCountExponent); } return sb.append("}").toString(); } @Override @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public int describeContents() { return 0; } /** @hide */ @Override @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mMinServiceIntervalMicros); dest.writeInt(mMaxServiceIntervalMicros); dest.writeInt(mMinDataRateKbps); dest.writeInt(mDelayBoundMicros); dest.writeInt(mOptionalFieldBitmap); dest.writeInt(mMaxMsduSizeOctets); dest.writeInt(mServiceStartTimeMicros); dest.writeInt(mServiceStartTimeLinkId); dest.writeInt(mMeanDataRateKbps); dest.writeInt(mBurstSizeOctets); dest.writeInt(mMsduLifetimeMillis); dest.writeInt(mDeliveryRatio); dest.writeInt(mCountExponent); } /** @hide */ QosCharacteristics(@NonNull Parcel in) { this.mMinServiceIntervalMicros = in.readInt(); this.mMaxServiceIntervalMicros = in.readInt(); this.mMinDataRateKbps = in.readInt(); this.mDelayBoundMicros = in.readInt(); this.mOptionalFieldBitmap = in.readInt(); this.mMaxMsduSizeOctets = in.readInt(); this.mServiceStartTimeMicros = in.readInt(); this.mServiceStartTimeLinkId = in.readInt(); this.mMeanDataRateKbps = in.readInt(); this.mBurstSizeOctets = in.readInt(); this.mMsduLifetimeMillis = in.readInt(); this.mDeliveryRatio = in.readInt(); this.mCountExponent = in.readInt(); } @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public QosCharacteristics createFromParcel(Parcel in) { return new QosCharacteristics(in); } @Override public QosCharacteristics[] newArray(int size) { return new QosCharacteristics[size]; } }; /** * Builder for {@link QosCharacteristics}. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final class Builder { // Mandatory fields private final int mMinServiceIntervalMicros; private final int mMaxServiceIntervalMicros; private final int mMinDataRateKbps; private final int mDelayBoundMicros; // Optional fields private int mOptionalFieldBitmap; private int mMaxMsduSizeOctets; private int mServiceStartTimeMicros; private int mServiceStartTimeLinkId; private int mMeanDataRateKbps; private int mBurstSizeOctets; private int mMsduLifetimeMillis; private @DeliveryRatio int mDeliveryRatio; private int mCountExponent; /** * Constructor for {@link Builder}. * * @param minServiceIntervalMicros Positive integer specifying the minimum interval (in * microseconds) between the start of two consecutive service * periods (SPs) that are allocated for frame exchanges. * @param maxServiceIntervalMicros Positive integer specifying the maximum interval (in * microseconds) between the start of two consecutive SPs that * are allocated for frame exchanges. * @param minDataRateKbps Positive integer specifying the lowest data rate (in kilobits/sec) * for the transport of MSDUs or A-MSDUs belonging to the traffic * flow. * @param delayBoundMicros Positive integer specifying the maximum amount of time (in * microseconds) targeted to transport an MSDU or A-MSDU belonging to * the traffic flow. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public Builder(@IntRange(from = 1, to = Integer.MAX_VALUE) int minServiceIntervalMicros, @IntRange(from = 1, to = Integer.MAX_VALUE) int maxServiceIntervalMicros, @IntRange(from = 1, to = Integer.MAX_VALUE) int minDataRateKbps, @IntRange(from = 1, to = Integer.MAX_VALUE) int delayBoundMicros) { mMinServiceIntervalMicros = minServiceIntervalMicros; mMaxServiceIntervalMicros = maxServiceIntervalMicros; mMinDataRateKbps = minDataRateKbps; mDelayBoundMicros = delayBoundMicros; } /** * Set the maximum MSDU size in octets. * * @param maxMsduSizeOctets Positive integer specifying the maximum size (in octets) * of an MSDU belonging to the traffic flow. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public @NonNull Builder setMaxMsduSizeOctets( @IntRange(from = 1, to = Short.MAX_VALUE) int maxMsduSizeOctets) { mOptionalFieldBitmap |= MAX_MSDU_SIZE; mMaxMsduSizeOctets = maxMsduSizeOctets; return this; } /** * Set the service start time information. * * @param serviceStartTimeMicros Integer specifying the anticipated time (in microseconds) * when the traffic starts for the associated TID. * @param serviceStartTimeLinkId Bitmap in which the four LSBs indicate the link identifier * that corresponds to the link for which the TSF timer is * used to indicate the Service Start Time. No other bits * should be used. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public @NonNull Builder setServiceStartTimeInfo( @IntRange(from = 0, to = Integer.MAX_VALUE) int serviceStartTimeMicros, int serviceStartTimeLinkId) { mOptionalFieldBitmap |= SERVICE_START_TIME; mServiceStartTimeMicros = serviceStartTimeMicros; mServiceStartTimeLinkId = serviceStartTimeLinkId; return this; } /** * Set the mean data rate in kilobits per second. * * @param meanDataRateKbps Positive integer indicating the data rate specified (in * kilobits/sec) for transport of MSDUs or A-MSDUs belonging to the * traffic flow. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public @NonNull Builder setMeanDataRateKbps( @IntRange(from = 1, to = Integer.MAX_VALUE) int meanDataRateKbps) { mOptionalFieldBitmap |= MEAN_DATA_RATE; mMeanDataRateKbps = meanDataRateKbps; return this; } /** * Set the burst size in octets. * * @param burstSizeOctets Positive integer specifying the maximum burst (in octets) of * the MSDUs or A-MSDUs belonging to the traffic flow that arrive at * the MAC SAP within any time duration equal to the value specified * in the |delayBound| field. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public @NonNull Builder setBurstSizeOctets( @IntRange(from = 1, to = Integer.MAX_VALUE) int burstSizeOctets) { mOptionalFieldBitmap |= BURST_SIZE; mBurstSizeOctets = burstSizeOctets; return this; } /** * Set the MSDU lifetime in milliseconds. * * @param msduLifetimeMillis Positive integer specifying the maximum amount of time (in * milliseconds) since the arrival of the MSDU at the MAC data service * interface beyond which the MSDU is not useful even if received by * the receiver. The amount of time specified in this field is larger * than or equal to the amount of time specified in the |delayBound| * field, if present. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public @NonNull Builder setMsduLifetimeMillis( @IntRange(from = 1, to = Short.MAX_VALUE) int msduLifetimeMillis) { mOptionalFieldBitmap |= MSDU_LIFETIME; mMsduLifetimeMillis = msduLifetimeMillis; return this; } /** * Set the MSDU delivery information. * * @param deliveryRatio Enum indicating the percentage of the MSDUs that are expected to be * delivered successfully. * @param countExponent Exponent from which the number of incoming MSDUs is computed. The * number of incoming MSDUs is 10^|countExponent|, and is used to * determine the MSDU delivery ratio. Must be a number between * 0 and 15 (inclusive). */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public @NonNull Builder setMsduDeliveryInfo( @DeliveryRatio int deliveryRatio, @IntRange(from = 0, to = 15) int countExponent) { mOptionalFieldBitmap |= MSDU_DELIVERY_INFO; mDeliveryRatio = deliveryRatio; mCountExponent = countExponent; return this; } /** * Construct a QosCharacteristics object with the specified parameters. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public @NonNull QosCharacteristics build() { QosCharacteristics qosCharacteristics = new QosCharacteristics( mMinServiceIntervalMicros, mMaxServiceIntervalMicros, mMinDataRateKbps, mDelayBoundMicros, mOptionalFieldBitmap, mMaxMsduSizeOctets, mServiceStartTimeMicros, mServiceStartTimeLinkId, mMeanDataRateKbps, mBurstSizeOctets, mMsduLifetimeMillis, mDeliveryRatio, mCountExponent); if (!qosCharacteristics.validate()) { throw new IllegalArgumentException("Provided parameters are invalid"); } return qosCharacteristics; } } }