1 /* 2 * Copyright (C) 2014 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.services.telephony; 18 19 import android.content.Context; 20 import android.graphics.drawable.Icon; 21 import android.net.Uri; 22 import android.os.AsyncResult; 23 import android.os.Bundle; 24 import android.os.Handler; 25 import android.os.Looper; 26 import android.os.Message; 27 import android.os.PersistableBundle; 28 import android.telecom.CallAudioState; 29 import android.telecom.ConferenceParticipant; 30 import android.telecom.Connection; 31 import android.telecom.PhoneAccount; 32 import android.telecom.PhoneAccountHandle; 33 import android.telecom.StatusHints; 34 import android.telecom.TelecomManager; 35 import android.telecom.VideoProfile; 36 import android.telephony.CarrierConfigManager; 37 import android.telephony.DisconnectCause; 38 import android.telephony.PhoneNumberUtils; 39 import android.telephony.ServiceState; 40 import android.telephony.TelephonyManager; 41 import android.telephony.ims.ImsCallProfile; 42 import android.text.TextUtils; 43 import android.util.Pair; 44 45 import com.android.ims.ImsCall; 46 import com.android.internal.telephony.Call; 47 import com.android.internal.telephony.CallFailCause; 48 import com.android.internal.telephony.CallStateException; 49 import com.android.internal.telephony.Connection.Capability; 50 import com.android.internal.telephony.Connection.PostDialListener; 51 import com.android.internal.telephony.Phone; 52 import com.android.internal.telephony.PhoneConstants; 53 import com.android.internal.telephony.gsm.SuppServiceNotification; 54 import com.android.internal.telephony.imsphone.ImsPhone; 55 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker; 56 import com.android.internal.telephony.imsphone.ImsPhoneConnection; 57 import com.android.phone.ImsUtil; 58 import com.android.phone.PhoneGlobals; 59 import com.android.phone.PhoneUtils; 60 import com.android.phone.R; 61 62 import java.util.ArrayList; 63 import java.util.Arrays; 64 import java.util.Collections; 65 import java.util.HashMap; 66 import java.util.List; 67 import java.util.Map; 68 import java.util.Objects; 69 import java.util.Set; 70 import java.util.concurrent.ConcurrentHashMap; 71 72 /** 73 * Base class for CDMA and GSM connections. 74 */ 75 abstract class TelephonyConnection extends Connection implements Holdable { 76 private static final int MSG_PRECISE_CALL_STATE_CHANGED = 1; 77 private static final int MSG_RINGBACK_TONE = 2; 78 private static final int MSG_HANDOVER_STATE_CHANGED = 3; 79 private static final int MSG_DISCONNECT = 4; 80 private static final int MSG_MULTIPARTY_STATE_CHANGED = 5; 81 private static final int MSG_CONFERENCE_MERGE_FAILED = 6; 82 private static final int MSG_SUPP_SERVICE_NOTIFY = 7; 83 84 /** 85 * Mappings from {@link com.android.internal.telephony.Connection} extras keys to their 86 * equivalents defined in {@link android.telecom.Connection}. 87 */ 88 private static final Map<String, String> sExtrasMap = createExtrasMap(); 89 90 private static final int MSG_SET_VIDEO_STATE = 8; 91 private static final int MSG_SET_VIDEO_PROVIDER = 9; 92 private static final int MSG_SET_AUDIO_QUALITY = 10; 93 private static final int MSG_SET_CONFERENCE_PARTICIPANTS = 11; 94 private static final int MSG_CONNECTION_EXTRAS_CHANGED = 12; 95 private static final int MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES = 13; 96 private static final int MSG_ON_HOLD_TONE = 14; 97 private static final int MSG_CDMA_VOICE_PRIVACY_ON = 15; 98 private static final int MSG_CDMA_VOICE_PRIVACY_OFF = 16; 99 private static final int MSG_HANGUP = 17; 100 private static final int MSG_SET_CALL_RADIO_TECH = 18; 101 102 private final Handler mHandler = new Handler(Looper.getMainLooper()) { 103 @Override 104 public void handleMessage(Message msg) { 105 switch (msg.what) { 106 case MSG_PRECISE_CALL_STATE_CHANGED: 107 Log.v(TelephonyConnection.this, "MSG_PRECISE_CALL_STATE_CHANGED"); 108 updateState(); 109 break; 110 case MSG_HANDOVER_STATE_CHANGED: 111 Log.v(TelephonyConnection.this, "MSG_HANDOVER_STATE_CHANGED"); 112 AsyncResult ar = (AsyncResult) msg.obj; 113 com.android.internal.telephony.Connection connection = 114 (com.android.internal.telephony.Connection) ar.result; 115 if (mOriginalConnection != null) { 116 if (connection != null && 117 ((connection.getAddress() != null && 118 mOriginalConnection.getAddress() != null && 119 mOriginalConnection.getAddress().contains(connection.getAddress())) || 120 connection.getState() == mOriginalConnection.getStateBeforeHandover())) { 121 Log.d(TelephonyConnection.this, 122 "SettingOriginalConnection " + mOriginalConnection.toString() 123 + " with " + connection.toString()); 124 setOriginalConnection(connection); 125 mWasImsConnection = false; 126 } 127 } else { 128 Log.w(TelephonyConnection.this, 129 "MSG_HANDOVER_STATE_CHANGED: mOriginalConnection==null - invalid state (not cleaned up)"); 130 } 131 break; 132 case MSG_RINGBACK_TONE: 133 Log.v(TelephonyConnection.this, "MSG_RINGBACK_TONE"); 134 // TODO: This code assumes that there is only one connection in the foreground 135 // call, in other words, it punts on network-mediated conference calling. 136 if (getOriginalConnection() != getForegroundConnection()) { 137 Log.v(TelephonyConnection.this, "handleMessage, original connection is " + 138 "not foreground connection, skipping"); 139 return; 140 } 141 setRingbackRequested((Boolean) ((AsyncResult) msg.obj).result); 142 break; 143 case MSG_DISCONNECT: 144 updateState(); 145 break; 146 case MSG_MULTIPARTY_STATE_CHANGED: 147 boolean isMultiParty = (Boolean) msg.obj; 148 Log.i(this, "Update multiparty state to %s", isMultiParty ? "Y" : "N"); 149 mIsMultiParty = isMultiParty; 150 if (isMultiParty) { 151 notifyConferenceStarted(); 152 } 153 break; 154 case MSG_CONFERENCE_MERGE_FAILED: 155 notifyConferenceMergeFailed(); 156 break; 157 case MSG_SUPP_SERVICE_NOTIFY: 158 Phone phone = getPhone(); 159 Log.v(TelephonyConnection.this, "MSG_SUPP_SERVICE_NOTIFY on phoneId : " 160 + (phone != null ? Integer.toString(phone.getPhoneId()) 161 : "null")); 162 SuppServiceNotification mSsNotification = null; 163 if (msg.obj != null && ((AsyncResult) msg.obj).result != null) { 164 mSsNotification = 165 (SuppServiceNotification)((AsyncResult) msg.obj).result; 166 if (mOriginalConnection != null) { 167 handleSuppServiceNotification(mSsNotification); 168 } 169 } 170 break; 171 172 case MSG_SET_VIDEO_STATE: 173 int videoState = (int) msg.obj; 174 setVideoState(videoState); 175 176 // A change to the video state of the call can influence whether or not it 177 // can be part of a conference, whether another call can be added, and 178 // whether the call should have the HD audio property set. 179 refreshConferenceSupported(); 180 refreshDisableAddCall(); 181 updateConnectionProperties(); 182 break; 183 184 case MSG_SET_VIDEO_PROVIDER: 185 VideoProvider videoProvider = (VideoProvider) msg.obj; 186 setVideoProvider(videoProvider); 187 break; 188 189 case MSG_SET_AUDIO_QUALITY: 190 int audioQuality = (int) msg.obj; 191 setAudioQuality(audioQuality); 192 break; 193 194 case MSG_SET_CONFERENCE_PARTICIPANTS: 195 List<ConferenceParticipant> participants = (List<ConferenceParticipant>) msg.obj; 196 updateConferenceParticipants(participants); 197 break; 198 199 case MSG_CONNECTION_EXTRAS_CHANGED: 200 final Bundle extras = (Bundle) msg.obj; 201 updateExtras(extras); 202 break; 203 204 case MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES: 205 setOriginalConnectionCapabilities(msg.arg1); 206 break; 207 208 case MSG_ON_HOLD_TONE: 209 AsyncResult asyncResult = (AsyncResult) msg.obj; 210 Pair<com.android.internal.telephony.Connection, Boolean> heldInfo = 211 (Pair<com.android.internal.telephony.Connection, Boolean>) 212 asyncResult.result; 213 214 // Determines if the hold tone is starting or stopping. 215 boolean playTone = ((Boolean) (heldInfo.second)).booleanValue(); 216 217 // Determine which connection the hold tone is stopping or starting for 218 com.android.internal.telephony.Connection heldConnection = heldInfo.first; 219 220 // Only start or stop the hold tone if this is the connection which is starting 221 // or stopping the hold tone. 222 if (heldConnection == mOriginalConnection) { 223 // If starting the hold tone, send a connection event to Telecom which will 224 // cause it to play the on hold tone. 225 if (playTone) { 226 sendConnectionEvent(EVENT_ON_HOLD_TONE_START, null); 227 } else { 228 sendConnectionEvent(EVENT_ON_HOLD_TONE_END, null); 229 } 230 } 231 break; 232 233 case MSG_CDMA_VOICE_PRIVACY_ON: 234 Log.d(this, "MSG_CDMA_VOICE_PRIVACY_ON received"); 235 setCdmaVoicePrivacy(true); 236 break; 237 case MSG_CDMA_VOICE_PRIVACY_OFF: 238 Log.d(this, "MSG_CDMA_VOICE_PRIVACY_OFF received"); 239 setCdmaVoicePrivacy(false); 240 break; 241 case MSG_HANGUP: 242 int cause = (int) msg.obj; 243 hangup(cause); 244 break; 245 246 case MSG_SET_CALL_RADIO_TECH: 247 int vrat = (int) msg.obj; 248 // Check whether Wi-Fi call tech is changed, it means call radio tech is: 249 // a) changed from IWLAN to other value, or 250 // b) changed from other value to IWLAN. 251 // 252 // In other word, below conditions are all met: 253 // 1) {@link #getCallRadioTech} is different from new vrat 254 // 2) Current call radio technology indicates Wi-Fi call, i.e. {@link #isWifi} 255 // is true, or new vrat indicates Wi-Fi call. 256 boolean isWifiTechChange = getCallRadioTech() != vrat 257 && (isWifi() || vrat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN); 258 259 // Step 1) Updates call radio tech firstly, so that afterwards Wi-Fi related 260 // update actions are taken correctly. 261 setCallRadioTech(vrat); 262 263 // Step 2) Handles Wi-Fi call tech change. 264 if (isWifiTechChange) { 265 updateConnectionProperties(); 266 updateStatusHints(); 267 refreshDisableAddCall(); 268 } 269 break; 270 } 271 } 272 }; 273 274 /** 275 * Handles {@link SuppServiceNotification}s pertinent to Telephony. 276 * @param ssn the notification. 277 */ handleSuppServiceNotification(SuppServiceNotification ssn)278 private void handleSuppServiceNotification(SuppServiceNotification ssn) { 279 Log.i(this, "handleSuppServiceNotification: type=%d, code=%d", ssn.notificationType, 280 ssn.code); 281 if (ssn.notificationType == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1 282 && ssn.code == SuppServiceNotification.CODE_1_CALL_FORWARDED) { 283 sendConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null); 284 } 285 sendSuppServiceNotificationEvent(ssn.notificationType, ssn.code); 286 } 287 288 /** 289 * Sends a supplementary service notification connection event. 290 * This connection event includes the type and code, as well as a human readable message which 291 * is suitable for display to the user if the UI chooses to do so. 292 * @param type the {@link SuppServiceNotification#type}. 293 * @param code the {@link SuppServiceNotification#code}. 294 */ sendSuppServiceNotificationEvent(int type, int code)295 private void sendSuppServiceNotificationEvent(int type, int code) { 296 Bundle extras = new Bundle(); 297 extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_TYPE, type); 298 extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_CODE, code); 299 extras.putCharSequence(TelephonyManager.EXTRA_NOTIFICATION_MESSAGE, 300 getSuppServiceMessage(type, code)); 301 sendConnectionEvent(TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION, extras); 302 } 303 304 /** 305 * Retrieves a human-readable message for a supplementary service notification. 306 * This message is suitable for display to the user. 307 * @param type the code group. 308 * @param code the code. 309 * @return A {@link CharSequence} containing the message, or {@code null} if none defined. 310 */ getSuppServiceMessage(int type, int code)311 private CharSequence getSuppServiceMessage(int type, int code) { 312 int messageId = -1; 313 if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1) { 314 switch (code) { 315 case SuppServiceNotification.CODE_1_CALL_DEFLECTED: 316 messageId = R.string.supp_service_notification_call_deflected; 317 break; 318 case SuppServiceNotification.CODE_1_CALL_FORWARDED: 319 messageId = R.string.supp_service_notification_call_forwarded; 320 break; 321 case SuppServiceNotification.CODE_1_CALL_IS_WAITING: 322 messageId = R.string.supp_service_notification_call_waiting; 323 break; 324 case SuppServiceNotification.CODE_1_CLIR_SUPPRESSION_REJECTED: 325 messageId = R.string.supp_service_clir_suppression_rejected; 326 break; 327 case SuppServiceNotification.CODE_1_CUG_CALL: 328 messageId = R.string.supp_service_closed_user_group_call; 329 break; 330 case SuppServiceNotification.CODE_1_INCOMING_CALLS_BARRED: 331 messageId = R.string.supp_service_incoming_calls_barred; 332 break; 333 case SuppServiceNotification.CODE_1_OUTGOING_CALLS_BARRED: 334 messageId = R.string.supp_service_outgoing_calls_barred; 335 break; 336 case SuppServiceNotification.CODE_1_SOME_CF_ACTIVE: 337 // Intentional fall through. 338 case SuppServiceNotification.CODE_1_UNCONDITIONAL_CF_ACTIVE: 339 messageId = R.string.supp_service_call_forwarding_active; 340 break; 341 } 342 } else if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_2) { 343 switch (code) { 344 case SuppServiceNotification.CODE_2_ADDITIONAL_CALL_FORWARDED: 345 messageId = R.string.supp_service_additional_call_forwarded; 346 break; 347 case SuppServiceNotification.CODE_2_CALL_CONNECTED_ECT: 348 messageId = R.string.supp_service_additional_ect_connected; 349 break; 350 case SuppServiceNotification.CODE_2_CALL_CONNECTING_ECT: 351 messageId = R.string.supp_service_additional_ect_connecting; 352 break; 353 case SuppServiceNotification.CODE_2_CALL_ON_HOLD: 354 messageId = R.string.supp_service_call_on_hold; 355 break; 356 case SuppServiceNotification.CODE_2_CALL_RETRIEVED: 357 messageId = R.string.supp_service_call_resumed; 358 break; 359 case SuppServiceNotification.CODE_2_CUG_CALL: 360 messageId = R.string.supp_service_closed_user_group_call; 361 break; 362 case SuppServiceNotification.CODE_2_DEFLECTED_CALL: 363 messageId = R.string.supp_service_deflected_call; 364 break; 365 case SuppServiceNotification.CODE_2_FORWARDED_CALL: 366 messageId = R.string.supp_service_forwarded_call; 367 break; 368 case SuppServiceNotification.CODE_2_MULTI_PARTY_CALL: 369 messageId = R.string.supp_service_conference_call; 370 break; 371 case SuppServiceNotification.CODE_2_ON_HOLD_CALL_RELEASED: 372 messageId = R.string.supp_service_held_call_released; 373 break; 374 } 375 } 376 if (messageId != -1 && getPhone() != null && getPhone().getContext() != null) { 377 return getPhone().getContext().getText(messageId); 378 } else { 379 return null; 380 } 381 } 382 383 /** 384 * @return {@code true} if carrier video conferencing is supported, {@code false} otherwise. 385 */ isCarrierVideoConferencingSupported()386 public boolean isCarrierVideoConferencingSupported() { 387 return mIsCarrierVideoConferencingSupported; 388 } 389 390 /** 391 * A listener/callback mechanism that is specific communication from TelephonyConnections 392 * to TelephonyConnectionService (for now). It is more specific that Connection.Listener 393 * because it is only exposed in Telephony. 394 */ 395 public abstract static class TelephonyConnectionListener { onOriginalConnectionConfigured(TelephonyConnection c)396 public void onOriginalConnectionConfigured(TelephonyConnection c) {} onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure)397 public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {} 398 } 399 400 private final PostDialListener mPostDialListener = new PostDialListener() { 401 @Override 402 public void onPostDialWait() { 403 Log.v(TelephonyConnection.this, "onPostDialWait"); 404 if (mOriginalConnection != null) { 405 setPostDialWait(mOriginalConnection.getRemainingPostDialString()); 406 } 407 } 408 409 @Override 410 public void onPostDialChar(char c) { 411 Log.v(TelephonyConnection.this, "onPostDialChar: %s", c); 412 if (mOriginalConnection != null) { 413 setNextPostDialChar(c); 414 } 415 } 416 }; 417 418 /** 419 * Listener for listening to events in the {@link com.android.internal.telephony.Connection}. 420 */ 421 private final com.android.internal.telephony.Connection.Listener mOriginalConnectionListener = 422 new com.android.internal.telephony.Connection.ListenerBase() { 423 @Override 424 public void onVideoStateChanged(int videoState) { 425 mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState).sendToTarget(); 426 } 427 428 /* 429 * The {@link com.android.internal.telephony.Connection} has reported a change in 430 * connection capability. 431 * @param capabilities bit mask containing voice or video or both capabilities. 432 */ 433 @Override 434 public void onConnectionCapabilitiesChanged(int capabilities) { 435 mHandler.obtainMessage(MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES, 436 capabilities, 0).sendToTarget(); 437 } 438 439 /** 440 * The {@link com.android.internal.telephony.Connection} has reported a change in the 441 * video call provider. 442 * 443 * @param videoProvider The video call provider. 444 */ 445 @Override 446 public void onVideoProviderChanged(VideoProvider videoProvider) { 447 mHandler.obtainMessage(MSG_SET_VIDEO_PROVIDER, videoProvider).sendToTarget(); 448 } 449 450 /** 451 * Used by {@link com.android.internal.telephony.Connection} to report a change for 452 * the call radio technology. 453 * 454 * @param vrat the RIL Voice Radio Technology used for current connection. 455 */ 456 @Override 457 public void onCallRadioTechChanged(@ServiceState.RilRadioTechnology int vrat) { 458 mHandler.obtainMessage(MSG_SET_CALL_RADIO_TECH, vrat).sendToTarget(); 459 } 460 461 /** 462 * Used by the {@link com.android.internal.telephony.Connection} to report a change in the 463 * audio quality for the current call. 464 * 465 * @param audioQuality The audio quality. 466 */ 467 @Override 468 public void onAudioQualityChanged(int audioQuality) { 469 mHandler.obtainMessage(MSG_SET_AUDIO_QUALITY, audioQuality).sendToTarget(); 470 } 471 /** 472 * Handles a change in the state of conference participant(s), as reported by the 473 * {@link com.android.internal.telephony.Connection}. 474 * 475 * @param participants The participant(s) which changed. 476 */ 477 @Override 478 public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants) { 479 mHandler.obtainMessage(MSG_SET_CONFERENCE_PARTICIPANTS, participants).sendToTarget(); 480 } 481 482 /* 483 * Handles a change to the multiparty state for this connection. 484 * 485 * @param isMultiParty {@code true} if the call became multiparty, {@code false} 486 * otherwise. 487 */ 488 @Override 489 public void onMultipartyStateChanged(boolean isMultiParty) { 490 handleMultipartyStateChange(isMultiParty); 491 } 492 493 /** 494 * Handles the event that the request to merge calls failed. 495 */ 496 @Override 497 public void onConferenceMergedFailed() { 498 handleConferenceMergeFailed(); 499 } 500 501 @Override 502 public void onExtrasChanged(Bundle extras) { 503 mHandler.obtainMessage(MSG_CONNECTION_EXTRAS_CHANGED, extras).sendToTarget(); 504 } 505 506 /** 507 * Handles the phone exiting ECM mode by updating the connection capabilities. During an 508 * ongoing call, if ECM mode is exited, we will re-enable mute for CDMA calls. 509 */ 510 @Override 511 public void onExitedEcmMode() { 512 handleExitedEcmMode(); 513 } 514 515 /** 516 * Called from {@link ImsPhoneCallTracker} when a request to pull an external call has 517 * failed. 518 * @param externalConnection 519 */ 520 @Override 521 public void onCallPullFailed(com.android.internal.telephony.Connection externalConnection) { 522 if (externalConnection == null) { 523 return; 524 } 525 526 Log.i(this, "onCallPullFailed - pull failed; swapping back to call: %s", 527 externalConnection); 528 529 // Inform the InCallService of the fact that the call pull failed (it may choose to 530 // display a message informing the user of the pull failure). 531 sendConnectionEvent(Connection.EVENT_CALL_PULL_FAILED, null); 532 533 // Swap the ImsPhoneConnection we used to do the pull for the ImsExternalConnection 534 // which originally represented the call. 535 setOriginalConnection(externalConnection); 536 537 // Set our state to active again since we're no longer pulling. 538 setActiveInternal(); 539 } 540 541 /** 542 * Called from {@link ImsPhoneCallTracker} when a handover to WIFI has failed. 543 */ 544 @Override 545 public void onHandoverToWifiFailed() { 546 sendConnectionEvent(TelephonyManager.EVENT_HANDOVER_TO_WIFI_FAILED, null); 547 } 548 549 /** 550 * Informs the {@link android.telecom.ConnectionService} of a connection event raised by the 551 * original connection. 552 * @param event The connection event. 553 * @param extras The extras. 554 */ 555 @Override 556 public void onConnectionEvent(String event, Bundle extras) { 557 sendConnectionEvent(event, extras); 558 } 559 560 @Override 561 public void onRttModifyRequestReceived() { 562 sendRemoteRttRequest(); 563 } 564 565 @Override 566 public void onRttModifyResponseReceived(int status) { 567 updateConnectionProperties(); 568 refreshConferenceSupported(); 569 if (status == RttModifyStatus.SESSION_MODIFY_REQUEST_SUCCESS) { 570 sendRttInitiationSuccess(); 571 } else { 572 sendRttInitiationFailure(status); 573 } 574 } 575 576 @Override 577 public void onDisconnect(int cause) { 578 Log.i(this, "onDisconnect: callId=%s, cause=%s", getTelecomCallId(), 579 DisconnectCause.toString(cause)); 580 mHandler.obtainMessage(MSG_DISCONNECT).sendToTarget(); 581 } 582 583 @Override 584 public void onRttInitiated() { 585 if (mOriginalConnection != null) { 586 // if mOriginalConnection is null, the properties will get set when 587 // mOriginalConnection gets set. 588 updateConnectionProperties(); 589 refreshConferenceSupported(); 590 } 591 sendRttInitiationSuccess(); 592 } 593 594 @Override 595 public void onRttTerminated() { 596 updateConnectionProperties(); 597 sendRttSessionRemotelyTerminated(); 598 } 599 600 @Override 601 public void onOriginalConnectionReplaced( 602 com.android.internal.telephony.Connection newConnection) { 603 setOriginalConnection(newConnection); 604 } 605 606 @Override 607 public void onIsNetworkEmergencyCallChanged(boolean isEmergencyCall) { 608 setIsNetworkIdentifiedEmergencyCall(isEmergencyCall); 609 } 610 }; 611 612 protected com.android.internal.telephony.Connection mOriginalConnection; 613 private Call.State mConnectionState = Call.State.IDLE; 614 private Bundle mOriginalConnectionExtras = new Bundle(); 615 private boolean mIsStateOverridden = false; 616 private Call.State mOriginalConnectionState = Call.State.IDLE; 617 private Call.State mConnectionOverriddenState = Call.State.IDLE; 618 private RttTextStream mRttTextStream = null; 619 620 private boolean mWasImsConnection; 621 622 /** 623 * Tracks the multiparty state of the ImsCall so that changes in the bit state can be detected. 624 */ 625 private boolean mIsMultiParty = false; 626 627 /** 628 * The {@link com.android.internal.telephony.Connection} capabilities associated with the 629 * current {@link #mOriginalConnection}. 630 */ 631 private int mOriginalConnectionCapabilities; 632 633 /** 634 * Determines the audio quality is high for the {@link TelephonyConnection}. 635 * This is used when {@link TelephonyConnection#updateConnectionProperties}} is called to 636 * indicate whether a call has the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property. 637 */ 638 private boolean mHasHighDefAudio; 639 640 /** 641 * Indicates that the connection should be treated as an emergency call because the 642 * number dialed matches an internal list of emergency numbers. Does not guarantee whether 643 * the network will treat the call as an emergency call. 644 */ 645 private boolean mTreatAsEmergencyCall; 646 647 /** 648 * Indicates whether the network has identified this call as an emergency call. Where 649 * {@link #mTreatAsEmergencyCall} is based on comparing dialed numbers to a list of known 650 * emergency numbers, this property is based on whether the network itself has identified the 651 * call as an emergency call (which can be the case for an incoming call from emergency 652 * services). 653 */ 654 private boolean mIsNetworkIdentifiedEmergencyCall; 655 656 /** 657 * For video calls, indicates whether the outgoing video for the call can be paused using 658 * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState. 659 */ 660 private boolean mIsVideoPauseSupported; 661 662 /** 663 * Indicates whether this connection supports being a part of a conference.. 664 */ 665 private boolean mIsConferenceSupported; 666 667 /** 668 * Indicates whether managing conference call is supported after this connection being 669 * a part of a IMS conference. 670 */ 671 private boolean mIsManageImsConferenceCallSupported; 672 673 /** 674 * Indicates whether the carrier supports video conferencing; captures the current state of the 675 * carrier config 676 * {@link android.telephony.CarrierConfigManager#KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL}. 677 */ 678 private boolean mIsCarrierVideoConferencingSupported; 679 680 /** 681 * Indicates whether or not this connection has CDMA Enhanced Voice Privacy enabled. 682 */ 683 private boolean mIsCdmaVoicePrivacyEnabled; 684 685 /** 686 * Indicates whether this call is an outgoing call. 687 */ 688 protected final boolean mIsOutgoing; 689 690 /** 691 * Indicates whether the connection can be held. This filed combined with the state of the 692 * connection can determine whether {@link Connection#CAPABILITY_HOLD} should be added to the 693 * connection. 694 */ 695 private boolean mIsHoldable; 696 697 /** 698 * Indicates whether TTY is enabled; used to determine whether a call is VT capable. 699 */ 700 private boolean mIsTtyEnabled; 701 702 /** 703 * Indicates whether this call is using assisted dialing. 704 */ 705 private boolean mIsUsingAssistedDialing; 706 707 /** 708 * Indicates whether this connection supports showing preciese call failed cause. 709 */ 710 private boolean mShowPreciseFailedCause; 711 712 /** 713 * Listeners to our TelephonyConnection specific callbacks 714 */ 715 private final Set<TelephonyConnectionListener> mTelephonyListeners = Collections.newSetFromMap( 716 new ConcurrentHashMap<TelephonyConnectionListener, Boolean>(8, 0.9f, 1)); 717 TelephonyConnection(com.android.internal.telephony.Connection originalConnection, String callId, boolean isOutgoingCall)718 protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection, 719 String callId, boolean isOutgoingCall) { 720 mIsOutgoing = isOutgoingCall; 721 setTelecomCallId(callId); 722 if (originalConnection != null) { 723 setOriginalConnection(originalConnection); 724 } 725 } 726 727 /** 728 * Creates a clone of the current {@link TelephonyConnection}. 729 * 730 * @return The clone. 731 */ cloneConnection()732 public abstract TelephonyConnection cloneConnection(); 733 734 @Override onCallAudioStateChanged(CallAudioState audioState)735 public void onCallAudioStateChanged(CallAudioState audioState) { 736 // TODO: update TTY mode. 737 if (getPhone() != null) { 738 getPhone().setEchoSuppressionEnabled(); 739 } 740 } 741 742 @Override onStateChanged(int state)743 public void onStateChanged(int state) { 744 Log.v(this, "onStateChanged, state: " + Connection.stateToString(state)); 745 updateStatusHints(); 746 } 747 748 @Override onDisconnect()749 public void onDisconnect() { 750 Log.v(this, "onDisconnect"); 751 mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget(); 752 } 753 754 /** 755 * Notifies this Connection of a request to disconnect a participant of the conference managed 756 * by the connection. 757 * 758 * @param endpoint the {@link Uri} of the participant to disconnect. 759 */ 760 @Override onDisconnectConferenceParticipant(Uri endpoint)761 public void onDisconnectConferenceParticipant(Uri endpoint) { 762 Log.v(this, "onDisconnectConferenceParticipant %s", endpoint); 763 764 if (mOriginalConnection == null) { 765 return; 766 } 767 768 mOriginalConnection.onDisconnectConferenceParticipant(endpoint); 769 } 770 771 @Override onSeparate()772 public void onSeparate() { 773 Log.v(this, "onSeparate"); 774 if (mOriginalConnection != null) { 775 try { 776 mOriginalConnection.separate(); 777 } catch (CallStateException e) { 778 Log.e(this, e, "Call to Connection.separate failed with exception"); 779 } 780 } 781 } 782 783 @Override onAbort()784 public void onAbort() { 785 Log.v(this, "onAbort"); 786 mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget(); 787 } 788 789 @Override onHold()790 public void onHold() { 791 performHold(); 792 } 793 794 @Override onUnhold()795 public void onUnhold() { 796 performUnhold(); 797 } 798 799 @Override onAnswer(int videoState)800 public void onAnswer(int videoState) { 801 Log.v(this, "onAnswer"); 802 if (isValidRingingCall() && getPhone() != null) { 803 try { 804 getPhone().acceptCall(videoState); 805 } catch (CallStateException e) { 806 Log.e(this, e, "Failed to accept call."); 807 } 808 } 809 } 810 811 @Override onDeflect(Uri address)812 public void onDeflect(Uri address) { 813 Log.v(this, "onDeflect"); 814 if (mOriginalConnection != null && isValidRingingCall()) { 815 if (address == null) { 816 Log.w(this, "call deflect address uri is null"); 817 return; 818 } 819 String scheme = address.getScheme(); 820 String deflectNumber = ""; 821 String uriString = address.getSchemeSpecificPart(); 822 if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) { 823 if (!PhoneAccount.SCHEME_TEL.equals(scheme)) { 824 Log.w(this, "onDeflect, address scheme is not of type tel instead: " + 825 scheme); 826 return; 827 } 828 if (PhoneNumberUtils.isUriNumber(uriString)) { 829 Log.w(this, "Invalid deflect address. Not a legal PSTN number."); 830 return; 831 } 832 deflectNumber = PhoneNumberUtils.convertAndStrip(uriString); 833 if (TextUtils.isEmpty(deflectNumber)) { 834 Log.w(this, "Empty deflect number obtained from address uri"); 835 return; 836 } 837 } else { 838 Log.w(this, "Cannot deflect to voicemail uri"); 839 return; 840 } 841 842 try { 843 mOriginalConnection.deflect(deflectNumber); 844 } catch (CallStateException e) { 845 Log.e(this, e, "Failed to deflect call."); 846 } 847 } 848 } 849 850 @Override onReject()851 public void onReject() { 852 Log.v(this, "onReject"); 853 if (isValidRingingCall()) { 854 mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.INCOMING_REJECTED) 855 .sendToTarget(); 856 } 857 super.onReject(); 858 } 859 860 @Override onPostDialContinue(boolean proceed)861 public void onPostDialContinue(boolean proceed) { 862 Log.v(this, "onPostDialContinue, proceed: " + proceed); 863 if (mOriginalConnection != null) { 864 if (proceed) { 865 mOriginalConnection.proceedAfterWaitChar(); 866 } else { 867 mOriginalConnection.cancelPostDial(); 868 } 869 } 870 } 871 872 /** 873 * Handles requests to pull an external call. 874 */ 875 @Override onPullExternalCall()876 public void onPullExternalCall() { 877 if ((getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) != 878 Connection.PROPERTY_IS_EXTERNAL_CALL) { 879 Log.w(this, "onPullExternalCall - cannot pull non-external call"); 880 return; 881 } 882 883 if (mOriginalConnection != null) { 884 mOriginalConnection.pullExternalCall(); 885 } 886 } 887 888 @Override onStartRtt(RttTextStream textStream)889 public void onStartRtt(RttTextStream textStream) { 890 if (isImsConnection()) { 891 ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection; 892 if (originalConnection.isRttEnabledForCall()) { 893 originalConnection.setCurrentRttTextStream(textStream); 894 } else { 895 originalConnection.startRtt(textStream); 896 } 897 } else { 898 Log.w(this, "onStartRtt - not in IMS, so RTT cannot be enabled."); 899 } 900 } 901 902 @Override onStopRtt()903 public void onStopRtt() { 904 if (isImsConnection()) { 905 ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection; 906 if (originalConnection.isRttEnabledForCall()) { 907 originalConnection.stopRtt(); 908 } else { 909 Log.w(this, "onStopRtt - not in RTT call, ignoring"); 910 } 911 } else { 912 Log.w(this, "onStopRtt - not in IMS, ignoring"); 913 } 914 } 915 916 @Override handleRttUpgradeResponse(RttTextStream textStream)917 public void handleRttUpgradeResponse(RttTextStream textStream) { 918 if (!isImsConnection()) { 919 Log.w(this, "handleRttUpgradeResponse - not in IMS, so RTT cannot be enabled."); 920 return; 921 } 922 ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection; 923 originalConnection.sendRttModifyResponse(textStream); 924 } 925 performHold()926 public void performHold() { 927 Log.v(this, "performHold"); 928 // TODO: Can dialing calls be put on hold as well since they take up the 929 // foreground call slot? 930 if (Call.State.ACTIVE == mConnectionState) { 931 Log.v(this, "Holding active call"); 932 try { 933 Phone phone = mOriginalConnection.getCall().getPhone(); 934 935 Call ringingCall = phone.getRingingCall(); 936 937 // Although the method says switchHoldingAndActive, it eventually calls a RIL method 938 // called switchWaitingOrHoldingAndActive. What this means is that if we try to put 939 // a call on hold while a call-waiting call exists, it'll end up accepting the 940 // call-waiting call, which is bad if that was not the user's intention. We are 941 // cheating here and simply skipping it because we know any attempt to hold a call 942 // while a call-waiting call is happening is likely a request from Telecom prior to 943 // accepting the call-waiting call. 944 // TODO: Investigate a better solution. It would be great here if we 945 // could "fake" hold by silencing the audio and microphone streams for this call 946 // instead of actually putting it on hold. 947 if (ringingCall.getState() != Call.State.WAITING) { 948 // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic. 949 if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) { 950 ImsPhone imsPhone = (ImsPhone) phone; 951 imsPhone.holdActiveCall(); 952 return; 953 } 954 phone.switchHoldingAndActive(); 955 } 956 957 // TODO: Cdma calls are slightly different. 958 } catch (CallStateException e) { 959 Log.e(this, e, "Exception occurred while trying to put call on hold."); 960 } 961 } else { 962 Log.w(this, "Cannot put a call that is not currently active on hold."); 963 } 964 } 965 performUnhold()966 public void performUnhold() { 967 Log.v(this, "performUnhold"); 968 if (Call.State.HOLDING == mConnectionState) { 969 try { 970 Phone phone = mOriginalConnection.getCall().getPhone(); 971 // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic. 972 if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) { 973 ImsPhone imsPhone = (ImsPhone) phone; 974 imsPhone.unholdHeldCall(); 975 return; 976 } 977 // Here's the deal--Telephony hold/unhold is weird because whenever there exists 978 // more than one call, one of them must always be active. In other words, if you 979 // have an active call and holding call, and you put the active call on hold, it 980 // will automatically activate the holding call. This is weird with how Telecom 981 // sends its commands. When a user opts to "unhold" a background call, telecom 982 // issues hold commands to all active calls, and then the unhold command to the 983 // background call. This means that we get two commands...each of which reduces to 984 // switchHoldingAndActive(). The result is that they simply cancel each other out. 985 // To fix this so that it works well with telecom we add a minor hack. If we 986 // have one telephony call, everything works as normally expected. But if we have 987 // two or more calls, we will ignore all requests to "unhold" knowing that the hold 988 // requests already do what we want. If you've read up to this point, I'm very sorry 989 // that we are doing this. I didn't think of a better solution that wouldn't also 990 // make the Telecom APIs very ugly. 991 992 if (!hasMultipleTopLevelCalls()) { 993 mOriginalConnection.getCall().getPhone().switchHoldingAndActive(); 994 } else { 995 Log.i(this, "Skipping unhold command for %s", this); 996 } 997 } catch (CallStateException e) { 998 Log.e(this, e, "Exception occurred while trying to release call from hold."); 999 } 1000 } else { 1001 Log.w(this, "Cannot release a call that is not already on hold from hold."); 1002 } 1003 } 1004 performConference(Connection otherConnection)1005 public void performConference(Connection otherConnection) { 1006 Log.d(this, "performConference - %s", this); 1007 if (getPhone() != null) { 1008 try { 1009 // We dont use the "other" connection because there is no concept of that in the 1010 // implementation of calls inside telephony. Basically, you can "conference" and it 1011 // will conference with the background call. We know that otherConnection is the 1012 // background call because it would never have called setConferenceableConnections() 1013 // otherwise. 1014 getPhone().conference(); 1015 } catch (CallStateException e) { 1016 Log.e(this, e, "Failed to conference call."); 1017 } 1018 } 1019 } 1020 1021 /** 1022 * Builds connection capabilities common to all TelephonyConnections. Namely, apply IMS-based 1023 * capabilities. 1024 */ buildConnectionCapabilities()1025 protected int buildConnectionCapabilities() { 1026 int callCapabilities = 0; 1027 if (mOriginalConnection != null && mOriginalConnection.isIncoming()) { 1028 callCapabilities |= CAPABILITY_SPEED_UP_MT_AUDIO; 1029 } 1030 if (!shouldTreatAsEmergencyCall() && isImsConnection() && canHoldImsCalls()) { 1031 callCapabilities |= CAPABILITY_SUPPORT_HOLD; 1032 if (mIsHoldable && (getState() == STATE_ACTIVE || getState() == STATE_HOLDING)) { 1033 callCapabilities |= CAPABILITY_HOLD; 1034 } 1035 } 1036 1037 Log.d(this, "buildConnectionCapabilities: isHoldable = " 1038 + mIsHoldable + " State = " + getState() + " capabilities = " + callCapabilities); 1039 1040 return callCapabilities; 1041 } 1042 updateConnectionCapabilities()1043 protected final void updateConnectionCapabilities() { 1044 int newCapabilities = buildConnectionCapabilities(); 1045 1046 newCapabilities = applyOriginalConnectionCapabilities(newCapabilities); 1047 newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PAUSE_VIDEO, 1048 mIsVideoPauseSupported && isVideoCapable()); 1049 newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PULL_CALL, 1050 isExternalConnection() && isPullable()); 1051 newCapabilities = applyConferenceTerminationCapabilities(newCapabilities); 1052 newCapabilities = changeBitmask(newCapabilities, CAPABILITY_SUPPORT_DEFLECT, 1053 isImsConnection() && canDeflectImsCalls()); 1054 1055 if (getConnectionCapabilities() != newCapabilities) { 1056 setConnectionCapabilities(newCapabilities); 1057 } 1058 } 1059 buildConnectionProperties()1060 protected int buildConnectionProperties() { 1061 int connectionProperties = 0; 1062 1063 // If the phone is in ECM mode, mark the call to indicate that the callback number should be 1064 // shown. 1065 Phone phone = getPhone(); 1066 if (phone != null && phone.isInEcm()) { 1067 connectionProperties |= PROPERTY_EMERGENCY_CALLBACK_MODE; 1068 } 1069 1070 return connectionProperties; 1071 } 1072 1073 /** 1074 * Updates the properties of the connection. 1075 */ updateConnectionProperties()1076 protected final void updateConnectionProperties() { 1077 int newProperties = buildConnectionProperties(); 1078 1079 newProperties = changeBitmask(newProperties, PROPERTY_HIGH_DEF_AUDIO, 1080 hasHighDefAudioProperty()); 1081 newProperties = changeBitmask(newProperties, PROPERTY_WIFI, isWifi()); 1082 newProperties = changeBitmask(newProperties, PROPERTY_IS_EXTERNAL_CALL, 1083 isExternalConnection()); 1084 newProperties = changeBitmask(newProperties, PROPERTY_HAS_CDMA_VOICE_PRIVACY, 1085 mIsCdmaVoicePrivacyEnabled); 1086 newProperties = changeBitmask(newProperties, PROPERTY_ASSISTED_DIALING_USED, 1087 mIsUsingAssistedDialing); 1088 newProperties = changeBitmask(newProperties, PROPERTY_IS_RTT, isRtt()); 1089 newProperties = changeBitmask(newProperties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL, 1090 isNetworkIdentifiedEmergencyCall()); 1091 1092 if (getConnectionProperties() != newProperties) { 1093 setConnectionProperties(newProperties); 1094 } 1095 } 1096 updateAddress()1097 protected final void updateAddress() { 1098 updateConnectionCapabilities(); 1099 updateConnectionProperties(); 1100 if (mOriginalConnection != null) { 1101 Uri address; 1102 if (isShowingOriginalDialString() 1103 && mOriginalConnection.getOrigDialString() != null) { 1104 address = getAddressFromNumber(mOriginalConnection.getOrigDialString()); 1105 } else { 1106 address = getAddressFromNumber(mOriginalConnection.getAddress()); 1107 } 1108 int presentation = mOriginalConnection.getNumberPresentation(); 1109 if (!Objects.equals(address, getAddress()) || 1110 presentation != getAddressPresentation()) { 1111 Log.v(this, "updateAddress, address changed"); 1112 if ((getConnectionProperties() & PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0) { 1113 address = null; 1114 } 1115 setAddress(address, presentation); 1116 } 1117 1118 String name = filterCnapName(mOriginalConnection.getCnapName()); 1119 int namePresentation = mOriginalConnection.getCnapNamePresentation(); 1120 if (!Objects.equals(name, getCallerDisplayName()) || 1121 namePresentation != getCallerDisplayNamePresentation()) { 1122 Log.v(this, "updateAddress, caller display name changed"); 1123 setCallerDisplayName(name, namePresentation); 1124 } 1125 1126 if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) { 1127 mTreatAsEmergencyCall = true; 1128 } 1129 1130 // Changing the address of the connection can change whether it is an emergency call or 1131 // not, which can impact whether it can be part of a conference. 1132 refreshConferenceSupported(); 1133 } 1134 } 1135 onRemovedFromCallService()1136 void onRemovedFromCallService() { 1137 // Subclass can override this to do cleanup. 1138 } 1139 setOriginalConnection(com.android.internal.telephony.Connection originalConnection)1140 void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) { 1141 Log.v(this, "new TelephonyConnection, originalConnection: " + originalConnection); 1142 if (mOriginalConnection != null && originalConnection != null 1143 && !originalConnection.isIncoming() 1144 && originalConnection.getOrigDialString() == null 1145 && isShowingOriginalDialString()) { 1146 Log.i(this, "new original dial string is null, convert to: " 1147 + mOriginalConnection.getOrigDialString()); 1148 originalConnection.setConverted(mOriginalConnection.getOrigDialString()); 1149 } 1150 1151 clearOriginalConnection(); 1152 mOriginalConnectionExtras.clear(); 1153 mOriginalConnection = originalConnection; 1154 mOriginalConnection.setTelecomCallId(getTelecomCallId()); 1155 getPhone().registerForPreciseCallStateChanged( 1156 mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null); 1157 getPhone().registerForHandoverStateChanged( 1158 mHandler, MSG_HANDOVER_STATE_CHANGED, null); 1159 getPhone().registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null); 1160 getPhone().registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null); 1161 getPhone().registerForOnHoldTone(mHandler, MSG_ON_HOLD_TONE, null); 1162 getPhone().registerForInCallVoicePrivacyOn(mHandler, MSG_CDMA_VOICE_PRIVACY_ON, null); 1163 getPhone().registerForInCallVoicePrivacyOff(mHandler, MSG_CDMA_VOICE_PRIVACY_OFF, null); 1164 mOriginalConnection.addPostDialListener(mPostDialListener); 1165 mOriginalConnection.addListener(mOriginalConnectionListener); 1166 1167 // Set video state and capabilities 1168 setVideoState(mOriginalConnection.getVideoState()); 1169 setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities()); 1170 setIsNetworkIdentifiedEmergencyCall(mOriginalConnection.isNetworkIdentifiedEmergencyCall()); 1171 setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip()); 1172 setVideoProvider(mOriginalConnection.getVideoProvider()); 1173 setAudioQuality(mOriginalConnection.getAudioQuality()); 1174 setTechnologyTypeExtra(); 1175 1176 setCallRadioTech(mOriginalConnection.getCallRadioTech()); 1177 1178 // Post update of extras to the handler; extras are updated via the handler to ensure thread 1179 // safety. The Extras Bundle is cloned in case the original extras are modified while they 1180 // are being added to mOriginalConnectionExtras in updateExtras. 1181 Bundle connExtras = mOriginalConnection.getConnectionExtras(); 1182 mHandler.obtainMessage(MSG_CONNECTION_EXTRAS_CHANGED, connExtras == null ? null : 1183 new Bundle(connExtras)).sendToTarget(); 1184 1185 if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) { 1186 mTreatAsEmergencyCall = true; 1187 } 1188 1189 if (isImsConnection()) { 1190 mWasImsConnection = true; 1191 } 1192 mIsMultiParty = mOriginalConnection.isMultiparty(); 1193 1194 Bundle extrasToPut = new Bundle(); 1195 List<String> extrasToRemove = new ArrayList<>(); 1196 if (mOriginalConnection.isActiveCallDisconnectedOnAnswer()) { 1197 extrasToPut.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true); 1198 } else { 1199 extrasToRemove.add(Connection.EXTRA_ANSWERING_DROPS_FG_CALL); 1200 } 1201 1202 if (shouldSetDisableAddCallExtra()) { 1203 extrasToPut.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true); 1204 } else { 1205 extrasToRemove.add(Connection.EXTRA_DISABLE_ADD_CALL); 1206 } 1207 putExtras(extrasToPut); 1208 removeExtras(extrasToRemove); 1209 1210 // updateState can set mOriginalConnection to null if its state is DISCONNECTED, so this 1211 // should be executed *after* the above setters have run. 1212 updateState(); 1213 if (mOriginalConnection == null) { 1214 Log.w(this, "original Connection was nulled out as part of setOriginalConnection. " + 1215 originalConnection); 1216 } 1217 1218 fireOnOriginalConnectionConfigured(); 1219 } 1220 1221 /** 1222 * Filters the CNAP name to not include a list of names that are unhelpful to the user for 1223 * Caller ID purposes. 1224 */ filterCnapName(final String cnapName)1225 private String filterCnapName(final String cnapName) { 1226 if (cnapName == null) { 1227 return null; 1228 } 1229 PersistableBundle carrierConfig = getCarrierConfig(); 1230 String[] filteredCnapNames = null; 1231 if (carrierConfig != null) { 1232 filteredCnapNames = carrierConfig.getStringArray( 1233 CarrierConfigManager.KEY_FILTERED_CNAP_NAMES_STRING_ARRAY); 1234 } 1235 if (filteredCnapNames != null) { 1236 long cnapNameMatches = Arrays.asList(filteredCnapNames) 1237 .stream() 1238 .filter(filteredCnapName -> filteredCnapName.equals(cnapName.toUpperCase())) 1239 .count(); 1240 if (cnapNameMatches > 0) { 1241 Log.i(this, "filterCnapName: Filtered CNAP Name: " + cnapName); 1242 return ""; 1243 } 1244 } 1245 return cnapName; 1246 } 1247 1248 /** 1249 * Sets the EXTRA_CALL_TECHNOLOGY_TYPE extra on the connection to report back to Telecom. 1250 */ setTechnologyTypeExtra()1251 private void setTechnologyTypeExtra() { 1252 if (getPhone() != null) { 1253 putExtra(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE, getPhone().getPhoneType()); 1254 } 1255 } 1256 refreshDisableAddCall()1257 private void refreshDisableAddCall() { 1258 if (shouldSetDisableAddCallExtra()) { 1259 putExtra(Connection.EXTRA_DISABLE_ADD_CALL, true); 1260 } else { 1261 removeExtras(Connection.EXTRA_DISABLE_ADD_CALL); 1262 } 1263 } 1264 shouldSetDisableAddCallExtra()1265 private boolean shouldSetDisableAddCallExtra() { 1266 if (mOriginalConnection == null) { 1267 return false; 1268 } 1269 boolean carrierShouldAllowAddCall = mOriginalConnection.shouldAllowAddCallDuringVideoCall(); 1270 if (carrierShouldAllowAddCall) { 1271 return false; 1272 } 1273 Phone phone = getPhone(); 1274 if (phone == null) { 1275 return false; 1276 } 1277 boolean isCurrentVideoCall = false; 1278 boolean wasVideoCall = false; 1279 boolean isVowifiEnabled = false; 1280 if (phone instanceof ImsPhone) { 1281 ImsPhone imsPhone = (ImsPhone) phone; 1282 if (imsPhone.getForegroundCall() != null 1283 && imsPhone.getForegroundCall().getImsCall() != null) { 1284 ImsCall call = imsPhone.getForegroundCall().getImsCall(); 1285 isCurrentVideoCall = call.isVideoCall(); 1286 wasVideoCall = call.wasVideoCall(); 1287 } 1288 1289 isVowifiEnabled = ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId()); 1290 } 1291 1292 if (isCurrentVideoCall) { 1293 return true; 1294 } else if (wasVideoCall && isWifi() && !isVowifiEnabled) { 1295 return true; 1296 } 1297 return false; 1298 } 1299 hasHighDefAudioProperty()1300 private boolean hasHighDefAudioProperty() { 1301 if (!mHasHighDefAudio) { 1302 return false; 1303 } 1304 1305 boolean isVideoCall = VideoProfile.isVideo(getVideoState()); 1306 1307 PersistableBundle b = getCarrierConfig(); 1308 boolean canWifiCallsBeHdAudio = 1309 b != null && b.getBoolean(CarrierConfigManager.KEY_WIFI_CALLS_CAN_BE_HD_AUDIO); 1310 boolean canVideoCallsBeHdAudio = 1311 b != null && b.getBoolean(CarrierConfigManager.KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO); 1312 boolean canGsmCdmaCallsBeHdAudio = 1313 b != null && b.getBoolean(CarrierConfigManager.KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO); 1314 boolean shouldDisplayHdAudio = 1315 b != null && b.getBoolean(CarrierConfigManager.KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL); 1316 1317 if (!shouldDisplayHdAudio) { 1318 return false; 1319 } 1320 1321 if (isGsmCdmaConnection() && !canGsmCdmaCallsBeHdAudio) { 1322 return false; 1323 } 1324 1325 if (isVideoCall && !canVideoCallsBeHdAudio) { 1326 return false; 1327 } 1328 1329 if (isWifi() && !canWifiCallsBeHdAudio) { 1330 return false; 1331 } 1332 1333 return true; 1334 } 1335 canHoldImsCalls()1336 private boolean canHoldImsCalls() { 1337 PersistableBundle b = getCarrierConfig(); 1338 // Return true if the CarrierConfig is unavailable 1339 return !doesDeviceRespectHoldCarrierConfig() || b == null || 1340 b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL); 1341 } 1342 getCarrierConfig()1343 private PersistableBundle getCarrierConfig() { 1344 Phone phone = getPhone(); 1345 if (phone == null) { 1346 return null; 1347 } 1348 return PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId()); 1349 } 1350 canDeflectImsCalls()1351 private boolean canDeflectImsCalls() { 1352 PersistableBundle b = getCarrierConfig(); 1353 // Return false if the CarrierConfig is unavailable 1354 if (b != null) { 1355 return b.getBoolean( 1356 CarrierConfigManager.KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL) && 1357 isValidRingingCall(); 1358 } 1359 return false; 1360 } 1361 1362 /** 1363 * Determines if the device will respect the value of the 1364 * {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} configuration option. 1365 * 1366 * @return {@code false} if the device always supports holding IMS calls, {@code true} if it 1367 * will use {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} to determine if 1368 * hold is supported. 1369 */ doesDeviceRespectHoldCarrierConfig()1370 private boolean doesDeviceRespectHoldCarrierConfig() { 1371 Phone phone = getPhone(); 1372 if (phone == null) { 1373 return true; 1374 } 1375 return phone.getContext().getResources().getBoolean( 1376 com.android.internal.R.bool.config_device_respects_hold_carrier_config); 1377 } 1378 1379 /** 1380 * Whether the connection should be treated as an emergency. 1381 * @return {@code true} if the connection should be treated as an emergency call based 1382 * on the number dialed, {@code false} otherwise. 1383 */ shouldTreatAsEmergencyCall()1384 protected boolean shouldTreatAsEmergencyCall() { 1385 return mTreatAsEmergencyCall; 1386 } 1387 1388 /** 1389 * Un-sets the underlying radio connection. 1390 */ clearOriginalConnection()1391 void clearOriginalConnection() { 1392 if (mOriginalConnection != null) { 1393 if (getPhone() != null) { 1394 getPhone().unregisterForPreciseCallStateChanged(mHandler); 1395 getPhone().unregisterForRingbackTone(mHandler); 1396 getPhone().unregisterForHandoverStateChanged(mHandler); 1397 getPhone().unregisterForDisconnect(mHandler); 1398 getPhone().unregisterForSuppServiceNotification(mHandler); 1399 getPhone().unregisterForOnHoldTone(mHandler); 1400 getPhone().unregisterForInCallVoicePrivacyOn(mHandler); 1401 getPhone().unregisterForInCallVoicePrivacyOff(mHandler); 1402 } 1403 mOriginalConnection.removePostDialListener(mPostDialListener); 1404 mOriginalConnection.removeListener(mOriginalConnectionListener); 1405 mOriginalConnection = null; 1406 } 1407 } 1408 hangup(int telephonyDisconnectCode)1409 protected void hangup(int telephonyDisconnectCode) { 1410 if (mOriginalConnection != null) { 1411 try { 1412 // Hanging up a ringing call requires that we invoke call.hangup() as opposed to 1413 // connection.hangup(). Without this change, the party originating the call 1414 // will not get sent to voicemail if the user opts to reject the call. 1415 if (isValidRingingCall()) { 1416 Call call = getCall(); 1417 if (call != null) { 1418 call.hangup(); 1419 } else { 1420 Log.w(this, "Attempting to hangup a connection without backing call."); 1421 } 1422 } else { 1423 // We still prefer to call connection.hangup() for non-ringing calls 1424 // in order to support hanging-up specific calls within a conference call. 1425 // If we invoked call.hangup() while in a conference, we would end up 1426 // hanging up the entire conference call instead of the specific connection. 1427 mOriginalConnection.hangup(); 1428 } 1429 } catch (CallStateException e) { 1430 Log.e(this, e, "Call to Connection.hangup failed with exception"); 1431 } 1432 } else { 1433 if (getState() == STATE_DISCONNECTED) { 1434 Log.i(this, "hangup called on an already disconnected call!"); 1435 close(); 1436 } else { 1437 // There are a few cases where mOriginalConnection has not been set yet. For 1438 // example, when the radio has to be turned on to make an emergency call, 1439 // mOriginalConnection could not be set for many seconds. 1440 setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause( 1441 android.telephony.DisconnectCause.LOCAL, 1442 "Local Disconnect before connection established.")); 1443 close(); 1444 } 1445 } 1446 } 1447 getOriginalConnection()1448 com.android.internal.telephony.Connection getOriginalConnection() { 1449 return mOriginalConnection; 1450 } 1451 getCall()1452 protected Call getCall() { 1453 if (mOriginalConnection != null) { 1454 return mOriginalConnection.getCall(); 1455 } 1456 return null; 1457 } 1458 getPhone()1459 Phone getPhone() { 1460 Call call = getCall(); 1461 if (call != null) { 1462 return call.getPhone(); 1463 } 1464 return null; 1465 } 1466 hasMultipleTopLevelCalls()1467 private boolean hasMultipleTopLevelCalls() { 1468 int numCalls = 0; 1469 Phone phone = getPhone(); 1470 if (phone != null) { 1471 if (!phone.getRingingCall().isIdle()) { 1472 numCalls++; 1473 } 1474 if (!phone.getForegroundCall().isIdle()) { 1475 numCalls++; 1476 } 1477 if (!phone.getBackgroundCall().isIdle()) { 1478 numCalls++; 1479 } 1480 } 1481 return numCalls > 1; 1482 } 1483 getForegroundConnection()1484 private com.android.internal.telephony.Connection getForegroundConnection() { 1485 if (getPhone() != null) { 1486 return getPhone().getForegroundCall().getEarliestConnection(); 1487 } 1488 return null; 1489 } 1490 1491 /** 1492 * Checks for and returns the list of conference participants 1493 * associated with this connection. 1494 */ getConferenceParticipants()1495 public List<ConferenceParticipant> getConferenceParticipants() { 1496 if (mOriginalConnection == null) { 1497 Log.v(this, "Null mOriginalConnection, cannot get conf participants."); 1498 return null; 1499 } 1500 return mOriginalConnection.getConferenceParticipants(); 1501 } 1502 1503 /** 1504 * Checks to see the original connection corresponds to an active incoming call. Returns false 1505 * if there is no such actual call, or if the associated call is not incoming (See 1506 * {@link Call.State#isRinging}). 1507 */ isValidRingingCall()1508 private boolean isValidRingingCall() { 1509 if (getPhone() == null) { 1510 Log.v(this, "isValidRingingCall, phone is null"); 1511 return false; 1512 } 1513 1514 Call ringingCall = getPhone().getRingingCall(); 1515 if (!ringingCall.getState().isRinging()) { 1516 Log.v(this, "isValidRingingCall, ringing call is not in ringing state"); 1517 return false; 1518 } 1519 1520 if (ringingCall.getEarliestConnection() != mOriginalConnection) { 1521 Log.v(this, "isValidRingingCall, ringing call connection does not match"); 1522 return false; 1523 } 1524 1525 Log.v(this, "isValidRingingCall, returning true"); 1526 return true; 1527 } 1528 1529 // Make sure the extras being passed into this method is a COPY of the original extras Bundle. 1530 // We do not want the extras to be cleared or modified during mOriginalConnectionExtras.putAll 1531 // below. updateExtras(Bundle extras)1532 protected void updateExtras(Bundle extras) { 1533 if (mOriginalConnection != null) { 1534 if (extras != null) { 1535 // Check if extras have changed and need updating. 1536 if (!areBundlesEqual(mOriginalConnectionExtras, extras)) { 1537 if (Log.DEBUG) { 1538 Log.d(TelephonyConnection.this, "Updating extras:"); 1539 for (String key : extras.keySet()) { 1540 Object value = extras.get(key); 1541 if (value instanceof String) { 1542 Log.d(this, "updateExtras Key=" + Log.pii(key) + 1543 " value=" + Log.pii((String)value)); 1544 } 1545 } 1546 } 1547 mOriginalConnectionExtras.clear(); 1548 1549 mOriginalConnectionExtras.putAll(extras); 1550 1551 // Remap any string extras that have a remapping defined. 1552 for (String key : mOriginalConnectionExtras.keySet()) { 1553 if (sExtrasMap.containsKey(key)) { 1554 String newKey = sExtrasMap.get(key); 1555 mOriginalConnectionExtras.putString(newKey, extras.getString(key)); 1556 mOriginalConnectionExtras.remove(key); 1557 } 1558 } 1559 1560 // Ensure extras are propagated to Telecom. 1561 putExtras(mOriginalConnectionExtras); 1562 } else { 1563 Log.d(this, "Extras update not required"); 1564 } 1565 } else { 1566 Log.d(this, "updateExtras extras: " + Log.pii(extras)); 1567 } 1568 } 1569 } 1570 areBundlesEqual(Bundle extras, Bundle newExtras)1571 private static boolean areBundlesEqual(Bundle extras, Bundle newExtras) { 1572 if (extras == null || newExtras == null) { 1573 return extras == newExtras; 1574 } 1575 1576 if (extras.size() != newExtras.size()) { 1577 return false; 1578 } 1579 1580 for(String key : extras.keySet()) { 1581 if (key != null) { 1582 final Object value = extras.get(key); 1583 final Object newValue = newExtras.get(key); 1584 if (!Objects.equals(value, newValue)) { 1585 return false; 1586 } 1587 } 1588 } 1589 return true; 1590 } 1591 setStateOverride(Call.State state)1592 void setStateOverride(Call.State state) { 1593 mIsStateOverridden = true; 1594 mConnectionOverriddenState = state; 1595 // Need to keep track of the original connection's state before override. 1596 mOriginalConnectionState = mOriginalConnection.getState(); 1597 updateStateInternal(); 1598 } 1599 resetStateOverride()1600 void resetStateOverride() { 1601 mIsStateOverridden = false; 1602 updateStateInternal(); 1603 } 1604 updateStateInternal()1605 void updateStateInternal() { 1606 if (mOriginalConnection == null) { 1607 return; 1608 } 1609 Call.State newState; 1610 // If the state is overridden and the state of the original connection hasn't changed since, 1611 // then we continue in the overridden state, else we go to the original connection's state. 1612 if (mIsStateOverridden && mOriginalConnectionState == mOriginalConnection.getState()) { 1613 newState = mConnectionOverriddenState; 1614 } else { 1615 newState = mOriginalConnection.getState(); 1616 } 1617 int cause = mOriginalConnection.getDisconnectCause(); 1618 Log.v(this, "Update state from %s to %s for %s", mConnectionState, newState, 1619 getTelecomCallId()); 1620 1621 if (mConnectionState != newState) { 1622 mConnectionState = newState; 1623 switch (newState) { 1624 case IDLE: 1625 break; 1626 case ACTIVE: 1627 setActiveInternal(); 1628 break; 1629 case HOLDING: 1630 setOnHold(); 1631 break; 1632 case DIALING: 1633 case ALERTING: 1634 if (mOriginalConnection != null && mOriginalConnection.isPulledCall()) { 1635 setPulling(); 1636 } else { 1637 setDialing(); 1638 } 1639 break; 1640 case INCOMING: 1641 case WAITING: 1642 setRinging(); 1643 break; 1644 case DISCONNECTED: 1645 if (shouldTreatAsEmergencyCall() 1646 && (cause 1647 == android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE 1648 || cause 1649 == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE)) { 1650 // We can get into a situation where the radio wants us to redial the 1651 // same emergency call on the other available slot. This will not set 1652 // the state to disconnected and will instead tell the 1653 // TelephonyConnectionService to 1654 // create a new originalConnection using the new Slot. 1655 fireOnOriginalConnectionRetryDial(cause 1656 == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE); 1657 } else { 1658 int preciseDisconnectCause = CallFailCause.NOT_VALID; 1659 if (mShowPreciseFailedCause) { 1660 preciseDisconnectCause = 1661 mOriginalConnection.getPreciseDisconnectCause(); 1662 } 1663 setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause( 1664 mOriginalConnection.getDisconnectCause(), 1665 preciseDisconnectCause, 1666 mOriginalConnection.getVendorDisconnectCause(), 1667 getPhone().getPhoneId())); 1668 close(); 1669 } 1670 break; 1671 case DISCONNECTING: 1672 break; 1673 } 1674 } 1675 } 1676 updateState()1677 void updateState() { 1678 if (mOriginalConnection == null) { 1679 return; 1680 } 1681 1682 updateStateInternal(); 1683 updateStatusHints(); 1684 updateConnectionCapabilities(); 1685 updateConnectionProperties(); 1686 updateAddress(); 1687 updateMultiparty(); 1688 refreshDisableAddCall(); 1689 } 1690 1691 /** 1692 * Checks for changes to the multiparty bit. If a conference has started, informs listeners. 1693 */ updateMultiparty()1694 private void updateMultiparty() { 1695 if (mOriginalConnection == null) { 1696 return; 1697 } 1698 1699 if (mIsMultiParty != mOriginalConnection.isMultiparty()) { 1700 mIsMultiParty = mOriginalConnection.isMultiparty(); 1701 1702 if (mIsMultiParty) { 1703 notifyConferenceStarted(); 1704 } 1705 } 1706 } 1707 1708 /** 1709 * Handles a failure when merging calls into a conference. 1710 * {@link com.android.internal.telephony.Connection.Listener#onConferenceMergedFailed()} 1711 * listener. 1712 */ handleConferenceMergeFailed()1713 private void handleConferenceMergeFailed(){ 1714 mHandler.obtainMessage(MSG_CONFERENCE_MERGE_FAILED).sendToTarget(); 1715 } 1716 1717 /** 1718 * Handles requests to update the multiparty state received via the 1719 * {@link com.android.internal.telephony.Connection.Listener#onMultipartyStateChanged(boolean)} 1720 * listener. 1721 * <p> 1722 * Note: We post this to the mHandler to ensure that if a conference must be created as a 1723 * result of the multiparty state change, the conference creation happens on the correct 1724 * thread. This ensures that the thread check in 1725 * {@link com.android.internal.telephony.Phone#checkCorrectThread(android.os.Handler)} 1726 * does not fire. 1727 * 1728 * @param isMultiParty {@code true} if this connection is multiparty, {@code false} otherwise. 1729 */ handleMultipartyStateChange(boolean isMultiParty)1730 private void handleMultipartyStateChange(boolean isMultiParty) { 1731 Log.i(this, "Update multiparty state to %s", isMultiParty ? "Y" : "N"); 1732 mHandler.obtainMessage(MSG_MULTIPARTY_STATE_CHANGED, isMultiParty).sendToTarget(); 1733 } 1734 setActiveInternal()1735 private void setActiveInternal() { 1736 if (getState() == STATE_ACTIVE) { 1737 Log.w(this, "Should not be called if this is already ACTIVE"); 1738 return; 1739 } 1740 1741 // When we set a call to active, we need to make sure that there are no other active 1742 // calls. However, the ordering of state updates to connections can be non-deterministic 1743 // since all connections register for state changes on the phone independently. 1744 // To "optimize", we check here to see if there already exists any active calls. If so, 1745 // we issue an update for those calls first to make sure we only have one top-level 1746 // active call. 1747 if (getConnectionService() != null) { 1748 for (Connection current : getConnectionService().getAllConnections()) { 1749 if (current != this && current instanceof TelephonyConnection) { 1750 TelephonyConnection other = (TelephonyConnection) current; 1751 if (other.getState() == STATE_ACTIVE) { 1752 other.updateState(); 1753 } 1754 } 1755 } 1756 } 1757 setActive(); 1758 } 1759 close()1760 protected void close() { 1761 Log.v(this, "close"); 1762 clearOriginalConnection(); 1763 destroy(); 1764 } 1765 1766 /** 1767 * Determines if the current connection is video capable. 1768 * 1769 * A connection is deemed to be video capable if the original connection capabilities state that 1770 * both local and remote video is supported. 1771 * 1772 * @return {@code true} if the connection is video capable, {@code false} otherwise. 1773 */ isVideoCapable()1774 private boolean isVideoCapable() { 1775 return can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL) 1776 && can(mOriginalConnectionCapabilities, 1777 Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL); 1778 } 1779 1780 /** 1781 * Determines if the current connection is an external connection. 1782 * 1783 * A connection is deemed to be external if the original connection capabilities state that it 1784 * is. 1785 * 1786 * @return {@code true} if the connection is external, {@code false} otherwise. 1787 */ isExternalConnection()1788 private boolean isExternalConnection() { 1789 return can(mOriginalConnectionCapabilities, Capability.IS_EXTERNAL_CONNECTION); 1790 } 1791 1792 /** 1793 * Determines if the current connection has RTT enabled. 1794 */ isRtt()1795 private boolean isRtt() { 1796 return mOriginalConnection != null 1797 && mOriginalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS 1798 && mOriginalConnection instanceof ImsPhoneConnection 1799 && ((ImsPhoneConnection) mOriginalConnection).isRttEnabledForCall(); 1800 } 1801 1802 /** 1803 * Determines if the current connection is pullable. 1804 * 1805 * A connection is deemed to be pullable if the original connection capabilities state that it 1806 * is. 1807 * 1808 * @return {@code true} if the connection is pullable, {@code false} otherwise. 1809 */ isPullable()1810 private boolean isPullable() { 1811 return can(mOriginalConnectionCapabilities, Capability.IS_EXTERNAL_CONNECTION) 1812 && can(mOriginalConnectionCapabilities, Capability.IS_PULLABLE); 1813 } 1814 1815 /** 1816 * Sets whether or not CDMA enhanced call privacy is enabled for this connection. 1817 */ setCdmaVoicePrivacy(boolean isEnabled)1818 private void setCdmaVoicePrivacy(boolean isEnabled) { 1819 if(mIsCdmaVoicePrivacyEnabled != isEnabled) { 1820 mIsCdmaVoicePrivacyEnabled = isEnabled; 1821 updateConnectionProperties(); 1822 } 1823 } 1824 1825 /** 1826 * Applies capabilities specific to conferences termination to the 1827 * {@code ConnectionCapabilities} bit-mask. 1828 * 1829 * @param capabilities The {@code ConnectionCapabilities} bit-mask. 1830 * @return The capabilities with the IMS conference capabilities applied. 1831 */ applyConferenceTerminationCapabilities(int capabilities)1832 private int applyConferenceTerminationCapabilities(int capabilities) { 1833 int currentCapabilities = capabilities; 1834 1835 // An IMS call cannot be individually disconnected or separated from its parent conference. 1836 // If the call was IMS, even if it hands over to GMS, these capabilities are not supported. 1837 if (!mWasImsConnection) { 1838 currentCapabilities |= CAPABILITY_DISCONNECT_FROM_CONFERENCE; 1839 currentCapabilities |= CAPABILITY_SEPARATE_FROM_CONFERENCE; 1840 } 1841 1842 return currentCapabilities; 1843 } 1844 1845 /** 1846 * Stores the new original connection capabilities, and applies them to the current connection, 1847 * notifying any listeners as necessary. 1848 * 1849 * @param connectionCapabilities The original connection capabilties. 1850 */ setOriginalConnectionCapabilities(int connectionCapabilities)1851 public void setOriginalConnectionCapabilities(int connectionCapabilities) { 1852 mOriginalConnectionCapabilities = connectionCapabilities; 1853 updateConnectionCapabilities(); 1854 updateConnectionProperties(); 1855 } 1856 1857 /** 1858 * Called to apply the capabilities present in the {@link #mOriginalConnection} to this 1859 * {@link Connection}. Provides a mapping between the capabilities present in the original 1860 * connection (see {@link com.android.internal.telephony.Connection.Capability}) and those in 1861 * this {@link Connection}. 1862 * 1863 * @param capabilities The capabilities bitmask from the {@link Connection}. 1864 * @return the capabilities bitmask with the original connection capabilities remapped and 1865 * applied. 1866 */ applyOriginalConnectionCapabilities(int capabilities)1867 public int applyOriginalConnectionCapabilities(int capabilities) { 1868 // We only support downgrading to audio if both the remote and local side support 1869 // downgrading to audio. 1870 boolean supportsDowngradeToAudio = can(mOriginalConnectionCapabilities, 1871 Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL | 1872 Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE); 1873 capabilities = changeBitmask(capabilities, 1874 CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO, !supportsDowngradeToAudio); 1875 1876 capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL, 1877 can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL)); 1878 1879 boolean isLocalVideoSupported = can(mOriginalConnectionCapabilities, 1880 Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL) && !mIsTtyEnabled; 1881 capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL, 1882 isLocalVideoSupported); 1883 1884 return capabilities; 1885 } 1886 1887 /** 1888 * Whether the call is using wifi. 1889 */ isWifi()1890 boolean isWifi() { 1891 return getCallRadioTech() == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; 1892 } 1893 1894 /** 1895 * Sets whether this call has been identified by the network as an emergency call. 1896 * @param isNetworkIdentifiedEmergencyCall {@code true} if the network has identified this call 1897 * as an emergency call, {@code false} otherwise. 1898 */ setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall)1899 public void setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall) { 1900 Log.d(this, "setIsNetworkIdentifiedEmergencyCall; callId=%s, " 1901 + "isNetworkIdentifiedEmergencyCall=%b", getTelecomCallId(), 1902 isNetworkIdentifiedEmergencyCall); 1903 mIsNetworkIdentifiedEmergencyCall = isNetworkIdentifiedEmergencyCall; 1904 updateConnectionProperties(); 1905 } 1906 1907 /** 1908 * @return {@code true} if the network has identified this call as an emergency call, 1909 * {@code false} otherwise. 1910 */ isNetworkIdentifiedEmergencyCall()1911 public boolean isNetworkIdentifiedEmergencyCall() { 1912 return mIsNetworkIdentifiedEmergencyCall; 1913 } 1914 1915 /** 1916 * @return {@code true} if this is an outgoing call, {@code false} otherwise. 1917 */ isOutgoingCall()1918 boolean isOutgoingCall() { 1919 return mIsOutgoing; 1920 } 1921 1922 /** 1923 * Sets the current call audio quality. Used during rebuild of the properties 1924 * to set or unset the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property. 1925 * 1926 * @param audioQuality The audio quality. 1927 */ setAudioQuality(int audioQuality)1928 public void setAudioQuality(int audioQuality) { 1929 mHasHighDefAudio = audioQuality == 1930 com.android.internal.telephony.Connection.AUDIO_QUALITY_HIGH_DEFINITION; 1931 updateConnectionProperties(); 1932 } 1933 resetStateForConference()1934 void resetStateForConference() { 1935 if (getState() == Connection.STATE_HOLDING) { 1936 resetStateOverride(); 1937 } 1938 } 1939 setHoldingForConference()1940 boolean setHoldingForConference() { 1941 if (getState() == Connection.STATE_ACTIVE) { 1942 setStateOverride(Call.State.HOLDING); 1943 return true; 1944 } 1945 return false; 1946 } 1947 setRttTextStream(RttTextStream s)1948 public void setRttTextStream(RttTextStream s) { 1949 mRttTextStream = s; 1950 } 1951 getRttTextStream()1952 public RttTextStream getRttTextStream() { 1953 return mRttTextStream; 1954 } 1955 1956 /** 1957 * For video calls, sets whether this connection supports pausing the outgoing video for the 1958 * call using the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState. 1959 * 1960 * @param isVideoPauseSupported {@code true} if pause state supported, {@code false} otherwise. 1961 */ setVideoPauseSupported(boolean isVideoPauseSupported)1962 public void setVideoPauseSupported(boolean isVideoPauseSupported) { 1963 mIsVideoPauseSupported = isVideoPauseSupported; 1964 } 1965 1966 /** 1967 * @return {@code true} if this connection supports pausing the outgoing video using the 1968 * {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState. 1969 */ getVideoPauseSupported()1970 public boolean getVideoPauseSupported() { 1971 return mIsVideoPauseSupported; 1972 } 1973 1974 /** 1975 * Sets whether this connection supports conference calling. 1976 * @param isConferenceSupported {@code true} if conference calling is supported by this 1977 * connection, {@code false} otherwise. 1978 */ setConferenceSupported(boolean isConferenceSupported)1979 public void setConferenceSupported(boolean isConferenceSupported) { 1980 mIsConferenceSupported = isConferenceSupported; 1981 } 1982 1983 /** 1984 * @return {@code true} if this connection supports merging calls into a conference. 1985 */ isConferenceSupported()1986 public boolean isConferenceSupported() { 1987 return mIsConferenceSupported; 1988 } 1989 1990 /** 1991 * Sets whether managing conference call is supported after this connection being a part of a 1992 * Ims conference. 1993 * 1994 * @param isManageImsConferenceCallSupported {@code true} if manage conference calling is 1995 * supported after this connection being a part of a IMS conference, 1996 * {@code false} otherwise. 1997 */ setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported)1998 public void setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported) { 1999 mIsManageImsConferenceCallSupported = isManageImsConferenceCallSupported; 2000 } 2001 2002 /** 2003 * @return {@code true} if manage conference calling is supported after this connection being a 2004 * part of a IMS conference. 2005 */ isManageImsConferenceCallSupported()2006 public boolean isManageImsConferenceCallSupported() { 2007 return mIsManageImsConferenceCallSupported; 2008 } 2009 2010 /** 2011 * Sets whether this connection supports showing precise call disconnect cause. 2012 * @param showPreciseFailedCause {@code true} if showing precise call 2013 * disconnect cause is supported by this connection, {@code false} otherwise. 2014 */ setShowPreciseFailedCause(boolean showPreciseFailedCause)2015 public void setShowPreciseFailedCause(boolean showPreciseFailedCause) { 2016 mShowPreciseFailedCause = showPreciseFailedCause; 2017 } 2018 2019 /** 2020 * Sets whether TTY is enabled or not. 2021 * @param isTtyEnabled 2022 */ setTtyEnabled(boolean isTtyEnabled)2023 public void setTtyEnabled(boolean isTtyEnabled) { 2024 mIsTtyEnabled = isTtyEnabled; 2025 updateConnectionCapabilities(); 2026 } 2027 2028 /** 2029 * Whether the original connection is an IMS connection. 2030 * @return {@code True} if the original connection is an IMS connection, {@code false} 2031 * otherwise. 2032 */ isImsConnection()2033 protected boolean isImsConnection() { 2034 com.android.internal.telephony.Connection originalConnection = getOriginalConnection(); 2035 return originalConnection != null && 2036 originalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS; 2037 } 2038 2039 /** 2040 * Whether the original connection is an GSM/CDMA connection. 2041 * @return {@code True} if the original connection is an GSM/CDMA connection, {@code false} 2042 * otherwise. 2043 */ isGsmCdmaConnection()2044 protected boolean isGsmCdmaConnection() { 2045 Phone phone = getPhone(); 2046 if (phone != null) { 2047 switch (phone.getPhoneType()) { 2048 case PhoneConstants.PHONE_TYPE_GSM: 2049 case PhoneConstants.PHONE_TYPE_CDMA: 2050 return true; 2051 default: 2052 return false; 2053 } 2054 } 2055 return false; 2056 } 2057 2058 /** 2059 * Whether the original connection was ever an IMS connection, either before or now. 2060 * @return {@code True} if the original connection was ever an IMS connection, {@code false} 2061 * otherwise. 2062 */ wasImsConnection()2063 public boolean wasImsConnection() { 2064 return mWasImsConnection; 2065 } 2066 getIsUsingAssistedDialing()2067 boolean getIsUsingAssistedDialing() { 2068 return mIsUsingAssistedDialing; 2069 } 2070 setIsUsingAssistedDialing(Boolean isUsingAssistedDialing)2071 void setIsUsingAssistedDialing(Boolean isUsingAssistedDialing) { 2072 mIsUsingAssistedDialing = isUsingAssistedDialing; 2073 updateConnectionProperties(); 2074 } 2075 getAddressFromNumber(String number)2076 private static Uri getAddressFromNumber(String number) { 2077 // Address can be null for blocked calls. 2078 if (number == null) { 2079 number = ""; 2080 } 2081 return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null); 2082 } 2083 2084 /** 2085 * Changes a capabilities bit-mask to add or remove a capability. 2086 * 2087 * @param bitmask The bit-mask. 2088 * @param bitfield The bit-field to change. 2089 * @param enabled Whether the bit-field should be set or removed. 2090 * @return The bit-mask with the bit-field changed. 2091 */ changeBitmask(int bitmask, int bitfield, boolean enabled)2092 private int changeBitmask(int bitmask, int bitfield, boolean enabled) { 2093 if (enabled) { 2094 return bitmask | bitfield; 2095 } else { 2096 return bitmask & ~bitfield; 2097 } 2098 } 2099 updateStatusHints()2100 private void updateStatusHints() { 2101 if (isWifi() && getPhone() != null) { 2102 int labelId = isValidRingingCall() 2103 ? R.string.status_hint_label_incoming_wifi_call 2104 : R.string.status_hint_label_wifi_call; 2105 2106 Context context = getPhone().getContext(); 2107 setStatusHints(new StatusHints( 2108 context.getString(labelId), 2109 Icon.createWithResource( 2110 context, R.drawable.ic_signal_wifi_4_bar_24dp), 2111 null /* extras */)); 2112 } else { 2113 setStatusHints(null); 2114 } 2115 } 2116 2117 /** 2118 * Register a listener for {@link TelephonyConnection} specific triggers. 2119 * @param l The instance of the listener to add 2120 * @return The connection being listened to 2121 */ addTelephonyConnectionListener(TelephonyConnectionListener l)2122 public final TelephonyConnection addTelephonyConnectionListener(TelephonyConnectionListener l) { 2123 mTelephonyListeners.add(l); 2124 // If we already have an original connection, let's call back immediately. 2125 // This would be the case for incoming calls. 2126 if (mOriginalConnection != null) { 2127 fireOnOriginalConnectionConfigured(); 2128 } 2129 return this; 2130 } 2131 2132 /** 2133 * Remove a listener for {@link TelephonyConnection} specific triggers. 2134 * @param l The instance of the listener to remove 2135 * @return The connection being listened to 2136 */ removeTelephonyConnectionListener( TelephonyConnectionListener l)2137 public final TelephonyConnection removeTelephonyConnectionListener( 2138 TelephonyConnectionListener l) { 2139 if (l != null) { 2140 mTelephonyListeners.remove(l); 2141 } 2142 return this; 2143 } 2144 2145 @Override setHoldable(boolean isHoldable)2146 public void setHoldable(boolean isHoldable) { 2147 mIsHoldable = isHoldable; 2148 updateConnectionCapabilities(); 2149 } 2150 2151 @Override isChildHoldable()2152 public boolean isChildHoldable() { 2153 return getConference() != null; 2154 } 2155 isHoldable()2156 public boolean isHoldable() { 2157 return mIsHoldable; 2158 } 2159 2160 /** 2161 * Fire a callback to the various listeners for when the original connection is 2162 * set in this {@link TelephonyConnection} 2163 */ fireOnOriginalConnectionConfigured()2164 private final void fireOnOriginalConnectionConfigured() { 2165 for (TelephonyConnectionListener l : mTelephonyListeners) { 2166 l.onOriginalConnectionConfigured(this); 2167 } 2168 } 2169 fireOnOriginalConnectionRetryDial(boolean isPermanentFailure)2170 private final void fireOnOriginalConnectionRetryDial(boolean isPermanentFailure) { 2171 for (TelephonyConnectionListener l : mTelephonyListeners) { 2172 l.onOriginalConnectionRetry(this, isPermanentFailure); 2173 } 2174 } 2175 2176 /** 2177 * Handles exiting ECM mode. 2178 */ handleExitedEcmMode()2179 protected void handleExitedEcmMode() { 2180 updateConnectionProperties(); 2181 } 2182 2183 /** 2184 * Determines whether the connection supports conference calling. A connection supports 2185 * conference calling if it: 2186 * 1. Is not an emergency call. 2187 * 2. Carrier supports conference calls. 2188 * 3. If call is a video call, carrier supports video conference calls. 2189 * 4. If call is a wifi call and VoWIFI is disabled and carrier supports merging these calls. 2190 */ refreshConferenceSupported()2191 private void refreshConferenceSupported() { 2192 boolean isVideoCall = VideoProfile.isVideo(getVideoState()); 2193 Phone phone = getPhone(); 2194 if (phone == null) { 2195 Log.w(this, "refreshConferenceSupported = false; phone is null"); 2196 if (isConferenceSupported()) { 2197 setConferenceSupported(false); 2198 notifyConferenceSupportedChanged(false); 2199 } 2200 return; 2201 } 2202 2203 boolean isIms = phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS; 2204 boolean isVoWifiEnabled = false; 2205 if (isIms) { 2206 isVoWifiEnabled = ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId()); 2207 } 2208 PhoneAccountHandle phoneAccountHandle = isIms ? PhoneUtils 2209 .makePstnPhoneAccountHandle(phone.getDefaultPhone()) 2210 : PhoneUtils.makePstnPhoneAccountHandle(phone); 2211 TelecomAccountRegistry telecomAccountRegistry = TelecomAccountRegistry 2212 .getInstance(getPhone().getContext()); 2213 boolean isConferencingSupported = telecomAccountRegistry 2214 .isMergeCallSupported(phoneAccountHandle); 2215 boolean isImsConferencingSupported = telecomAccountRegistry 2216 .isMergeImsCallSupported(phoneAccountHandle); 2217 mIsCarrierVideoConferencingSupported = telecomAccountRegistry 2218 .isVideoConferencingSupported(phoneAccountHandle); 2219 boolean isMergeOfWifiCallsAllowedWhenVoWifiOff = telecomAccountRegistry 2220 .isMergeOfWifiCallsAllowedWhenVoWifiOff(phoneAccountHandle); 2221 2222 Log.v(this, "refreshConferenceSupported : isConfSupp=%b, isImsConfSupp=%b, " + 2223 "isVidConfSupp=%b, isMergeOfWifiAllowed=%b, " + 2224 "isWifi=%b, isVoWifiEnabled=%b", 2225 isConferencingSupported, isImsConferencingSupported, 2226 mIsCarrierVideoConferencingSupported, isMergeOfWifiCallsAllowedWhenVoWifiOff, 2227 isWifi(), isVoWifiEnabled); 2228 boolean isConferenceSupported = true; 2229 if (mTreatAsEmergencyCall) { 2230 isConferenceSupported = false; 2231 Log.d(this, "refreshConferenceSupported = false; emergency call"); 2232 } else if (isRtt()) { 2233 isConferenceSupported = false; 2234 Log.d(this, "refreshConferenceSupported = false; rtt call"); 2235 } else if (!isConferencingSupported || isIms && !isImsConferencingSupported) { 2236 isConferenceSupported = false; 2237 Log.d(this, "refreshConferenceSupported = false; carrier doesn't support conf."); 2238 } else if (isVideoCall && !mIsCarrierVideoConferencingSupported) { 2239 isConferenceSupported = false; 2240 Log.d(this, "refreshConferenceSupported = false; video conf not supported."); 2241 } else if (!isMergeOfWifiCallsAllowedWhenVoWifiOff && isWifi() && !isVoWifiEnabled) { 2242 isConferenceSupported = false; 2243 Log.d(this, 2244 "refreshConferenceSupported = false; can't merge wifi calls when voWifi off."); 2245 } else { 2246 Log.d(this, "refreshConferenceSupported = true."); 2247 } 2248 2249 if (isConferenceSupported != isConferenceSupported()) { 2250 setConferenceSupported(isConferenceSupported); 2251 notifyConferenceSupportedChanged(isConferenceSupported); 2252 } 2253 } 2254 /** 2255 * Provides a mapping from extras keys which may be found in the 2256 * {@link com.android.internal.telephony.Connection} to their equivalents defined in 2257 * {@link android.telecom.Connection}. 2258 * 2259 * @return Map containing key mappings. 2260 */ createExtrasMap()2261 private static Map<String, String> createExtrasMap() { 2262 Map<String, String> result = new HashMap<String, String>(); 2263 result.put(ImsCallProfile.EXTRA_CHILD_NUMBER, 2264 android.telecom.Connection.EXTRA_CHILD_ADDRESS); 2265 result.put(ImsCallProfile.EXTRA_DISPLAY_TEXT, 2266 android.telecom.Connection.EXTRA_CALL_SUBJECT); 2267 result.put(ImsCallProfile.EXTRA_ADDITIONAL_SIP_INVITE_FIELDS, 2268 android.telecom.Connection.EXTRA_SIP_INVITE); 2269 return Collections.unmodifiableMap(result); 2270 } 2271 isShowingOriginalDialString()2272 private boolean isShowingOriginalDialString() { 2273 boolean showOrigDialString = false; 2274 Phone phone = getPhone(); 2275 if (phone != null && (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) 2276 && !mOriginalConnection.isIncoming()) { 2277 PersistableBundle pb = getCarrierConfig(); 2278 if (pb != null) { 2279 showOrigDialString = pb.getBoolean(CarrierConfigManager 2280 .KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL); 2281 Log.d(this, "showOrigDialString: " + showOrigDialString); 2282 } 2283 } 2284 return showOrigDialString; 2285 } 2286 2287 /** 2288 * Creates a string representation of this {@link TelephonyConnection}. Primarily intended for 2289 * use in log statements. 2290 * 2291 * @return String representation of the connection. 2292 */ 2293 @Override toString()2294 public String toString() { 2295 StringBuilder sb = new StringBuilder(); 2296 sb.append("[TelephonyConnection objId:"); 2297 sb.append(System.identityHashCode(this)); 2298 sb.append(" telecomCallID:"); 2299 sb.append(getTelecomCallId()); 2300 sb.append(" type:"); 2301 if (isImsConnection()) { 2302 sb.append("ims"); 2303 } else if (this instanceof com.android.services.telephony.GsmConnection) { 2304 sb.append("gsm"); 2305 } else if (this instanceof CdmaConnection) { 2306 sb.append("cdma"); 2307 } 2308 sb.append(" state:"); 2309 sb.append(Connection.stateToString(getState())); 2310 sb.append(" capabilities:"); 2311 sb.append(capabilitiesToString(getConnectionCapabilities())); 2312 sb.append(" properties:"); 2313 sb.append(propertiesToString(getConnectionProperties())); 2314 sb.append(" address:"); 2315 sb.append(Log.pii(getAddress())); 2316 sb.append(" originalConnection:"); 2317 sb.append(mOriginalConnection); 2318 sb.append(" partOfConf:"); 2319 if (getConference() == null) { 2320 sb.append("N"); 2321 } else { 2322 sb.append("Y"); 2323 } 2324 sb.append(" confSupported:"); 2325 sb.append(mIsConferenceSupported ? "Y" : "N"); 2326 sb.append("]"); 2327 return sb.toString(); 2328 } 2329 } 2330