1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.telephony.ims;
18 
19 import android.annotation.NonNull;
20 import android.annotation.SystemApi;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.telephony.ims.feature.MmTelFeature;
24 
25 import java.util.Arrays;
26 import java.util.Objects;
27 import java.util.TreeSet;
28 
29 /**
30  * A MediaThreshold represents a series of packet loss rate, jitter and rtp inactivity time
31  * thresholds which when crossed should result in a {@link MediaQualityStatus} report being
32  * generated by the {@link ImsService} via {@link MmTelFeature#notifyMediaQualityStatusChanged(
33  * MediaQualityStatus)}
34  *
35  * <p/>
36  * A {@link MediaQualityStatus} should be triggered when any of various
37  * attributes pass one of the thresholds defined here.
38  *
39  *  @hide
40  */
41 @SystemApi
42 public final class MediaThreshold implements Parcelable {
43     private final int[] mRtpPacketLossRate;
44     private final int[] mRtpJitter;
45     private final long[] mRtpInactivityTimeMillis;
46 
47     /**
48      * Retrieves threshold values for RTP packet loss rate in percentage.
49      *
50      * @return int array including threshold values for packet loss rate
51      *
52      * @hide
53      */
54     @NonNull
55     @SystemApi
getThresholdsRtpPacketLossRate()56     public int[] getThresholdsRtpPacketLossRate() {
57         return mRtpPacketLossRate;
58     }
59 
60     /**
61      * Retrieves threshold values for jitter(RFC3550) in milliseconds.
62      *
63      * @return int array including threshold values for RTP jitter.
64      */
65     @NonNull
getThresholdsRtpJitterMillis()66     public int[] getThresholdsRtpJitterMillis() {
67         return mRtpJitter;
68     }
69 
70     /**
71      * Retrieves threshold values for RTP inactivity time in milliseconds.
72      *
73      * @return int array including threshold values for RTP inactivity time.
74      */
75     @NonNull
getThresholdsRtpInactivityTimeMillis()76     public long[] getThresholdsRtpInactivityTimeMillis() {
77         return mRtpInactivityTimeMillis;
78     }
79 
MediaThreshold( int[] packetLossRateThresholds, int[] jitterThresholds, long[] inactivityTimeThresholds)80     private MediaThreshold(
81             int[] packetLossRateThresholds,
82             int[] jitterThresholds,
83             long[] inactivityTimeThresholds) {
84         mRtpPacketLossRate = packetLossRateThresholds;
85         mRtpJitter = jitterThresholds;
86         mRtpInactivityTimeMillis = inactivityTimeThresholds;
87     }
88 
89     /**
90      * Creates a new instance of {@link MediaThreshold} from a parcel.
91      * @param in The parceled data to read.
92      */
MediaThreshold(@onNull Parcel in)93     private MediaThreshold(@NonNull Parcel in) {
94         mRtpPacketLossRate = in.createIntArray();
95         mRtpJitter = in.createIntArray();
96         mRtpInactivityTimeMillis = in.createLongArray();
97     }
98 
99     @Override
writeToParcel(@onNull Parcel dest, int flags)100     public void writeToParcel(@NonNull Parcel dest, int flags) {
101         dest.writeIntArray(mRtpPacketLossRate);
102         dest.writeIntArray(mRtpJitter);
103         dest.writeLongArray(mRtpInactivityTimeMillis);
104     }
105 
106     public static final @NonNull Creator<MediaThreshold> CREATOR =
107             new Creator<MediaThreshold>() {
108                 @Override
109                 public MediaThreshold createFromParcel(@NonNull Parcel in) {
110                     return new MediaThreshold(in);
111                 }
112 
113                 @Override
114                 public MediaThreshold[] newArray(int size) {
115                     return new MediaThreshold[size];
116                 }
117             };
118 
119     /**
120      * Returns whether the RTP packet loss rate threshold is valid or not.
121      *
122      * @param packetLossRate packet loss rate
123      * @return the threshold is valid or not.
124      * @hide
125      */
isValidRtpPacketLossRate(int packetLossRate)126     public static boolean isValidRtpPacketLossRate(int packetLossRate) {
127         return (packetLossRate >= 0 && packetLossRate <= 100);
128     }
129 
130     /**
131      * Returns whether the RTP jitter threshold is valid or not.
132      *
133      * @param jitter jitter value in milliseconds
134      * @return the threshold is valid or not.
135      * @hide
136      */
isValidJitterMillis(int jitter)137     public static boolean isValidJitterMillis(int jitter) {
138         return (jitter >= 0 && jitter <= 10000);
139     }
140 
141     /**
142      * Returns whether the RTP packet loss rate threshold is valid or not.
143      *
144      * @param inactivityTime packet loss rate
145      * @return the threshold is valid or not.
146      * @hide
147      */
isValidRtpInactivityTimeMillis(long inactivityTime)148     public static boolean isValidRtpInactivityTimeMillis(long inactivityTime) {
149         return (inactivityTime >= 0 && inactivityTime <= 60000);
150     }
151 
152     @Override
describeContents()153     public int describeContents() {
154         return 0;
155     }
156 
157     @Override
equals(Object o)158     public boolean equals(Object o) {
159         if (this == o) return true;
160         if (o == null || getClass() != o.getClass()) return false;
161         MediaThreshold that = (MediaThreshold) o;
162         return Arrays.equals(mRtpPacketLossRate, that.mRtpPacketLossRate)
163                 && Arrays.equals(mRtpJitter, that.mRtpJitter)
164                 && Arrays.equals(mRtpInactivityTimeMillis, that.mRtpInactivityTimeMillis);
165     }
166 
167     @Override
hashCode()168     public int hashCode() {
169         return Objects.hash(Arrays.hashCode(mRtpPacketLossRate), Arrays.hashCode(mRtpJitter),
170                 Arrays.hashCode(mRtpInactivityTimeMillis));
171     }
172 
173     @Override
toString()174     public String toString() {
175         StringBuilder sb = new StringBuilder();
176         sb.append("MediaThreshold{mRtpPacketLossRate=");
177         for (int i : mRtpPacketLossRate) {
178             sb.append(" ").append(i);
179         }
180         sb.append(", mRtpJitter=");
181         for (int b : mRtpJitter) {
182             sb.append(" ").append(b);
183         }
184         sb.append(", mRtpInactivityTimeMillis=");
185         for (long i : mRtpInactivityTimeMillis) {
186             sb.append(" ").append(i);
187         }
188         sb.append("}");
189         return sb.toString();
190     }
191 
192     /**
193      * Provides a convenient way to set the fields of an {@link MediaThreshold} when creating a
194      * new instance.
195      *
196      * <p>The example below shows how you might create a new {@code RtpThreshold}:
197      *
198      * <pre><code>
199      *
200      * RtpThreshold = new RtpThreshold.Builder()
201      *     .setRtpSessionType({@link MediaQualityStatus#MEDIA_SESSION_TYPE_AUDIO} or
202      *                          {@link MediaQualityStatus#MEDIA_SESSION_TYPE_VIDEO})
203      *     .setThresholdsRtpPacketLossRate(int[] packetLossRateThresholds)
204      *     .setThresholdsRtpJitterMillis(int[] jitterThresholds)
205      *     .setThresholdsRtpInactivityTimeMillis(int[] inactivityTimeThresholds)
206      *     .build();
207      * </code></pre>
208      *
209      * @hide
210      */
211     public static final class Builder {
212         private int[] mRtpPacketLossRate = null;
213         private int[] mRtpJitter = null;
214         private long[] mRtpInactivityTimeMillis = null;
215 
216         /**
217          * Default constructor for the Builder.
218          *
219          * @hide
220          */
Builder()221         public Builder() {
222         }
223 
224         /**
225          * Set threshold values for RTP packet loss rate in percentage.
226          * <p/>
227          * The packet loss calculation should be done at least once per
228          * second. It should be calculated with at least the last 3 seconds
229          * of data.
230          *
231          * @param packetLossRateThresholds int array for threshold values.
232          * @return The same instance of the builder.
233          *
234          * @hide
235          */
236         @NonNull
setThresholdsRtpPacketLossRate(int[] packetLossRateThresholds)237         public Builder setThresholdsRtpPacketLossRate(int[] packetLossRateThresholds) {
238             if (packetLossRateThresholds.length > 0) {
239                 TreeSet<Integer> thresholds = new TreeSet<>();
240                 for (Integer value : packetLossRateThresholds) {
241                     if (isValidRtpPacketLossRate(value)) {
242                         thresholds.add(value);
243                     }
244                 }
245                 int[] targetArray = new int[thresholds.size()];
246                 int i = 0;
247                 for (int element : thresholds) {
248                     targetArray[i++] = element;
249                 }
250                 this.mRtpPacketLossRate = targetArray;
251             } else {
252                 this.mRtpPacketLossRate = packetLossRateThresholds;
253             }
254             return this;
255         }
256 
257 
258         /**
259          * Set threshold values for RTP jitter in Milliseconds.
260          *
261          * @param jitterThresholds int array including threshold values for Jitter.
262          * @return The same instance of the builder.
263          *
264          * @hide
265          */
266         @NonNull
setThresholdsRtpJitterMillis(int[] jitterThresholds)267         public Builder setThresholdsRtpJitterMillis(int[] jitterThresholds) {
268             if (jitterThresholds.length > 0) {
269                 TreeSet<Integer> thresholds = new TreeSet<>();
270                 for (Integer value : jitterThresholds) {
271                     if (isValidJitterMillis(value)) {
272                         thresholds.add(value);
273                     }
274                 }
275                 int[] targetArray = new int[thresholds.size()];
276                 int i = 0;
277                 for (int element : thresholds) {
278                     targetArray[i++] = element;
279                 }
280                 this.mRtpJitter = targetArray;
281             } else {
282                 this.mRtpJitter = jitterThresholds;
283             }
284             return this;
285         }
286 
287         /**
288          * Set threshold values for RTP inactivity time.
289          *
290          * @param inactivityTimeThresholds int array including threshold
291          *                              values for RTP inactivity time.
292          * @return The same instance of the builder.
293          *
294          * @hide
295          */
296         @NonNull
setThresholdsRtpInactivityTimeMillis(long[] inactivityTimeThresholds)297         public Builder setThresholdsRtpInactivityTimeMillis(long[] inactivityTimeThresholds) {
298             if (inactivityTimeThresholds.length > 0) {
299                 TreeSet<Long> thresholds = new TreeSet<>();
300                 for (Long value : inactivityTimeThresholds) {
301                     if (isValidRtpInactivityTimeMillis(value)) {
302                         thresholds.add(value);
303                     }
304                 }
305                 long[] targetArray = new long[thresholds.size()];
306                 int i = 0;
307                 for (long element : thresholds) {
308                     targetArray[i++] = element;
309                 }
310                 this.mRtpInactivityTimeMillis = targetArray;
311             } else {
312                 this.mRtpInactivityTimeMillis = inactivityTimeThresholds;
313             }
314             return this;
315         }
316 
317         /**
318          * Build the {@link MediaThreshold}
319          *
320          * @return the {@link MediaThreshold} object
321          *
322          * @hide
323          */
324         @NonNull
build()325         public MediaThreshold build() {
326             mRtpPacketLossRate = mRtpPacketLossRate != null ? mRtpPacketLossRate : new int[0];
327             mRtpJitter = mRtpJitter != null ? mRtpJitter : new int[0];
328             mRtpInactivityTimeMillis =
329                     mRtpInactivityTimeMillis != null ? mRtpInactivityTimeMillis : new long[0];
330             return new MediaThreshold(mRtpPacketLossRate, mRtpJitter, mRtpInactivityTimeMillis);
331         }
332     }
333 }
334