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__DIRECTION__CALL_DIRECTION_MO; 23 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT; 24 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST; 25 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_SLOW; 26 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_FAST; 27 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_NORMAL; 28 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_SLOW; 29 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST; 30 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_SLOW; 31 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN; 32 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST; 33 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW; 34 35 import android.annotation.Nullable; 36 import android.os.SystemClock; 37 import android.telephony.Annotation.NetworkType; 38 import android.telephony.DisconnectCause; 39 import android.telephony.ServiceState; 40 import android.telephony.SubscriptionInfo; 41 import android.telephony.TelephonyManager; 42 import android.telephony.ims.ImsReasonInfo; 43 import android.telephony.ims.ImsStreamMediaProfile; 44 import android.util.SparseArray; 45 import android.util.SparseIntArray; 46 import android.util.SparseLongArray; 47 48 import com.android.internal.annotations.VisibleForTesting; 49 import com.android.internal.telephony.Call; 50 import com.android.internal.telephony.Connection; 51 import com.android.internal.telephony.DriverCall; 52 import com.android.internal.telephony.GsmCdmaConnection; 53 import com.android.internal.telephony.Phone; 54 import com.android.internal.telephony.PhoneConstants; 55 import com.android.internal.telephony.PhoneFactory; 56 import com.android.internal.telephony.ServiceStateTracker; 57 import com.android.internal.telephony.imsphone.ImsPhoneConnection; 58 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession; 59 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.AudioCodec; 60 import com.android.internal.telephony.uicc.UiccController; 61 import com.android.internal.telephony.uicc.UiccSlot; 62 import com.android.telephony.Rlog; 63 64 import java.util.ArrayList; 65 import java.util.HashSet; 66 import java.util.List; 67 import java.util.Set; 68 import java.util.stream.Collectors; 69 70 /** Collects voice call events per phone ID for the pulled atom. */ 71 public class VoiceCallSessionStats { 72 private static final String TAG = VoiceCallSessionStats.class.getSimpleName(); 73 74 /** Bitmask value of unknown audio codecs. */ 75 private static final long AUDIO_CODEC_UNKNOWN = 1L << AudioCodec.AUDIO_CODEC_UNKNOWN; 76 77 /** 78 * Value denoting the carrier ID being unknown. 79 * 80 * <p>NOTE: 0 is unused in {@code carrier_list.textpb} (it starts from 1). 81 */ 82 private static final int CARRIER_ID_UNKNOWN = 0; 83 84 /** Holds the audio codec bitmask value for CS calls. */ 85 private static final SparseLongArray CS_CODEC_MAP = buildGsmCdmaCodecMap(); 86 87 /** Holds the audio codec bitmask value for IMS calls. */ 88 private static final SparseLongArray IMS_CODEC_MAP = buildImsCodecMap(); 89 90 /** Holds setup duration buckets with keys as their lower bounds in milliseconds. */ 91 private static final SparseIntArray CALL_SETUP_DURATION_MAP = buildCallSetupDurationMap(); 92 93 /** 94 * Tracks statistics for each call connection, indexed with ID returned by {@link 95 * #getConnectionId}. 96 */ 97 private final SparseArray<VoiceCallSession> mCallProtos = new SparseArray<>(); 98 99 /** 100 * Tracks call RAT usage. 101 * 102 * <p>RAT usage is mainly tied to phones rather than calls, since each phone can have multiple 103 * concurrent calls, and we do not want to count the RAT duration multiple times. 104 */ 105 private final VoiceCallRatTracker mRatUsage = new VoiceCallRatTracker(); 106 107 private final int mPhoneId; 108 private final Phone mPhone; 109 private int mCarrierId = CARRIER_ID_UNKNOWN; 110 111 private final PersistAtomsStorage mAtomsStorage = 112 PhoneFactory.getMetricsCollector().getAtomsStorage(); 113 private final UiccController mUiccController = UiccController.getInstance(); 114 VoiceCallSessionStats(int phoneId, Phone phone)115 public VoiceCallSessionStats(int phoneId, Phone phone) { 116 mPhoneId = phoneId; 117 mPhone = phone; 118 } 119 120 /* CS calls */ 121 122 /** Updates internal states when previous CS calls are accepted to track MT call setup time. */ onRilAcceptCall(List<Connection> connections)123 public synchronized void onRilAcceptCall(List<Connection> connections) { 124 for (Connection conn : connections) { 125 addCall(conn); 126 } 127 } 128 129 /** Updates internal states when a CS MO call is created. */ onRilDial(Connection conn)130 public synchronized void onRilDial(Connection conn) { 131 addCall(conn); 132 } 133 134 /** 135 * Updates internal states when CS calls are created or terminated, or CS call state is changed. 136 */ onRilCallListChanged(List<GsmCdmaConnection> connections)137 public synchronized void onRilCallListChanged(List<GsmCdmaConnection> connections) { 138 for (Connection conn : connections) { 139 int id = getConnectionId(conn); 140 if (!mCallProtos.contains(id)) { 141 // handle new connections 142 if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { 143 addCall(conn); 144 checkCallSetup(conn, mCallProtos.get(id)); 145 } else { 146 logd("onRilCallListChanged: skip adding disconnected connection"); 147 } 148 } else { 149 VoiceCallSession proto = mCallProtos.get(id); 150 // handle call state change 151 checkCallSetup(conn, proto); 152 // handle terminated connections 153 if (conn.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED) { 154 proto.bearerAtEnd = getBearer(conn); // should be CS 155 proto.disconnectReasonCode = conn.getDisconnectCause(); 156 proto.disconnectExtraCode = conn.getPreciseDisconnectCause(); 157 proto.disconnectExtraMessage = conn.getVendorDisconnectCause(); 158 finishCall(id); 159 } 160 } 161 } 162 // NOTE: we cannot check stray connections (CS call in our list but not in RIL), as 163 // GsmCdmaCallTracker can call this with a partial list 164 } 165 166 /* IMS calls */ 167 168 /** Updates internal states when an IMS MO call is created. */ onImsDial(ImsPhoneConnection conn)169 public synchronized void onImsDial(ImsPhoneConnection conn) { 170 addCall(conn); 171 if (conn.hasRttTextStream()) { 172 setRttStarted(conn); 173 } 174 } 175 176 /** Updates internal states when an IMS MT call is created. */ onImsCallReceived(ImsPhoneConnection conn)177 public synchronized void onImsCallReceived(ImsPhoneConnection conn) { 178 addCall(conn); 179 if (conn.hasRttTextStream()) { 180 setRttStarted(conn); 181 } 182 } 183 184 /** Updates internal states when previous IMS calls are accepted to track MT call setup time. */ onImsAcceptCall(List<Connection> connections)185 public synchronized void onImsAcceptCall(List<Connection> connections) { 186 for (Connection conn : connections) { 187 addCall(conn); 188 } 189 } 190 191 /** Updates internal states when an IMS call is terminated. */ onImsCallTerminated( @ullable ImsPhoneConnection conn, ImsReasonInfo reasonInfo)192 public synchronized void onImsCallTerminated( 193 @Nullable ImsPhoneConnection conn, ImsReasonInfo reasonInfo) { 194 if (conn == null) { 195 List<Integer> imsConnIds = getImsConnectionIds(); 196 if (imsConnIds.size() == 1) { 197 loge("onImsCallTerminated: ending IMS call w/ conn=null"); 198 finishImsCall(imsConnIds.get(0), reasonInfo); 199 } else { 200 loge("onImsCallTerminated: %d IMS calls w/ conn=null", imsConnIds.size()); 201 } 202 } else { 203 int id = getConnectionId(conn); 204 if (mCallProtos.contains(id)) { 205 finishImsCall(id, reasonInfo); 206 } else { 207 loge("onImsCallTerminated: untracked connection"); 208 // fake a call so at least some info can be tracked 209 addCall(conn); 210 finishImsCall(id, reasonInfo); 211 } 212 } 213 } 214 215 /** Updates internal states when RTT is started on an IMS call. */ onRttStarted(ImsPhoneConnection conn)216 public synchronized void onRttStarted(ImsPhoneConnection conn) { 217 setRttStarted(conn); 218 } 219 220 /* general & misc. */ 221 222 /** Updates internal states when carrier changes. */ onActiveSubscriptionInfoChanged(List<SubscriptionInfo> subInfos)223 public synchronized void onActiveSubscriptionInfoChanged(List<SubscriptionInfo> subInfos) { 224 int slotId = getSimSlotId(); 225 if (subInfos != null) { 226 for (SubscriptionInfo subInfo : subInfos) { 227 if (subInfo.getSimSlotIndex() == slotId) { 228 mCarrierId = subInfo.getCarrierId(); 229 } 230 } 231 } 232 } 233 234 /** Updates internal states when audio codec for a call is changed. */ onAudioCodecChanged(Connection conn, int audioQuality)235 public synchronized void onAudioCodecChanged(Connection conn, int audioQuality) { 236 VoiceCallSession proto = mCallProtos.get(getConnectionId(conn)); 237 if (proto == null) { 238 loge("onAudioCodecChanged: untracked connection"); 239 return; 240 } 241 proto.codecBitmask |= audioQualityToCodecBitmask(proto.bearerAtEnd, audioQuality); 242 } 243 244 /** 245 * Updates internal states when a call changes state to track setup time and status. 246 * 247 * <p>This is currently mainly used by IMS since CS call states are updated through {@link 248 * #onRilCallListChanged}. 249 */ onCallStateChanged(Call call)250 public synchronized void onCallStateChanged(Call call) { 251 for (Connection conn : call.getConnections()) { 252 VoiceCallSession proto = mCallProtos.get(getConnectionId(conn)); 253 if (proto != null) { 254 checkCallSetup(conn, proto); 255 } else { 256 loge("onCallStateChanged: untracked connection"); 257 } 258 } 259 } 260 261 /** Updates internal states when an IMS call is handover to a CS call. */ onRilSrvccStateChanged(int state)262 public synchronized void onRilSrvccStateChanged(int state) { 263 List<Connection> handoverConnections = null; 264 if (mPhone.getImsPhone() != null) { 265 loge("onRilSrvccStateChanged: ImsPhone is null"); 266 } else { 267 handoverConnections = mPhone.getImsPhone().getHandoverConnection(); 268 } 269 List<Integer> imsConnIds; 270 if (handoverConnections == null) { 271 imsConnIds = getImsConnectionIds(); 272 loge("onRilSrvccStateChanged: ImsPhone has no handover, we have %d", imsConnIds.size()); 273 } else { 274 imsConnIds = 275 handoverConnections.stream() 276 .map(VoiceCallSessionStats::getConnectionId) 277 .collect(Collectors.toList()); 278 } 279 switch (state) { 280 case TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED: 281 // connection will now be CS 282 for (int id : imsConnIds) { 283 VoiceCallSession proto = mCallProtos.get(id); 284 proto.srvccCompleted = true; 285 proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS; 286 } 287 break; 288 case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED: 289 for (int id : imsConnIds) { 290 mCallProtos.get(id).srvccFailureCount++; 291 } 292 break; 293 case TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED: 294 for (int id : imsConnIds) { 295 mCallProtos.get(id).srvccCancellationCount++; 296 } 297 break; 298 default: // including STARTED and NONE, do nothing 299 } 300 } 301 302 /** Updates internal states when RAT changes. */ onServiceStateChanged(ServiceState state)303 public synchronized void onServiceStateChanged(ServiceState state) { 304 if (hasCalls()) { 305 updateRatTracker(state); 306 } 307 } 308 309 /* internal */ 310 311 /** 312 * Adds a call connection. 313 * 314 * <p>Should be called when the call is created, and when setup begins (upon {@code 315 * RilRequest.RIL_REQUEST_ANSWER} or {@code ImsCommand.IMS_CMD_ACCEPT}). 316 */ addCall(Connection conn)317 private void addCall(Connection conn) { 318 int id = getConnectionId(conn); 319 if (mCallProtos.contains(id)) { 320 // mostly handles ringing MT call getting accepted (MT call setup begins) 321 logd("addCall: resetting setup info"); 322 VoiceCallSession proto = mCallProtos.get(id); 323 proto.setupBeginMillis = getTimeMillis(); 324 proto.setupDuration = VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN; 325 } else { 326 int bearer = getBearer(conn); 327 ServiceState serviceState = getServiceState(); 328 int rat = getRat(serviceState); 329 330 VoiceCallSession proto = new VoiceCallSession(); 331 332 proto.bearerAtStart = bearer; 333 proto.bearerAtEnd = bearer; 334 proto.direction = getDirection(conn); 335 proto.setupDuration = VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN; 336 proto.setupFailed = true; 337 proto.disconnectReasonCode = conn.getDisconnectCause(); 338 proto.disconnectExtraCode = conn.getPreciseDisconnectCause(); 339 proto.disconnectExtraMessage = conn.getVendorDisconnectCause(); 340 proto.ratAtStart = rat; 341 proto.ratAtEnd = rat; 342 proto.ratSwitchCount = 0L; 343 proto.codecBitmask = 0L; 344 proto.simSlotIndex = getSimSlotId(); 345 proto.isMultiSim = SimSlotState.getCurrentState().numActiveSims > 1; 346 proto.isEsim = isEsim(); 347 proto.carrierId = mCarrierId; 348 proto.srvccCompleted = false; 349 proto.srvccFailureCount = 0L; 350 proto.srvccCancellationCount = 0L; 351 proto.rttEnabled = false; 352 proto.isEmergency = conn.isEmergencyCall(); 353 proto.isRoaming = serviceState != null ? serviceState.getVoiceRoaming() : false; 354 355 // internal fields for tracking 356 proto.setupBeginMillis = getTimeMillis(); 357 358 proto.concurrentCallCountAtStart = mCallProtos.size(); 359 mCallProtos.put(id, proto); 360 361 // RAT call count needs to be updated 362 updateRatTracker(serviceState); 363 } 364 } 365 366 /** Sends the call metrics to persist storage when it is finished. */ finishCall(int connectionId)367 private void finishCall(int connectionId) { 368 VoiceCallSession proto = mCallProtos.get(connectionId); 369 if (proto == null) { 370 loge("finishCall: could not find call to be removed"); 371 return; 372 } 373 mCallProtos.delete(connectionId); 374 proto.concurrentCallCountAtEnd = mCallProtos.size(); 375 376 // ensure internal fields are cleared 377 proto.setupBeginMillis = 0L; 378 379 // sanitize for javanano & StatsEvent 380 if (proto.disconnectExtraMessage == null) { 381 proto.disconnectExtraMessage = ""; 382 } 383 384 mAtomsStorage.addVoiceCallSession(proto); 385 386 // merge RAT usages to PersistPullers when the call session ends (i.e. no more active calls) 387 if (!hasCalls()) { 388 mRatUsage.conclude(getTimeMillis()); 389 mAtomsStorage.addVoiceCallRatUsage(mRatUsage); 390 mRatUsage.clear(); 391 } 392 } 393 setRttStarted(ImsPhoneConnection conn)394 private void setRttStarted(ImsPhoneConnection conn) { 395 VoiceCallSession proto = mCallProtos.get(getConnectionId(conn)); 396 if (proto == null) { 397 loge("onRttStarted: untracked connection"); 398 return; 399 } 400 // should be IMS w/o SRVCC 401 if (proto.bearerAtStart != getBearer(conn) || proto.bearerAtEnd != getBearer(conn)) { 402 loge("onRttStarted: connection bearer mismatch but proceeding"); 403 } 404 proto.rttEnabled = true; 405 } 406 407 /** Returns a {@link Set} of Connection IDs so RAT usage can be correctly tracked. */ getConnectionIds()408 private Set<Integer> getConnectionIds() { 409 Set<Integer> ids = new HashSet<>(); 410 for (int i = 0; i < mCallProtos.size(); i++) { 411 ids.add(mCallProtos.keyAt(i)); 412 } 413 return ids; 414 } 415 getImsConnectionIds()416 private List<Integer> getImsConnectionIds() { 417 List<Integer> imsConnIds = new ArrayList<>(mCallProtos.size()); 418 for (int i = 0; i < mCallProtos.size(); i++) { 419 if (mCallProtos.valueAt(i).bearerAtEnd 420 == VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) { 421 imsConnIds.add(mCallProtos.keyAt(i)); 422 } 423 } 424 return imsConnIds; 425 } 426 hasCalls()427 private boolean hasCalls() { 428 return mCallProtos.size() > 0; 429 } 430 checkCallSetup(Connection conn, VoiceCallSession proto)431 private void checkCallSetup(Connection conn, VoiceCallSession proto) { 432 if (proto.setupBeginMillis != 0L && isSetupFinished(conn.getCall())) { 433 proto.setupDuration = classifySetupDuration(getTimeMillis() - proto.setupBeginMillis); 434 proto.setupBeginMillis = 0L; 435 } 436 // clear setupFailed if call now active, but otherwise leave it unchanged 437 if (conn.getState() == Call.State.ACTIVE) { 438 proto.setupFailed = false; 439 } 440 } 441 updateRatTracker(ServiceState state)442 private void updateRatTracker(ServiceState state) { 443 int rat = getRat(state); 444 mRatUsage.add(mCarrierId, rat, getTimeMillis(), getConnectionIds()); 445 for (int i = 0; i < mCallProtos.size(); i++) { 446 VoiceCallSession proto = mCallProtos.valueAt(i); 447 if (proto.ratAtEnd != rat) { 448 proto.ratSwitchCount++; 449 proto.ratAtEnd = rat; 450 } 451 // assuming that SIM carrier ID does not change during the call 452 } 453 } 454 finishImsCall(int id, ImsReasonInfo reasonInfo)455 private void finishImsCall(int id, ImsReasonInfo reasonInfo) { 456 VoiceCallSession proto = mCallProtos.get(id); 457 proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS; 458 proto.disconnectReasonCode = reasonInfo.mCode; 459 proto.disconnectExtraCode = reasonInfo.mExtraCode; 460 proto.disconnectExtraMessage = reasonInfo.mExtraMessage; 461 finishCall(id); 462 } 463 isEsim()464 private boolean isEsim() { 465 int slotId = getSimSlotId(); 466 UiccSlot slot = mUiccController.getUiccSlot(slotId); 467 if (slot != null) { 468 return slot.isEuicc(); 469 } else { 470 // should not happen, but assume we are not using eSIM 471 loge("isEsim: slot %d is null", slotId); 472 return false; 473 } 474 } 475 getSimSlotId()476 private int getSimSlotId() { 477 // NOTE: UiccController's mapping hasn't be initialized when Phone was created 478 return mUiccController.getSlotIdFromPhoneId(mPhoneId); 479 } 480 getServiceState()481 private @Nullable ServiceState getServiceState() { 482 ServiceStateTracker tracker = mPhone.getServiceStateTracker(); 483 return tracker != null ? tracker.getServiceState() : null; 484 } 485 getDirection(Connection conn)486 private static int getDirection(Connection conn) { 487 return conn.isIncoming() 488 ? VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT 489 : VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO; 490 } 491 getBearer(Connection conn)492 private static int getBearer(Connection conn) { 493 int phoneType = conn.getPhoneType(); 494 switch (phoneType) { 495 case PhoneConstants.PHONE_TYPE_GSM: 496 case PhoneConstants.PHONE_TYPE_CDMA: 497 return VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS; 498 case PhoneConstants.PHONE_TYPE_IMS: 499 return VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS; 500 default: 501 loge("getBearer: unknown phoneType=%d", phoneType); 502 return VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN; 503 } 504 } 505 getRat(@ullable ServiceState state)506 private @NetworkType int getRat(@Nullable ServiceState state) { 507 if (state == null) { 508 return TelephonyManager.NETWORK_TYPE_UNKNOWN; 509 } 510 boolean isWifiCall = 511 mPhone.getImsPhone() != null 512 && mPhone.getImsPhone().isWifiCallingEnabled() 513 && state.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_IWLAN; 514 return isWifiCall ? TelephonyManager.NETWORK_TYPE_IWLAN : state.getVoiceNetworkType(); 515 } 516 517 // NOTE: when setup is finished for MO calls, it is not successful yet. isSetupFinished(@ullable Call call)518 private static boolean isSetupFinished(@Nullable Call call) { 519 if (call != null) { 520 switch (call.getState()) { 521 case ACTIVE: // MT setup: accepted to ACTIVE 522 case ALERTING: // MO setup: dial to ALERTING 523 return true; 524 default: // do nothing 525 } 526 } 527 return false; 528 } 529 audioQualityToCodecBitmask(int bearer, int audioQuality)530 private static long audioQualityToCodecBitmask(int bearer, int audioQuality) { 531 switch (bearer) { 532 case VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS: 533 return CS_CODEC_MAP.get(audioQuality, AUDIO_CODEC_UNKNOWN); 534 case VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS: 535 return IMS_CODEC_MAP.get(audioQuality, AUDIO_CODEC_UNKNOWN); 536 default: 537 loge("audioQualityToCodecBitmask: unknown bearer %d", bearer); 538 return AUDIO_CODEC_UNKNOWN; 539 } 540 } 541 classifySetupDuration(long durationMillis)542 private static int classifySetupDuration(long durationMillis) { 543 for (int i = 0; i < CALL_SETUP_DURATION_MAP.size(); i++) { 544 if (durationMillis < CALL_SETUP_DURATION_MAP.keyAt(i)) { 545 return CALL_SETUP_DURATION_MAP.valueAt(i); 546 } 547 } 548 return VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_SLOW; 549 } 550 551 /** 552 * Generates an ID for each connection, which should be the same for IMS and CS connections 553 * involved in the same SRVCC. 554 * 555 * <p>Among the fields copied from ImsPhoneConnection to GsmCdmaConnection during SRVCC, the 556 * Connection's create time seems to be the best choice for ID (assuming no multiple calls in a 557 * millisecond). The 64-bit time is truncated to 32-bit so it can be used as an index in various 558 * data structures, which is good for calls shorter than 49 days. 559 */ getConnectionId(Connection conn)560 private static int getConnectionId(Connection conn) { 561 return conn == null ? 0 : (int) conn.getCreateTime(); 562 } 563 564 @VisibleForTesting getTimeMillis()565 protected long getTimeMillis() { 566 return SystemClock.elapsedRealtime(); 567 } 568 logd(String format, Object... args)569 private static void logd(String format, Object... args) { 570 Rlog.d(TAG, String.format(format, args)); 571 } 572 loge(String format, Object... args)573 private static void loge(String format, Object... args) { 574 Rlog.e(TAG, String.format(format, args)); 575 } 576 buildGsmCdmaCodecMap()577 private static SparseLongArray buildGsmCdmaCodecMap() { 578 SparseLongArray map = new SparseLongArray(); 579 580 map.put(DriverCall.AUDIO_QUALITY_AMR, 1L << AudioCodec.AUDIO_CODEC_AMR); 581 map.put(DriverCall.AUDIO_QUALITY_AMR_WB, 1L << AudioCodec.AUDIO_CODEC_AMR_WB); 582 map.put(DriverCall.AUDIO_QUALITY_GSM_EFR, 1L << AudioCodec.AUDIO_CODEC_GSM_EFR); 583 map.put(DriverCall.AUDIO_QUALITY_GSM_FR, 1L << AudioCodec.AUDIO_CODEC_GSM_FR); 584 map.put(DriverCall.AUDIO_QUALITY_GSM_HR, 1L << AudioCodec.AUDIO_CODEC_GSM_HR); 585 map.put(DriverCall.AUDIO_QUALITY_EVRC, 1L << AudioCodec.AUDIO_CODEC_EVRC); 586 map.put(DriverCall.AUDIO_QUALITY_EVRC_B, 1L << AudioCodec.AUDIO_CODEC_EVRC_B); 587 map.put(DriverCall.AUDIO_QUALITY_EVRC_WB, 1L << AudioCodec.AUDIO_CODEC_EVRC_WB); 588 map.put(DriverCall.AUDIO_QUALITY_EVRC_NW, 1L << AudioCodec.AUDIO_CODEC_EVRC_NW); 589 590 return map; 591 } 592 buildImsCodecMap()593 private static SparseLongArray buildImsCodecMap() { 594 SparseLongArray map = new SparseLongArray(); 595 596 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_AMR, 1L << AudioCodec.AUDIO_CODEC_AMR); 597 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB, 1L << AudioCodec.AUDIO_CODEC_AMR_WB); 598 map.put( 599 ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K, 600 1L << AudioCodec.AUDIO_CODEC_QCELP13K); 601 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC, 1L << AudioCodec.AUDIO_CODEC_EVRC); 602 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B, 1L << AudioCodec.AUDIO_CODEC_EVRC_B); 603 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB, 1L << AudioCodec.AUDIO_CODEC_EVRC_WB); 604 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW, 1L << AudioCodec.AUDIO_CODEC_EVRC_NW); 605 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR, 1L << AudioCodec.AUDIO_CODEC_GSM_EFR); 606 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR, 1L << AudioCodec.AUDIO_CODEC_GSM_FR); 607 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR, 1L << AudioCodec.AUDIO_CODEC_GSM_HR); 608 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711U, 1L << AudioCodec.AUDIO_CODEC_G711U); 609 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G723, 1L << AudioCodec.AUDIO_CODEC_G723); 610 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711A, 1L << AudioCodec.AUDIO_CODEC_G711A); 611 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G722, 1L << AudioCodec.AUDIO_CODEC_G722); 612 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711AB, 1L << AudioCodec.AUDIO_CODEC_G711AB); 613 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G729, 1L << AudioCodec.AUDIO_CODEC_G729); 614 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB, 1L << AudioCodec.AUDIO_CODEC_EVS_NB); 615 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB, 1L << AudioCodec.AUDIO_CODEC_EVS_WB); 616 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB, 1L << AudioCodec.AUDIO_CODEC_EVS_SWB); 617 map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB, 1L << AudioCodec.AUDIO_CODEC_EVS_FB); 618 619 return map; 620 } 621 buildCallSetupDurationMap()622 private static SparseIntArray buildCallSetupDurationMap() { 623 SparseIntArray map = new SparseIntArray(); 624 625 map.put(0, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN); 626 map.put(60, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST); 627 map.put(100, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST); 628 map.put(300, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST); 629 map.put(600, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_FAST); 630 map.put(1000, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_NORMAL); 631 map.put(3000, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_SLOW); 632 map.put(6000, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW); 633 map.put(10000, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_SLOW); 634 // anything above would be CALL_SETUP_DURATION_EXTREMELY_SLOW 635 636 return map; 637 } 638 } 639