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.imsmedia;
18 
19 import android.os.Parcel;
20 import android.os.Parcelable;
21 
22 import androidx.annotation.NonNull;
23 import androidx.annotation.Nullable;
24 
25 import java.util.Arrays;
26 import java.util.Objects;
27 
28 /**
29  * Class to set the threshold for media quality status notifications
30  *
31  * @hide
32  */
33 public final class MediaQualityThreshold implements Parcelable {
34     private final int[] mRtpInactivityTimerMillis;
35     private final int mRtcpInactivityTimerMillis;
36     private final int mRtpHysteresisTimeInMillis;
37     private final int mRtpPacketLossDurationMillis;
38     private final int[] mRtpPacketLossRate;
39     private final int[] mRtpJitterMillis;
40     private final boolean mNotifyCurrentStatus;
41     private final int mVideoBitrateBps;
42 
43     /** @hide **/
MediaQualityThreshold(Parcel in)44     public MediaQualityThreshold(Parcel in) {
45         int arrayLength = in.readInt();
46         mRtpInactivityTimerMillis = new int[arrayLength];
47         for (int i = 0; i < arrayLength; i++) {
48             mRtpInactivityTimerMillis[i] = in.readInt();
49         }
50         mRtcpInactivityTimerMillis = in.readInt();
51         mRtpHysteresisTimeInMillis = in.readInt();
52         mRtpPacketLossDurationMillis = in.readInt();
53         arrayLength = in.readInt();
54         mRtpPacketLossRate = new int[arrayLength];
55         for (int i = 0; i < arrayLength; i++) {
56             mRtpPacketLossRate[i] = in.readInt();
57         }
58         arrayLength = in.readInt();
59         mRtpJitterMillis = new int[arrayLength];
60         for (int i = 0; i < arrayLength; i++) {
61             mRtpJitterMillis[i] = in.readInt();
62         }
63         mNotifyCurrentStatus = in.readBoolean();
64         mVideoBitrateBps = in.readInt();
65     }
66 
67     /** @hide **/
MediaQualityThreshold(Builder builder)68     public MediaQualityThreshold(Builder builder) {
69         mRtpInactivityTimerMillis = Arrays.copyOf(builder.mRtpInactivityTimerMillis,
70             builder.mRtpInactivityTimerMillis.length);
71         mRtcpInactivityTimerMillis = builder.mRtcpInactivityTimerMillis;
72         mRtpHysteresisTimeInMillis = builder.mRtpHysteresisTimeInMillis;
73         mRtpPacketLossDurationMillis = builder.mRtpPacketLossDurationMillis;
74         mRtpPacketLossRate = Arrays.copyOf(builder.mRtpPacketLossRate,
75             builder.mRtpPacketLossRate.length);
76         mRtpJitterMillis = Arrays.copyOf(builder.mRtpJitterMillis,
77             builder.mRtpJitterMillis.length);
78         mNotifyCurrentStatus = builder.mNotifyCurrentStatus;
79         mVideoBitrateBps = builder.mVideoBitrateBps;
80     }
81 
82     /** @hide **/
getRtpInactivityTimerMillis()83     public int[] getRtpInactivityTimerMillis() {
84         return mRtpInactivityTimerMillis;
85     }
86 
87     /** @hide **/
getRtcpInactivityTimerMillis()88     public int getRtcpInactivityTimerMillis() {
89         return mRtcpInactivityTimerMillis;
90     }
91 
92     /** @hide **/
getRtpHysteresisTimeInMillis()93     public int getRtpHysteresisTimeInMillis() {
94         return mRtpHysteresisTimeInMillis;
95     }
96 
97     /** @hide **/
getRtpPacketLossDurationMillis()98     public int getRtpPacketLossDurationMillis() {
99         return mRtpPacketLossDurationMillis;
100     }
101 
102     /** @hide **/
getRtpPacketLossRate()103     public int[] getRtpPacketLossRate() {
104         return mRtpPacketLossRate;
105     }
106 
107     /** @hide **/
getRtpJitterMillis()108     public int[] getRtpJitterMillis() {
109         return mRtpJitterMillis;
110     }
111 
112     /** @hide **/
getNotifyCurrentStatus()113     public boolean getNotifyCurrentStatus() {
114         return mNotifyCurrentStatus;
115     }
116 
getVideoBitrateBps()117     public int getVideoBitrateBps() {
118         return mVideoBitrateBps;
119     }
120 
121     @NonNull
122     @Override
toString()123     public String toString() {
124         return "MediaQualityThreshold: {mRtpInactivityTimerMillis="
125             + Arrays.toString(mRtpInactivityTimerMillis)
126             + ", mRtcpInactivityTimerMillis=" + mRtcpInactivityTimerMillis
127             + ", mRtpHysteresisTimeInMillis =" + mRtpHysteresisTimeInMillis
128             + ", mRtpPacketLossDurationMillis=" + mRtpPacketLossDurationMillis
129             + ", mRtpPacketLossRate=" + Arrays.toString(mRtpPacketLossRate)
130             + ", mRtpJitterMillis=" + Arrays.toString(mRtpJitterMillis)
131             + ", mNotifyCurrentStatus=" + mNotifyCurrentStatus
132             + ", mVideoBitrateBps=" + mVideoBitrateBps
133             + " }";
134     }
135 
136     @Override
hashCode()137     public int hashCode() {
138         return Objects.hash(Arrays.hashCode(mRtpInactivityTimerMillis), mRtcpInactivityTimerMillis,
139             mRtpHysteresisTimeInMillis, mRtpPacketLossDurationMillis,
140             Arrays.hashCode(mRtpPacketLossRate), Arrays.hashCode(mRtpJitterMillis),
141             mNotifyCurrentStatus, mVideoBitrateBps);
142     }
143 
144     @Override
equals(@ullable Object o)145     public boolean equals(@Nullable Object o) {
146         if (o == null || !(o instanceof MediaQualityThreshold)) {
147             return false;
148         }
149 
150         if (this == o) {
151             return true;
152         }
153 
154         MediaQualityThreshold s = (MediaQualityThreshold) o;
155 
156         return (Arrays.equals(mRtpInactivityTimerMillis, s.mRtpInactivityTimerMillis)
157             && mRtcpInactivityTimerMillis == s.mRtcpInactivityTimerMillis
158             && mRtpHysteresisTimeInMillis == s.mRtpHysteresisTimeInMillis
159             && mRtpPacketLossDurationMillis == s.mRtpPacketLossDurationMillis
160             && Arrays.equals(mRtpPacketLossRate, s.mRtpPacketLossRate)
161             && Arrays.equals(mRtpJitterMillis, s.mRtpJitterMillis)
162             && mNotifyCurrentStatus == s.mNotifyCurrentStatus
163             && mVideoBitrateBps == s.mVideoBitrateBps);
164     }
165 
166     /**
167      * {@link Parcelable#describeContents}
168      */
describeContents()169     public int describeContents() {
170         return 0;
171     }
172 
173     /**
174      * {@link Parcelable#writeToParcel}
175      */
writeToParcel(Parcel dest, int flags)176     public void writeToParcel(Parcel dest, int flags) {
177         dest.writeIntArray(mRtpInactivityTimerMillis);
178         dest.writeInt(mRtcpInactivityTimerMillis);
179         dest.writeInt(mRtpHysteresisTimeInMillis);
180         dest.writeInt(mRtpPacketLossDurationMillis);
181         dest.writeIntArray(mRtpPacketLossRate);
182         dest.writeIntArray(mRtpJitterMillis);
183         dest.writeBoolean(mNotifyCurrentStatus);
184         dest.writeInt(mVideoBitrateBps);
185     }
186 
187     public static final @NonNull Parcelable.Creator<MediaQualityThreshold>
188         CREATOR = new Parcelable.Creator() {
189         public MediaQualityThreshold createFromParcel(Parcel in) {
190             // TODO use builder class so it will validate
191             return new MediaQualityThreshold(in);
192         }
193 
194         public MediaQualityThreshold[] newArray(int size) {
195             return new MediaQualityThreshold[size];
196         }
197     };
198 
199     /**
200      * Provides a convenient way to set the fields of a {@link MediaQualityThreshold}
201      * when creating a new instance.
202      */
203     public static final class Builder {
204         private int[] mRtpInactivityTimerMillis;
205         private int mRtcpInactivityTimerMillis;
206         private int mRtpHysteresisTimeInMillis;
207         private int mRtpPacketLossDurationMillis;
208         private int[] mRtpPacketLossRate;
209         private int[] mRtpJitterMillis;
210         private boolean mNotifyCurrentStatus;
211         private int mVideoBitrateBps;
212 
213         /**
214          * Default constructor for Builder.
215          */
Builder()216         public Builder() {
217             mRtpInactivityTimerMillis = new int[0];
218             mRtcpInactivityTimerMillis = 0;
219             mRtpHysteresisTimeInMillis = 0;
220             mRtpPacketLossDurationMillis = 0;
221             mRtpPacketLossRate = new int[0];
222             mRtpJitterMillis = new int[0];
223             mVideoBitrateBps = 0;
224         }
225 
226         /**
227          * Set the timer in milliseconds for monitoring RTP inactivity
228          *
229          * @param timer The array of inacitivity timer values in milliseconds
230          *
231          * @return The same instance of the builder
232          */
setRtpInactivityTimerMillis(@onNull final int[] timer)233         public @NonNull Builder setRtpInactivityTimerMillis(@NonNull final int[] timer) {
234             this.mRtpInactivityTimerMillis = Arrays.copyOf(timer, timer.length);
235             return this;
236         }
237 
238         /**
239          * Set the timer in milliseconds for monitoring RTCP inactivity
240          *
241          * @param timer The timer value in milliseconds
242          *
243          * @return The same instance of the builder
244          */
setRtcpInactivityTimerMillis(final int timer)245         public @NonNull Builder setRtcpInactivityTimerMillis(final int timer) {
246             this.mRtcpInactivityTimerMillis = timer;
247             return this;
248         }
249 
250         /**
251          * Set the threshold hysteresis time for packet loss and jitter. This has a goal to prevent
252          * frequent ping-pong notification. So whenever a notifier needs to report the cross of
253          * threshold in opposite direction, this hysteresis timer should be respected.
254          *
255          * @param time The hysteresis time in milliseconds
256          *
257          * @return The same instance of the builder
258          */
setRtpHysteresisTimeInMillis(final int time)259         public @NonNull Builder setRtpHysteresisTimeInMillis(final int time) {
260             this.mRtpHysteresisTimeInMillis = time;
261             return this;
262         }
263 
264         /**
265          * Set the duration in milliseconds for monitoring the RTP packet loss rate
266          *
267          * @param duration The duration in milliseconds
268          *
269          * @return The same instance of the builder
270          */
setRtpPacketLossDurationMillis(final int duration)271         public @NonNull Builder setRtpPacketLossDurationMillis(final int duration) {
272             this.mRtpPacketLossDurationMillis = duration;
273             return this;
274         }
275 
276         /**
277          * Set the RTP packet loss rate threshold in percentage
278          *
279          * Packet loss rate = (Number of packets lost / number of packets expected) * 100
280          *
281          * @param packetLossRate The array of packet loss rates
282          *
283          * @return The same instance of the builder
284          */
setRtpPacketLossRate(@onNull final int[] packetLossRate)285         public @NonNull Builder setRtpPacketLossRate(@NonNull final int[] packetLossRate) {
286             this.mRtpPacketLossRate = Arrays.copyOf(packetLossRate, packetLossRate.length);
287             return this;
288         }
289 
290         /**
291          * Set the RTP jitter threshold in milliseconds
292          *
293          * @param jitter The array of jitter thresholds
294          *
295          * @return The same instance of the builder
296          */
setRtpJitterMillis(@onNull final int[] jitter)297         public @NonNull Builder setRtpJitterMillis(@NonNull final int[] jitter) {
298             this.mRtpJitterMillis = Arrays.copyOf(jitter, jitter.length);
299             return this;
300         }
301 
302         /**
303          * A flag indicating whether the client needs to be notify the current media quality status
304          * right after threshold is being set. True means the media stack should notify the client
305          * of the current status.
306          *
307          * @param notify The boolean state if it requires the prompt notification of
308          * MediaQualityStatus
309          *
310          * @return The same instance of the builder
311          */
setNotifyCurrentStatus(final boolean notify)312         public @NonNull Builder setNotifyCurrentStatus(final boolean notify) {
313             this.mNotifyCurrentStatus = notify;
314             return this;
315         }
316 
317         /**
318          * The receiving bitrate threshold in bps for video call. If it is not zero, bitrate
319          * notification event is triggered when the receiving frame bitrate is less than the
320          * threshold.
321          */
setVideoBitrateBps(final int bitrate)322         public @NonNull Builder setVideoBitrateBps(final int bitrate) {
323             this.mVideoBitrateBps = bitrate;
324             return this;
325         }
326 
327         /**
328          * Build the MediaQualityThreshold.
329          *
330          * @return the MediaQualityThreshold object.
331          */
build()332         public @NonNull MediaQualityThreshold build() {
333             // TODO validation
334             return new MediaQualityThreshold(this);
335         }
336     }
337 }
338 
339