| /* |
| * 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)} |
| * |
| * <p/> |
| * 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<MediaThreshold> CREATOR = |
| new Creator<MediaThreshold>() { |
| @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. |
| * |
| * <p>The example below shows how you might create a new {@code RtpThreshold}: |
| * |
| * <pre><code> |
| * |
| * 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(); |
| * </code></pre> |
| * |
| * @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. |
| * <p/> |
| * 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<Integer> 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<Integer> 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<Long> 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); |
| } |
| } |
| } |