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