1 /* 2 * Copyright (C) 2020 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 com.android.internal.telephony.metrics; 18 19 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS; 20 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS; 21 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN; 22 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_FIVE_MINUTES; 23 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_ONE_HOUR; 24 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_ONE_MINUTE; 25 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_TEN_MINUTES; 26 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_THIRTY_MINUTES; 27 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_MORE_THAN_ONE_HOUR; 28 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN; 29 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO; 30 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT; 31 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_FULLBAND; 32 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND; 33 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND; 34 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN; 35 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_WIDEBAND; 36 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST; 37 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_SLOW; 38 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_FAST; 39 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_NORMAL; 40 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_SLOW; 41 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST; 42 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_SLOW; 43 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN; 44 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST; 45 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW; 46 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_GREAT; 47 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_NONE_OR_UNKNOWN; 48 49 import android.annotation.Nullable; 50 import android.content.Context; 51 import android.net.wifi.WifiInfo; 52 import android.net.wifi.WifiManager; 53 import android.os.SystemClock; 54 import android.telecom.VideoProfile; 55 import android.telecom.VideoProfile.VideoState; 56 import android.telephony.Annotation.NetworkType; 57 import android.telephony.AnomalyReporter; 58 import android.telephony.DisconnectCause; 59 import android.telephony.NetworkRegistrationInfo; 60 import android.telephony.ServiceState; 61 import android.telephony.TelephonyManager; 62 import android.telephony.ims.ImsReasonInfo; 63 import android.telephony.ims.ImsStreamMediaProfile; 64 import android.util.LongSparseArray; 65 import android.util.SparseArray; 66 import android.util.SparseIntArray; 67 68 import com.android.internal.annotations.VisibleForTesting; 69 import com.android.internal.telephony.Call; 70 import com.android.internal.telephony.Connection; 71 import com.android.internal.telephony.DriverCall; 72 import com.android.internal.telephony.GsmCdmaConnection; 73 import com.android.internal.telephony.Phone; 74 import com.android.internal.telephony.PhoneConstants; 75 import com.android.internal.telephony.PhoneFactory; 76 import com.android.internal.telephony.ServiceStateTracker; 77 import com.android.internal.telephony.imsphone.ImsPhone; 78 import com.android.internal.telephony.imsphone.ImsPhoneConnection; 79 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession; 80 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.AudioCodec; 81 import com.android.internal.telephony.uicc.UiccController; 82 import com.android.telephony.Rlog; 83 84 import java.util.ArrayList; 85 import java.util.Arrays; 86 import java.util.HashSet; 87 import java.util.List; 88 import java.util.Set; 89 import java.util.UUID; 90 import java.util.stream.Collectors; 91 92 /** Collects voice call events per phone ID for the pulled atom. */ 93 public class VoiceCallSessionStats { 94 private static final String TAG = VoiceCallSessionStats.class.getSimpleName(); 95 96 // Upper bounds of each call setup duration category in milliseconds. 97 private static final int CALL_SETUP_DURATION_UNKNOWN = 0; 98 private static final int CALL_SETUP_DURATION_EXTREMELY_FAST = 400; 99 private static final int CALL_SETUP_DURATION_ULTRA_FAST = 700; 100 private static final int CALL_SETUP_DURATION_VERY_FAST = 1000; 101 private static final int CALL_SETUP_DURATION_FAST = 1500; 102 private static final int CALL_SETUP_DURATION_NORMAL = 2500; 103 private static final int CALL_SETUP_DURATION_SLOW = 4000; 104 private static final int CALL_SETUP_DURATION_VERY_SLOW = 6000; 105 private static final int CALL_SETUP_DURATION_ULTRA_SLOW = 10000; 106 // CALL_SETUP_DURATION_EXTREMELY_SLOW has no upper bound (it includes everything above 10000) 107 108 // Upper bounds of each call duration category in milliseconds. 109 private static final int CALL_DURATION_ONE_MINUTE = 60000; 110 private static final int CALL_DURATION_FIVE_MINUTES = 300000; 111 private static final int CALL_DURATION_TEN_MINUTES = 600000; 112 private static final int CALL_DURATION_THIRTY_MINUTES = 1800000; 113 private static final int CALL_DURATION_ONE_HOUR = 3600000; 114 115 /** Number of buckets for codec quality, from UNKNOWN to FULLBAND. */ 116 private static final int CODEC_QUALITY_COUNT = 5; 117 118 /** 119 * Threshold to calculate the main audio codec quality of the call. 120 * 121 * <p>The audio codec quality was equal to or greater than the main audio codec quality for at 122 * least 70% of the call. 123 */ 124 private static final int MAIN_CODEC_QUALITY_THRESHOLD = 70; 125 126 /** Holds the audio codec value for CS calls. */ 127 private static final SparseIntArray CS_CODEC_MAP = buildGsmCdmaCodecMap(); 128 129 /** Holds the audio codec value for IMS calls. */ 130 private static final SparseIntArray IMS_CODEC_MAP = buildImsCodecMap(); 131 132 /** Holds setup duration buckets with values as their upper bounds in milliseconds. */ 133 private static final SparseIntArray CALL_SETUP_DURATION_MAP = buildCallSetupDurationMap(); 134 135 /** Holds call duration buckets with values as their upper bounds in milliseconds. */ 136 private static final SparseIntArray CALL_DURATION_MAP = buildCallDurationMap(); 137 138 /** UUID for reporting concurrent call anomaly */ 139 private static final UUID CONCURRENT_CALL_ANOMALY_UUID = 140 UUID.fromString("76780b5a-623e-48a4-be3f-925e05177c9c"); 141 142 /** If the number of concurrent calls exceeds this number, report anomaly*/ 143 private static final int MAX_NORMAL_CONCURRENT_CALLS = 3; 144 145 /** 146 * Tracks statistics for each call connection, indexed with ID returned by {@link 147 * #getConnectionId}. 148 */ 149 private final SparseArray<VoiceCallSession> mCallProtos = new SparseArray<>(); 150 151 /** 152 * Tracks usage of codecs for each call. 153 * 154 * <p>The outer array is used to map each connection id to the corresponding codec usage. The 155 * inner array is used to map timestamp (key) with the codec in use (value). 156 */ 157 private final SparseArray<LongSparseArray<Integer>> mCodecUsage = new SparseArray<>(); 158 159 /** 160 * Tracks call RAT usage. 161 * 162 * <p>RAT usage is mainly tied to phones rather than calls, since each phone can have multiple 163 * concurrent calls, and we do not want to count the RAT duration multiple times. 164 */ 165 private final VoiceCallRatTracker mRatUsage = new VoiceCallRatTracker(); 166 167 private final int mPhoneId; 168 private final Phone mPhone; 169 170 private final PersistAtomsStorage mAtomsStorage = 171 PhoneFactory.getMetricsCollector().getAtomsStorage(); 172 private final UiccController mUiccController = UiccController.getInstance(); 173 VoiceCallSessionStats(int phoneId, Phone phone)174 public VoiceCallSessionStats(int phoneId, Phone phone) { 175 mPhoneId = phoneId; 176 mPhone = phone; 177 } 178 179 /* CS calls */ 180 181 /** Updates internal states when previous CS calls are accepted to track MT call setup time. */ onRilAcceptCall(List<Connection> connections)182 public synchronized void onRilAcceptCall(List<Connection> connections) { 183 for (Connection conn : connections) { 184 acceptCall(conn); 185 } 186 } 187 188 /** Updates internal states when a CS MO call is created. */ onRilDial(Connection conn)189 public synchronized void onRilDial(Connection conn) { 190 addCall(conn); 191 } 192 193 /** 194 * Updates internal states when CS calls are created or terminated, or CS call state is changed. 195 */ onRilCallListChanged(List<GsmCdmaConnection> connections)196 public synchronized void onRilCallListChanged(List<GsmCdmaConnection> connections) { 197 for (Connection conn : connections) { 198 int id = getConnectionId(conn); 199 if (!mCallProtos.contains(id)) { 200 // handle new connections 201 if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { 202 addCall(conn); 203 checkCallSetup(conn, mCallProtos.get(id)); 204 } else { 205 logd( 206 "onRilCallListChanged: skip adding disconnected connection," 207 + " connectionId=%d", 208 id); 209 } 210 } else { 211 VoiceCallSession proto = mCallProtos.get(id); 212 // handle call state change 213 checkCallSetup(conn, proto); 214 // handle terminated connections 215 if (conn.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED) { 216 proto.bearerAtEnd = getBearer(conn); // should be CS 217 proto.disconnectReasonCode = conn.getDisconnectCause(); 218 proto.disconnectExtraCode = conn.getPreciseDisconnectCause(); 219 proto.disconnectExtraMessage = conn.getVendorDisconnectCause(); 220 proto.callDuration = classifyCallDuration(conn.getDurationMillis()); 221 finishCall(id); 222 } 223 } 224 } 225 // NOTE: we cannot check stray connections (CS call in our list but not in RIL), as 226 // GsmCdmaCallTracker can call this with a partial list 227 } 228 229 /* IMS calls */ 230 231 /** Updates internal states when an IMS MO call is created. */ onImsDial(ImsPhoneConnection conn)232 public synchronized void onImsDial(ImsPhoneConnection conn) { 233 addCall(conn); 234 if (conn.hasRttTextStream()) { 235 setRttStarted(conn); 236 } 237 } 238 239 /** Updates internal states when an IMS MT call is created. */ onImsCallReceived(ImsPhoneConnection conn)240 public synchronized void onImsCallReceived(ImsPhoneConnection conn) { 241 addCall(conn); 242 if (conn.hasRttTextStream()) { 243 setRttStarted(conn); 244 } 245 } 246 247 /** Updates internal states when previous IMS calls are accepted to track MT call setup time. */ onImsAcceptCall(List<Connection> connections)248 public synchronized void onImsAcceptCall(List<Connection> connections) { 249 for (Connection conn : connections) { 250 acceptCall(conn); 251 } 252 } 253 254 /** Updates internal states when an IMS fails to start. */ onImsCallStartFailed( @ullable ImsPhoneConnection conn, ImsReasonInfo reasonInfo)255 public synchronized void onImsCallStartFailed( 256 @Nullable ImsPhoneConnection conn, ImsReasonInfo reasonInfo) { 257 onImsCallTerminated(conn, reasonInfo); 258 } 259 260 /** Updates internal states when an IMS call is terminated. */ onImsCallTerminated( @ullable ImsPhoneConnection conn, ImsReasonInfo reasonInfo)261 public synchronized void onImsCallTerminated( 262 @Nullable ImsPhoneConnection conn, ImsReasonInfo reasonInfo) { 263 if (conn == null) { 264 List<Integer> imsConnIds = getImsConnectionIds(); 265 if (imsConnIds.size() == 1) { 266 loge("onImsCallTerminated: ending IMS call w/ conn=null"); 267 finishImsCall(imsConnIds.get(0), reasonInfo, 0); 268 } else { 269 loge("onImsCallTerminated: %d IMS calls w/ conn=null", imsConnIds.size()); 270 } 271 } else { 272 int id = getConnectionId(conn); 273 if (mCallProtos.contains(id)) { 274 finishImsCall(id, reasonInfo, conn.getDurationMillis()); 275 } else { 276 loge("onImsCallTerminated: untracked connection, connectionId=%d", id); 277 // fake a call so at least some info can be tracked 278 addCall(conn); 279 finishImsCall(id, reasonInfo, conn.getDurationMillis()); 280 } 281 } 282 } 283 284 /** Updates internal states when RTT is started on an IMS call. */ onRttStarted(ImsPhoneConnection conn)285 public synchronized void onRttStarted(ImsPhoneConnection conn) { 286 setRttStarted(conn); 287 } 288 289 /* general & misc. */ 290 291 /** Updates internal states when audio codec for a call is changed. */ onAudioCodecChanged(Connection conn, int audioQuality)292 public synchronized void onAudioCodecChanged(Connection conn, int audioQuality) { 293 int id = getConnectionId(conn); 294 VoiceCallSession proto = mCallProtos.get(id); 295 if (proto == null) { 296 loge("onAudioCodecChanged: untracked connection, connectionId=%d", id); 297 return; 298 } 299 int codec = audioQualityToCodec(proto.bearerAtEnd, audioQuality); 300 proto.codecBitmask |= (1L << codec); 301 302 if (mCodecUsage.contains(id)) { 303 mCodecUsage.get(id).append(getTimeMillis(), codec); 304 } else { 305 LongSparseArray<Integer> arr = new LongSparseArray<>(); 306 arr.append(getTimeMillis(), codec); 307 mCodecUsage.put(id, arr); 308 } 309 } 310 311 /** Updates internal states when video state changes. */ onVideoStateChange( ImsPhoneConnection conn, @VideoState int videoState)312 public synchronized void onVideoStateChange( 313 ImsPhoneConnection conn, @VideoState int videoState) { 314 int id = getConnectionId(conn); 315 VoiceCallSession proto = mCallProtos.get(id); 316 if (proto == null) { 317 loge("onVideoStateChange: untracked connection, connectionId=%d", id); 318 return; 319 } 320 logd("onVideoStateChange: video state=%d, connectionId=%d", videoState, id); 321 if (videoState != VideoProfile.STATE_AUDIO_ONLY) { 322 proto.videoEnabled = true; 323 } 324 } 325 326 /** Updates internal states when multiparty state changes. */ onMultipartyChange(ImsPhoneConnection conn, boolean isMultiParty)327 public synchronized void onMultipartyChange(ImsPhoneConnection conn, boolean isMultiParty) { 328 int id = getConnectionId(conn); 329 VoiceCallSession proto = mCallProtos.get(id); 330 if (proto == null) { 331 loge("onMultipartyChange: untracked connection, connectionId=%d", id); 332 return; 333 } 334 logd("onMultipartyChange: isMultiparty=%b, connectionId=%d", isMultiParty, id); 335 if (isMultiParty) { 336 proto.isMultiparty = true; 337 } 338 } 339 340 /** 341 * Updates internal states when a call changes state to track setup time and status. 342 * 343 * <p>This is currently mainly used by IMS since CS call states are updated through {@link 344 * #onRilCallListChanged}. 345 */ onCallStateChanged(Call call)346 public synchronized void onCallStateChanged(Call call) { 347 for (Connection conn : call.getConnections()) { 348 int id = getConnectionId(conn); 349 VoiceCallSession proto = mCallProtos.get(id); 350 if (proto != null) { 351 checkCallSetup(conn, proto); 352 } else { 353 loge("onCallStateChanged: untracked connection, connectionId=%d", id); 354 } 355 } 356 } 357 358 /** Updates internal states when an IMS call is handover to a CS call. */ onRilSrvccStateChanged(int state)359 public synchronized void onRilSrvccStateChanged(int state) { 360 List<Connection> handoverConnections = null; 361 if (mPhone.getImsPhone() != null) { 362 handoverConnections = mPhone.getImsPhone().getHandoverConnection(); 363 } else { 364 loge("onRilSrvccStateChanged: ImsPhone is null"); 365 } 366 List<Integer> imsConnIds; 367 if (handoverConnections == null) { 368 imsConnIds = getImsConnectionIds(); 369 loge("onRilSrvccStateChanged: ImsPhone has no handover, we have %d", imsConnIds.size()); 370 } else { 371 imsConnIds = 372 handoverConnections.stream() 373 .map(VoiceCallSessionStats::getConnectionId) 374 .collect(Collectors.toList()); 375 } 376 switch (state) { 377 case TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED: 378 // connection will now be CS 379 for (int id : imsConnIds) { 380 VoiceCallSession proto = mCallProtos.get(id); 381 proto.srvccCompleted = true; 382 proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS; 383 // Call RAT may have changed (e.g. IWLAN -> UMTS) due to bearer change 384 updateRatAtEnd(proto, getVoiceRatWithVoNRFix( 385 mPhone, mPhone.getServiceState(), proto.bearerAtEnd)); 386 } 387 break; 388 case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED: 389 for (int id : imsConnIds) { 390 mCallProtos.get(id).srvccFailureCount++; 391 } 392 break; 393 case TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED: 394 for (int id : imsConnIds) { 395 mCallProtos.get(id).srvccCancellationCount++; 396 } 397 break; 398 default: // including STARTED and NONE, do nothing 399 } 400 } 401 402 /** Updates internal states when RAT changes. */ onServiceStateChanged(ServiceState state)403 public synchronized void onServiceStateChanged(ServiceState state) { 404 if (hasCalls()) { 405 updateRatTracker(state); 406 } 407 } 408 409 /* internal */ 410 411 /** Handles ringing MT call getting accepted. */ acceptCall(Connection conn)412 private void acceptCall(Connection conn) { 413 int id = getConnectionId(conn); 414 if (mCallProtos.contains(id)) { 415 logd("acceptCall: resetting setup info, connectionId=%d", id); 416 VoiceCallSession proto = mCallProtos.get(id); 417 proto.setupBeginMillis = getTimeMillis(); 418 proto.setupDuration = VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN; 419 } else { 420 loge("acceptCall: untracked connection, connectionId=%d", id); 421 } 422 } 423 424 /** 425 * Adds a call connection. 426 * 427 * <p>Should be called when the call is created, and when setup begins (upon {@code 428 * RilRequest.RIL_REQUEST_ANSWER} or {@code ImsCommand.IMS_CMD_ACCEPT}). 429 */ addCall(Connection conn)430 private void addCall(Connection conn) { 431 int id = getConnectionId(conn); 432 if (mCallProtos.contains(id)) { 433 loge( 434 "addCall: already tracked connection, connectionId=%d, connectionInfo=%s", 435 id, conn); 436 return; 437 } 438 int bearer = getBearer(conn); 439 ServiceState serviceState = getServiceState(); 440 @NetworkType int rat = getVoiceRatWithVoNRFix(mPhone, serviceState, bearer); 441 VoiceCallSession proto = new VoiceCallSession(); 442 443 proto.bearerAtStart = bearer; 444 proto.bearerAtEnd = bearer; 445 proto.direction = getDirection(conn); 446 proto.setupDuration = VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN; 447 proto.setupFailed = true; 448 proto.disconnectReasonCode = conn.getDisconnectCause(); 449 proto.disconnectExtraCode = conn.getPreciseDisconnectCause(); 450 proto.disconnectExtraMessage = conn.getVendorDisconnectCause(); 451 proto.ratAtStart = rat; 452 proto.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN; 453 proto.ratAtEnd = rat; 454 proto.ratSwitchCount = 0L; 455 proto.codecBitmask = 0L; 456 proto.simSlotIndex = mPhoneId; 457 proto.isMultiSim = SimSlotState.isMultiSim(); 458 proto.isEsim = SimSlotState.isEsim(mPhoneId); 459 proto.carrierId = mPhone.getCarrierId(); 460 proto.srvccCompleted = false; 461 proto.srvccFailureCount = 0L; 462 proto.srvccCancellationCount = 0L; 463 proto.rttEnabled = false; 464 proto.isEmergency = conn.isEmergencyCall(); 465 proto.isRoaming = serviceState != null ? serviceState.getVoiceRoaming() : false; 466 proto.isMultiparty = conn.isMultiparty(); 467 proto.lastKnownRat = rat; 468 469 // internal fields for tracking 470 if (getDirection(conn) == VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT) { 471 // MT call setup hasn't begun hence set to 0 472 proto.setupBeginMillis = 0L; 473 } else { 474 proto.setupBeginMillis = getTimeMillis(); 475 } 476 477 // audio codec might have already been set 478 int codec = audioQualityToCodec(bearer, conn.getAudioCodec()); 479 if (codec != AudioCodec.AUDIO_CODEC_UNKNOWN) { 480 proto.codecBitmask = (1L << codec); 481 } 482 483 proto.concurrentCallCountAtStart = mCallProtos.size(); 484 if (proto.concurrentCallCountAtStart > MAX_NORMAL_CONCURRENT_CALLS) { 485 AnomalyReporter.reportAnomaly( 486 CONCURRENT_CALL_ANOMALY_UUID, "Anomalous number of concurrent calls"); 487 } 488 mCallProtos.put(id, proto); 489 490 // RAT call count needs to be updated 491 updateRatTracker(serviceState); 492 } 493 494 /** Sends the call metrics to persist storage when it is finished. */ finishCall(int connectionId)495 private void finishCall(int connectionId) { 496 VoiceCallSession proto = mCallProtos.get(connectionId); 497 if (proto == null) { 498 loge("finishCall: could not find call to be removed, connectionId=%d", connectionId); 499 return; 500 } 501 502 // Compute time it took to fail setup (except for MT calls that have never been picked up) 503 if (proto.setupFailed && proto.setupBeginMillis != 0L && proto.setupDurationMillis == 0) { 504 proto.setupDurationMillis = (int) (getTimeMillis() - proto.setupBeginMillis); 505 } 506 507 mCallProtos.delete(connectionId); 508 proto.concurrentCallCountAtEnd = mCallProtos.size(); 509 510 // Calculate signal strength at the end of the call 511 proto.signalStrengthAtEnd = getSignalStrength(proto.ratAtEnd); 512 513 // Calculate main codec quality 514 proto.mainCodecQuality = finalizeMainCodecQuality(connectionId); 515 516 // ensure internal fields are cleared 517 proto.setupBeginMillis = 0L; 518 519 // sanitize for javanano & StatsEvent 520 if (proto.disconnectExtraMessage == null) { 521 proto.disconnectExtraMessage = ""; 522 } 523 524 // Retry populating carrier ID if it was invalid 525 if (proto.carrierId <= 0) { 526 proto.carrierId = mPhone.getCarrierId(); 527 } 528 529 // Update end RAT 530 updateRatAtEnd(proto, getVoiceRatWithVoNRFix(mPhone, getServiceState(), proto.bearerAtEnd)); 531 532 mAtomsStorage.addVoiceCallSession(proto); 533 534 // merge RAT usages to PersistPullers when the call session ends (i.e. no more active calls) 535 if (!hasCalls()) { 536 mRatUsage.conclude(getTimeMillis()); 537 mAtomsStorage.addVoiceCallRatUsage(mRatUsage); 538 mRatUsage.clear(); 539 } 540 } 541 setRttStarted(ImsPhoneConnection conn)542 private void setRttStarted(ImsPhoneConnection conn) { 543 int id = getConnectionId(conn); 544 VoiceCallSession proto = mCallProtos.get(id); 545 if (proto == null) { 546 loge("onRttStarted: untracked connection, connectionId=%d", id); 547 return; 548 } 549 // should be IMS w/o SRVCC 550 if (proto.bearerAtStart != getBearer(conn) || proto.bearerAtEnd != getBearer(conn)) { 551 loge("onRttStarted: connection bearer mismatch but proceeding, connectionId=%d", id); 552 } 553 proto.rttEnabled = true; 554 } 555 556 /** Returns a {@link Set} of Connection IDs so RAT usage can be correctly tracked. */ getConnectionIds()557 private Set<Integer> getConnectionIds() { 558 Set<Integer> ids = new HashSet<>(); 559 for (int i = 0; i < mCallProtos.size(); i++) { 560 ids.add(mCallProtos.keyAt(i)); 561 } 562 return ids; 563 } 564 getImsConnectionIds()565 private List<Integer> getImsConnectionIds() { 566 List<Integer> imsConnIds = new ArrayList<>(mCallProtos.size()); 567 for (int i = 0; i < mCallProtos.size(); i++) { 568 if (mCallProtos.valueAt(i).bearerAtEnd 569 == VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) { 570 imsConnIds.add(mCallProtos.keyAt(i)); 571 } 572 } 573 return imsConnIds; 574 } 575 hasCalls()576 private boolean hasCalls() { 577 return mCallProtos.size() > 0; 578 } 579 checkCallSetup(Connection conn, VoiceCallSession proto)580 private void checkCallSetup(Connection conn, VoiceCallSession proto) { 581 if (proto.setupBeginMillis != 0L && isSetupFinished(conn.getCall())) { 582 proto.setupDurationMillis = (int) (getTimeMillis() - proto.setupBeginMillis); 583 proto.setupDuration = classifySetupDuration(proto.setupDurationMillis); 584 proto.setupBeginMillis = 0L; 585 } 586 // Clear setupFailed if call now active, but otherwise leave it unchanged 587 // This block is executed only once, when call becomes active for the first time. 588 if (proto.setupFailed && conn.getState() == Call.State.ACTIVE) { 589 proto.setupFailed = false; 590 // Track RAT when voice call is connected. 591 ServiceState serviceState = getServiceState(); 592 proto.ratAtConnected = getVoiceRatWithVoNRFix(mPhone, serviceState, proto.bearerAtEnd); 593 // Reset list of codecs with the last codec at the present time. In this way, we 594 // track codec quality only after call is connected and not while ringing. 595 resetCodecList(conn); 596 } 597 } 598 updateRatTracker(ServiceState state)599 private void updateRatTracker(ServiceState state) { 600 // RAT usage is not broken down by bearer. In case a CS call is made while there is IMS 601 // voice registration, this may be inaccurate (i.e. there could be multiple RAT in use, but 602 // we only pick the most feasible one). 603 @NetworkType int rat = getVoiceRatWithVoNRFix(mPhone, state, 604 VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN); 605 mRatUsage.add(mPhone.getCarrierId(), rat, getTimeMillis(), getConnectionIds()); 606 607 for (int i = 0; i < mCallProtos.size(); i++) { 608 VoiceCallSession proto = mCallProtos.valueAt(i); 609 rat = getVoiceRatWithVoNRFix(mPhone, state, proto.bearerAtEnd); 610 updateRatAtEnd(proto, rat); 611 proto.bandAtEnd = (rat == TelephonyManager.NETWORK_TYPE_IWLAN) 612 ? 0 613 : ServiceStateStats.getBand(state); 614 // assuming that SIM carrier ID does not change during the call 615 } 616 } 617 updateRatAtEnd(VoiceCallSession proto, @NetworkType int rat)618 private void updateRatAtEnd(VoiceCallSession proto, @NetworkType int rat) { 619 if (proto.ratAtEnd != rat) { 620 proto.ratSwitchCount++; 621 proto.ratAtEnd = rat; 622 if (rat != TelephonyManager.NETWORK_TYPE_UNKNOWN) { 623 proto.lastKnownRat = rat; 624 } 625 } 626 } 627 finishImsCall(int id, ImsReasonInfo reasonInfo, long durationMillis)628 private void finishImsCall(int id, ImsReasonInfo reasonInfo, long durationMillis) { 629 VoiceCallSession proto = mCallProtos.get(id); 630 proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS; 631 proto.disconnectReasonCode = reasonInfo.mCode; 632 proto.disconnectExtraCode = reasonInfo.mExtraCode; 633 proto.disconnectExtraMessage = ImsStats.filterExtraMessage(reasonInfo.mExtraMessage); 634 proto.callDuration = classifyCallDuration(durationMillis); 635 finishCall(id); 636 } 637 getServiceState()638 private @Nullable ServiceState getServiceState() { 639 ServiceStateTracker tracker = mPhone.getServiceStateTracker(); 640 return tracker != null ? tracker.getServiceState() : null; 641 } 642 getDirection(Connection conn)643 private static int getDirection(Connection conn) { 644 return conn.isIncoming() 645 ? VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT 646 : VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO; 647 } 648 getBearer(Connection conn)649 private static int getBearer(Connection conn) { 650 int phoneType = conn.getPhoneType(); 651 switch (phoneType) { 652 case PhoneConstants.PHONE_TYPE_GSM: 653 case PhoneConstants.PHONE_TYPE_CDMA: 654 return VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS; 655 case PhoneConstants.PHONE_TYPE_IMS: 656 return VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS; 657 default: 658 loge("getBearer: unknown phoneType=%d", phoneType); 659 return VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN; 660 } 661 } 662 663 /** Returns the signal strength. */ getSignalStrength(@etworkType int rat)664 private int getSignalStrength(@NetworkType int rat) { 665 if (rat == TelephonyManager.NETWORK_TYPE_IWLAN) { 666 return getSignalStrengthWifi(); 667 } else { 668 return getSignalStrengthCellular(); 669 } 670 } 671 672 /** Returns the signal strength of WiFi. */ getSignalStrengthWifi()673 private int getSignalStrengthWifi() { 674 WifiManager wifiManager = 675 (WifiManager) mPhone.getContext().getSystemService(Context.WIFI_SERVICE); 676 WifiInfo wifiInfo = wifiManager.getConnectionInfo(); 677 int result = VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_NONE_OR_UNKNOWN; 678 if (wifiInfo != null) { 679 int level = wifiManager.calculateSignalLevel(wifiInfo.getRssi()); 680 int max = wifiManager.getMaxSignalLevel(); 681 // Scale result into 0 to 4 range. 682 result = 683 VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_GREAT * level / max; 684 logd("WiFi level: " + result + " (" + level + "/" + max + ")"); 685 } 686 return result; 687 } 688 689 /** Returns the signal strength of cellular RAT. */ getSignalStrengthCellular()690 private int getSignalStrengthCellular() { 691 return mPhone.getSignalStrength().getLevel(); 692 } 693 694 /** 695 * This is a copy of ServiceStateStats.getVoiceRat(Phone, ServiceState, int) with minimum fix 696 * required for tracking EPSFB correctly. 697 */ getVoiceRatWithVoNRFix( Phone phone, @Nullable ServiceState state, int bearer)698 @VisibleForTesting private static @NetworkType int getVoiceRatWithVoNRFix( 699 Phone phone, @Nullable ServiceState state, int bearer) { 700 if (state == null) { 701 return TelephonyManager.NETWORK_TYPE_UNKNOWN; 702 } 703 ImsPhone imsPhone = (ImsPhone) phone.getImsPhone(); 704 if (bearer != VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS && imsPhone != null) { 705 @NetworkType int imsVoiceRat = imsPhone.getImsStats().getImsVoiceRadioTech(); 706 @NetworkType int wwanPsRat = 707 ServiceStateStats.getRat(state, NetworkRegistrationInfo.DOMAIN_PS); 708 if (imsVoiceRat != TelephonyManager.NETWORK_TYPE_UNKNOWN) { 709 // If IMS is registered over WWAN but WWAN PS is not in service, 710 // fallback to WWAN CS RAT 711 boolean isImsVoiceRatValid = 712 (imsVoiceRat == TelephonyManager.NETWORK_TYPE_IWLAN 713 || wwanPsRat != TelephonyManager.NETWORK_TYPE_UNKNOWN); 714 if (isImsVoiceRatValid) { 715 // Fix for VoNR and EPSFB, b/277906557 716 @NetworkType int oldRat = ServiceStateStats.getVoiceRat(phone, state, bearer), 717 rat = imsVoiceRat == TelephonyManager.NETWORK_TYPE_IWLAN 718 ? imsVoiceRat : wwanPsRat; 719 logd("getVoiceRatWithVoNRFix: oldRat=%d, newRat=%d", oldRat, rat); 720 return rat; 721 } 722 } 723 } 724 if (bearer == VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) { 725 return TelephonyManager.NETWORK_TYPE_UNKNOWN; 726 } else { 727 return ServiceStateStats.getRat(state, NetworkRegistrationInfo.DOMAIN_CS); 728 } 729 } 730 731 /** Resets the list of codecs used for the connection with only the codec currently in use. */ resetCodecList(Connection conn)732 private void resetCodecList(Connection conn) { 733 int id = getConnectionId(conn); 734 LongSparseArray<Integer> codecUsage = mCodecUsage.get(id); 735 if (codecUsage != null) { 736 int lastCodec = codecUsage.valueAt(codecUsage.size() - 1); 737 LongSparseArray<Integer> arr = new LongSparseArray<>(); 738 arr.append(getTimeMillis(), lastCodec); 739 mCodecUsage.put(id, arr); 740 } 741 } 742 743 /** Returns the main codec quality used during the call. */ finalizeMainCodecQuality(int connectionId)744 private int finalizeMainCodecQuality(int connectionId) { 745 // Retrieve information about codec usage for this call and remove it from main array. 746 if (!mCodecUsage.contains(connectionId)) { 747 return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN; 748 } 749 LongSparseArray<Integer> codecUsage = mCodecUsage.get(connectionId); 750 mCodecUsage.delete(connectionId); 751 752 // Append fake entry at the end, to facilitate the calculation of time for each codec. 753 codecUsage.put(getTimeMillis(), AudioCodec.AUDIO_CODEC_UNKNOWN); 754 755 // Calculate array with time for each quality 756 int totalTime = 0; 757 long[] timePerQuality = new long[CODEC_QUALITY_COUNT]; 758 for (int i = 0; i < codecUsage.size() - 1; i++) { 759 long time = codecUsage.keyAt(i + 1) - codecUsage.keyAt(i); 760 int quality = getCodecQuality(codecUsage.valueAt(i)); 761 timePerQuality[quality] += time; 762 totalTime += time; 763 } 764 logd("Time per codec quality = " + Arrays.toString(timePerQuality)); 765 766 // We calculate 70% duration of the call as the threshold for the main audio codec quality 767 // and iterate on all codec qualities. As soon as the sum of codec duration is greater than 768 // the threshold, we have identified the main codec quality. 769 long timeAtMinimumQuality = 0; 770 long timeThreshold = totalTime * MAIN_CODEC_QUALITY_THRESHOLD / 100; 771 for (int i = CODEC_QUALITY_COUNT - 1; i >= 0; i--) { 772 timeAtMinimumQuality += timePerQuality[i]; 773 if (timeAtMinimumQuality >= timeThreshold) { 774 return i; 775 } 776 } 777 return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN; 778 } 779 getCodecQuality(int codec)780 private int getCodecQuality(int codec) { 781 switch (codec) { 782 case AudioCodec.AUDIO_CODEC_AMR: 783 case AudioCodec.AUDIO_CODEC_QCELP13K: 784 case AudioCodec.AUDIO_CODEC_EVRC: 785 case AudioCodec.AUDIO_CODEC_EVRC_B: 786 case AudioCodec.AUDIO_CODEC_EVRC_NW: 787 case AudioCodec.AUDIO_CODEC_GSM_EFR: 788 case AudioCodec.AUDIO_CODEC_GSM_FR: 789 case AudioCodec.AUDIO_CODEC_GSM_HR: 790 case AudioCodec.AUDIO_CODEC_G711U: 791 case AudioCodec.AUDIO_CODEC_G723: 792 case AudioCodec.AUDIO_CODEC_G711A: 793 case AudioCodec.AUDIO_CODEC_G722: 794 case AudioCodec.AUDIO_CODEC_G711AB: 795 case AudioCodec.AUDIO_CODEC_G729: 796 case AudioCodec.AUDIO_CODEC_EVS_NB: 797 return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND; 798 case AudioCodec.AUDIO_CODEC_AMR_WB: 799 case AudioCodec.AUDIO_CODEC_EVS_WB: 800 case AudioCodec.AUDIO_CODEC_EVRC_WB: 801 return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_WIDEBAND; 802 case AudioCodec.AUDIO_CODEC_EVS_SWB: 803 return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND; 804 case AudioCodec.AUDIO_CODEC_EVS_FB: 805 return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_FULLBAND; 806 default: 807 return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN; 808 } 809 } 810 isSetupFinished(@ullable Call call)811 private static boolean isSetupFinished(@Nullable Call call) { 812 // NOTE: when setup is finished for MO calls, it is not successful yet. 813 if (call != null) { 814 switch (call.getState()) { 815 case ACTIVE: // MT setup: accepted to ACTIVE 816 case ALERTING: // MO setup: dial to ALERTING 817 return true; 818 default: // do nothing 819 } 820 } 821 return false; 822 } 823 audioQualityToCodec(int bearer, int audioQuality)824 private static int audioQualityToCodec(int bearer, int audioQuality) { 825 switch (bearer) { 826 case VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS: 827 return CS_CODEC_MAP.get(audioQuality, AudioCodec.AUDIO_CODEC_UNKNOWN); 828 case VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS: 829 return IMS_CODEC_MAP.get(audioQuality, AudioCodec.AUDIO_CODEC_UNKNOWN); 830 default: 831 loge("audioQualityToCodec: unknown bearer %d", bearer); 832 return AudioCodec.AUDIO_CODEC_UNKNOWN; 833 } 834 } 835 classifySetupDuration(int durationMillis)836 private static int classifySetupDuration(int durationMillis) { 837 // keys in CALL_SETUP_DURATION_MAP are upper bounds in ascending order 838 for (int i = 0; i < CALL_SETUP_DURATION_MAP.size(); i++) { 839 if (durationMillis < CALL_SETUP_DURATION_MAP.keyAt(i)) { 840 return CALL_SETUP_DURATION_MAP.valueAt(i); 841 } 842 } 843 return VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_SLOW; 844 } 845 classifyCallDuration(long durationMillis)846 private static int classifyCallDuration(long durationMillis) { 847 if (durationMillis == 0L) { 848 return VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN; 849 } 850 // keys in CALL_SETUP_DURATION_MAP are upper bounds in ascending order 851 for (int i = 0; i < CALL_DURATION_MAP.size(); i++) { 852 if (durationMillis < CALL_DURATION_MAP.keyAt(i)) { 853 return CALL_DURATION_MAP.valueAt(i); 854 } 855 } 856 return VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_MORE_THAN_ONE_HOUR; 857 } 858 859 /** 860 * Generates an ID for each connection, which should be the same for IMS and CS connections 861 * involved in the same SRVCC. 862 * 863 * <p>Among the fields copied from ImsPhoneConnection to GsmCdmaConnection during SRVCC, the 864 * Connection's create time seems to be the best choice for ID (assuming no multiple calls in a 865 * millisecond). The 64-bit time is truncated to 32-bit so it can be used as an index in various 866 * data structures, which is good for calls shorter than 49 days. 867 */ getConnectionId(Connection conn)868 private static int getConnectionId(Connection conn) { 869 return conn == null ? 0 : (int) conn.getCreateTime(); 870 } 871 872 @VisibleForTesting getTimeMillis()873 protected long getTimeMillis() { 874 return SystemClock.elapsedRealtime(); 875 } 876 logd(String format, Object... args)877 private static void logd(String format, Object... args) { 878 Rlog.d(TAG, String.format(format, args)); 879 } 880 loge(String format, Object... args)881 private static void loge(String format, Object... args) { 882 Rlog.e(TAG, String.format(format, args)); 883 } 884 buildGsmCdmaCodecMap()885 private static SparseIntArray buildGsmCdmaCodecMap() { 886 SparseIntArray map = new SparseIntArray(); 887 map.put(DriverCall.AUDIO_QUALITY_AMR, AudioCodec.AUDIO_CODEC_AMR); 888 map.put(DriverCall.AUDIO_QUALITY_AMR_WB, AudioCodec.AUDIO_CODEC_AMR_WB); 889 map.put(DriverCall.AUDIO_QUALITY_GSM_EFR, AudioCodec.AUDIO_CODEC_GSM_EFR); 890 map.put(DriverCall.AUDIO_QUALITY_GSM_FR, AudioCodec.AUDIO_CODEC_GSM_FR); 891 map.put(DriverCall.AUDIO_QUALITY_GSM_HR, AudioCodec.AUDIO_CODEC_GSM_HR); 892 map.put(DriverCall.AUDIO_QUALITY_EVRC, AudioCodec.AUDIO_CODEC_EVRC); 893 map.put(DriverCall.AUDIO_QUALITY_EVRC_B, AudioCodec.AUDIO_CODEC_EVRC_B); 894 map.put(DriverCall.AUDIO_QUALITY_EVRC_WB, AudioCodec.AUDIO_CODEC_EVRC_WB); 895 map.put(DriverCall.AUDIO_QUALITY_EVRC_NW, AudioCodec.AUDIO_CODEC_EVRC_NW); 896 return map; 897 } 898 buildImsCodecMap()899 private static SparseIntArray buildImsCodecMap() { 900 SparseIntArray map = new SparseIntArray(); 901 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_AMR, AudioCodec.AUDIO_CODEC_AMR); 902 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB, AudioCodec.AUDIO_CODEC_AMR_WB); 903 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K, AudioCodec.AUDIO_CODEC_QCELP13K); 904 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC, AudioCodec.AUDIO_CODEC_EVRC); 905 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B, AudioCodec.AUDIO_CODEC_EVRC_B); 906 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB, AudioCodec.AUDIO_CODEC_EVRC_WB); 907 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW, AudioCodec.AUDIO_CODEC_EVRC_NW); 908 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR, AudioCodec.AUDIO_CODEC_GSM_EFR); 909 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR, AudioCodec.AUDIO_CODEC_GSM_FR); 910 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR, AudioCodec.AUDIO_CODEC_GSM_HR); 911 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711U, AudioCodec.AUDIO_CODEC_G711U); 912 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G723, AudioCodec.AUDIO_CODEC_G723); 913 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711A, AudioCodec.AUDIO_CODEC_G711A); 914 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G722, AudioCodec.AUDIO_CODEC_G722); 915 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711AB, AudioCodec.AUDIO_CODEC_G711AB); 916 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G729, AudioCodec.AUDIO_CODEC_G729); 917 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB, AudioCodec.AUDIO_CODEC_EVS_NB); 918 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB, AudioCodec.AUDIO_CODEC_EVS_WB); 919 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB, AudioCodec.AUDIO_CODEC_EVS_SWB); 920 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB, AudioCodec.AUDIO_CODEC_EVS_FB); 921 return map; 922 } 923 buildCallSetupDurationMap()924 private static SparseIntArray buildCallSetupDurationMap() { 925 SparseIntArray map = new SparseIntArray(); 926 927 map.put( 928 CALL_SETUP_DURATION_UNKNOWN, 929 VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN); 930 map.put( 931 CALL_SETUP_DURATION_EXTREMELY_FAST, 932 VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST); 933 map.put( 934 CALL_SETUP_DURATION_ULTRA_FAST, 935 VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST); 936 map.put( 937 CALL_SETUP_DURATION_VERY_FAST, 938 VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST); 939 map.put( 940 CALL_SETUP_DURATION_FAST, 941 VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_FAST); 942 map.put( 943 CALL_SETUP_DURATION_NORMAL, 944 VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_NORMAL); 945 map.put( 946 CALL_SETUP_DURATION_SLOW, 947 VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_SLOW); 948 map.put( 949 CALL_SETUP_DURATION_VERY_SLOW, 950 VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW); 951 map.put( 952 CALL_SETUP_DURATION_ULTRA_SLOW, 953 VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_SLOW); 954 // anything above would be CALL_SETUP_DURATION_EXTREMELY_SLOW 955 956 return map; 957 } 958 buildCallDurationMap()959 private static SparseIntArray buildCallDurationMap() { 960 SparseIntArray map = new SparseIntArray(); 961 962 map.put( 963 CALL_DURATION_ONE_MINUTE, 964 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_ONE_MINUTE); 965 map.put( 966 CALL_DURATION_FIVE_MINUTES, 967 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_FIVE_MINUTES); 968 map.put( 969 CALL_DURATION_TEN_MINUTES, 970 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_TEN_MINUTES); 971 map.put( 972 CALL_DURATION_THIRTY_MINUTES, 973 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_THIRTY_MINUTES); 974 map.put( 975 CALL_DURATION_ONE_HOUR, 976 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_ONE_HOUR); 977 // anything above would be MORE_THAN_ONE_HOUR 978 979 return map; 980 } 981 } 982