/* * 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 android.telephony.ims; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.ims.feature.MmTelFeature; import java.util.Arrays; import java.util.Objects; import java.util.TreeSet; /** * A MediaThreshold represents a series of packet loss rate, jitter and rtp inactivity time * thresholds which when crossed should result in a {@link MediaQualityStatus} report being * generated by the {@link ImsService} via {@link MmTelFeature#notifyMediaQualityStatusChanged( * MediaQualityStatus)} * *

* A {@link MediaQualityStatus} should be triggered when any of various * attributes pass one of the thresholds defined here. * * @hide */ @SystemApi public final class MediaThreshold implements Parcelable { private final int[] mRtpPacketLossRate; private final int[] mRtpJitter; private final long[] mRtpInactivityTimeMillis; /** * Retrieves threshold values for RTP packet loss rate in percentage. * * @return int array including threshold values for packet loss rate * * @hide */ @NonNull @SystemApi public int[] getThresholdsRtpPacketLossRate() { return mRtpPacketLossRate; } /** * Retrieves threshold values for jitter(RFC3550) in milliseconds. * * @return int array including threshold values for RTP jitter. */ @NonNull public int[] getThresholdsRtpJitterMillis() { return mRtpJitter; } /** * Retrieves threshold values for RTP inactivity time in milliseconds. * * @return int array including threshold values for RTP inactivity time. */ @NonNull public long[] getThresholdsRtpInactivityTimeMillis() { return mRtpInactivityTimeMillis; } private MediaThreshold( int[] packetLossRateThresholds, int[] jitterThresholds, long[] inactivityTimeThresholds) { mRtpPacketLossRate = packetLossRateThresholds; mRtpJitter = jitterThresholds; mRtpInactivityTimeMillis = inactivityTimeThresholds; } /** * Creates a new instance of {@link MediaThreshold} from a parcel. * @param in The parceled data to read. */ private MediaThreshold(@NonNull Parcel in) { mRtpPacketLossRate = in.createIntArray(); mRtpJitter = in.createIntArray(); mRtpInactivityTimeMillis = in.createLongArray(); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeIntArray(mRtpPacketLossRate); dest.writeIntArray(mRtpJitter); dest.writeLongArray(mRtpInactivityTimeMillis); } public static final @NonNull Creator CREATOR = new Creator() { @Override public MediaThreshold createFromParcel(@NonNull Parcel in) { return new MediaThreshold(in); } @Override public MediaThreshold[] newArray(int size) { return new MediaThreshold[size]; } }; /** * Returns whether the RTP packet loss rate threshold is valid or not. * * @param packetLossRate packet loss rate * @return the threshold is valid or not. * @hide */ public static boolean isValidRtpPacketLossRate(int packetLossRate) { return (packetLossRate >= 0 && packetLossRate <= 100); } /** * Returns whether the RTP jitter threshold is valid or not. * * @param jitter jitter value in milliseconds * @return the threshold is valid or not. * @hide */ public static boolean isValidJitterMillis(int jitter) { return (jitter >= 0 && jitter <= 10000); } /** * Returns whether the RTP packet loss rate threshold is valid or not. * * @param inactivityTime packet loss rate * @return the threshold is valid or not. * @hide */ public static boolean isValidRtpInactivityTimeMillis(long inactivityTime) { return (inactivityTime >= 0 && inactivityTime <= 60000); } @Override public int describeContents() { return 0; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MediaThreshold that = (MediaThreshold) o; return Arrays.equals(mRtpPacketLossRate, that.mRtpPacketLossRate) && Arrays.equals(mRtpJitter, that.mRtpJitter) && Arrays.equals(mRtpInactivityTimeMillis, that.mRtpInactivityTimeMillis); } @Override public int hashCode() { return Objects.hash(Arrays.hashCode(mRtpPacketLossRate), Arrays.hashCode(mRtpJitter), Arrays.hashCode(mRtpInactivityTimeMillis)); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("MediaThreshold{mRtpPacketLossRate="); for (int i : mRtpPacketLossRate) { sb.append(" ").append(i); } sb.append(", mRtpJitter="); for (int b : mRtpJitter) { sb.append(" ").append(b); } sb.append(", mRtpInactivityTimeMillis="); for (long i : mRtpInactivityTimeMillis) { sb.append(" ").append(i); } sb.append("}"); return sb.toString(); } /** * Provides a convenient way to set the fields of an {@link MediaThreshold} when creating a * new instance. * *

The example below shows how you might create a new {@code RtpThreshold}: * *


     *
     * RtpThreshold = new RtpThreshold.Builder()
     *     .setRtpSessionType({@link MediaQualityStatus#MEDIA_SESSION_TYPE_AUDIO} or
     *                          {@link MediaQualityStatus#MEDIA_SESSION_TYPE_VIDEO})
     *     .setThresholdsRtpPacketLossRate(int[] packetLossRateThresholds)
     *     .setThresholdsRtpJitterMillis(int[] jitterThresholds)
     *     .setThresholdsRtpInactivityTimeMillis(int[] inactivityTimeThresholds)
     *     .build();
     * 
* * @hide */ public static final class Builder { private int[] mRtpPacketLossRate = null; private int[] mRtpJitter = null; private long[] mRtpInactivityTimeMillis = null; /** * Default constructor for the Builder. * * @hide */ public Builder() { } /** * Set threshold values for RTP packet loss rate in percentage. *

* The packet loss calculation should be done at least once per * second. It should be calculated with at least the last 3 seconds * of data. * * @param packetLossRateThresholds int array for threshold values. * @return The same instance of the builder. * * @hide */ @NonNull public Builder setThresholdsRtpPacketLossRate(int[] packetLossRateThresholds) { if (packetLossRateThresholds.length > 0) { TreeSet thresholds = new TreeSet<>(); for (Integer value : packetLossRateThresholds) { if (isValidRtpPacketLossRate(value)) { thresholds.add(value); } } int[] targetArray = new int[thresholds.size()]; int i = 0; for (int element : thresholds) { targetArray[i++] = element; } this.mRtpPacketLossRate = targetArray; } else { this.mRtpPacketLossRate = packetLossRateThresholds; } return this; } /** * Set threshold values for RTP jitter in Milliseconds. * * @param jitterThresholds int array including threshold values for Jitter. * @return The same instance of the builder. * * @hide */ @NonNull public Builder setThresholdsRtpJitterMillis(int[] jitterThresholds) { if (jitterThresholds.length > 0) { TreeSet thresholds = new TreeSet<>(); for (Integer value : jitterThresholds) { if (isValidJitterMillis(value)) { thresholds.add(value); } } int[] targetArray = new int[thresholds.size()]; int i = 0; for (int element : thresholds) { targetArray[i++] = element; } this.mRtpJitter = targetArray; } else { this.mRtpJitter = jitterThresholds; } return this; } /** * Set threshold values for RTP inactivity time. * * @param inactivityTimeThresholds int array including threshold * values for RTP inactivity time. * @return The same instance of the builder. * * @hide */ @NonNull public Builder setThresholdsRtpInactivityTimeMillis(long[] inactivityTimeThresholds) { if (inactivityTimeThresholds.length > 0) { TreeSet thresholds = new TreeSet<>(); for (Long value : inactivityTimeThresholds) { if (isValidRtpInactivityTimeMillis(value)) { thresholds.add(value); } } long[] targetArray = new long[thresholds.size()]; int i = 0; for (long element : thresholds) { targetArray[i++] = element; } this.mRtpInactivityTimeMillis = targetArray; } else { this.mRtpInactivityTimeMillis = inactivityTimeThresholds; } return this; } /** * Build the {@link MediaThreshold} * * @return the {@link MediaThreshold} object * * @hide */ @NonNull public MediaThreshold build() { mRtpPacketLossRate = mRtpPacketLossRate != null ? mRtpPacketLossRate : new int[0]; mRtpJitter = mRtpJitter != null ? mRtpJitter : new int[0]; mRtpInactivityTimeMillis = mRtpInactivityTimeMillis != null ? mRtpInactivityTimeMillis : new long[0]; return new MediaThreshold(mRtpPacketLossRate, mRtpJitter, mRtpInactivityTimeMillis); } } }