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