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