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.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.content.res.Resources; 23 import android.graphics.drawable.Icon; 24 import android.net.Uri; 25 import android.os.AsyncResult; 26 import android.os.Bundle; 27 import android.os.Handler; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.os.PersistableBundle; 31 import android.telecom.CallAudioState; 32 import android.telecom.Conference; 33 import android.telecom.Connection; 34 import android.telecom.ConnectionService; 35 import android.telecom.PhoneAccount; 36 import android.telecom.PhoneAccountHandle; 37 import android.telecom.StatusHints; 38 import android.telecom.TelecomManager; 39 import android.telecom.VideoProfile; 40 import android.telephony.CarrierConfigManager; 41 import android.telephony.DisconnectCause; 42 import android.telephony.PhoneNumberUtils; 43 import android.telephony.ServiceState; 44 import android.telephony.ServiceState.RilRadioTechnology; 45 import android.telephony.SubscriptionManager; 46 import android.telephony.TelephonyManager; 47 import android.telephony.ims.ImsCallProfile; 48 import android.telephony.ims.ImsStreamMediaProfile; 49 import android.text.TextUtils; 50 import android.util.Pair; 51 52 import com.android.ims.ImsCall; 53 import com.android.ims.ImsException; 54 import com.android.ims.internal.ConferenceParticipant; 55 import com.android.internal.annotations.VisibleForTesting; 56 import com.android.internal.os.SomeArgs; 57 import com.android.internal.telephony.Call; 58 import com.android.internal.telephony.CallFailCause; 59 import com.android.internal.telephony.CallStateException; 60 import com.android.internal.telephony.Connection.Capability; 61 import com.android.internal.telephony.Connection.PostDialListener; 62 import com.android.internal.telephony.Phone; 63 import com.android.internal.telephony.PhoneConstants; 64 import com.android.internal.telephony.gsm.SuppServiceNotification; 65 import com.android.internal.telephony.imsphone.ImsPhone; 66 import com.android.internal.telephony.imsphone.ImsPhoneCall; 67 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker; 68 import com.android.internal.telephony.imsphone.ImsPhoneConnection; 69 import com.android.phone.ImsUtil; 70 import com.android.phone.PhoneGlobals; 71 import com.android.phone.PhoneUtils; 72 import com.android.phone.R; 73 import com.android.telephony.Rlog; 74 75 import java.util.ArrayList; 76 import java.util.Arrays; 77 import java.util.Collections; 78 import java.util.HashMap; 79 import java.util.List; 80 import java.util.Map; 81 import java.util.Objects; 82 import java.util.Set; 83 import java.util.concurrent.ConcurrentHashMap; 84 85 /** 86 * Base class for CDMA and GSM connections. 87 */ 88 abstract class TelephonyConnection extends Connection implements Holdable { 89 private static final String LOG_TAG = "TelephonyConnection"; 90 91 private static final int MSG_PRECISE_CALL_STATE_CHANGED = 1; 92 private static final int MSG_RINGBACK_TONE = 2; 93 private static final int MSG_HANDOVER_STATE_CHANGED = 3; 94 private static final int MSG_DISCONNECT = 4; 95 private static final int MSG_MULTIPARTY_STATE_CHANGED = 5; 96 private static final int MSG_CONFERENCE_MERGE_FAILED = 6; 97 private static final int MSG_SUPP_SERVICE_NOTIFY = 7; 98 99 /** 100 * Mappings from {@link com.android.internal.telephony.Connection} extras keys to their 101 * equivalents defined in {@link android.telecom.Connection}. 102 */ 103 private static final Map<String, String> sExtrasMap = createExtrasMap(); 104 105 private static final int MSG_SET_VIDEO_STATE = 8; 106 private static final int MSG_SET_VIDEO_PROVIDER = 9; 107 private static final int MSG_SET_AUDIO_QUALITY = 10; 108 private static final int MSG_SET_CONFERENCE_PARTICIPANTS = 11; 109 private static final int MSG_CONNECTION_EXTRAS_CHANGED = 12; 110 private static final int MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES = 13; 111 private static final int MSG_ON_HOLD_TONE = 14; 112 private static final int MSG_CDMA_VOICE_PRIVACY_ON = 15; 113 private static final int MSG_CDMA_VOICE_PRIVACY_OFF = 16; 114 private static final int MSG_HANGUP = 17; 115 private static final int MSG_SET_CALL_RADIO_TECH = 18; 116 private static final int MSG_ON_CONNECTION_EVENT = 19; 117 private static final int MSG_REDIAL_CONNECTION_CHANGED = 20; 118 private static final int MSG_REJECT = 21; 119 120 private List<Uri> mParticipants; 121 private boolean mIsAdhocConferenceCall; 122 123 private final Handler mHandler = new Handler(Looper.getMainLooper()) { 124 @Override 125 public void handleMessage(Message msg) { 126 switch (msg.what) { 127 case MSG_PRECISE_CALL_STATE_CHANGED: 128 Log.v(TelephonyConnection.this, "MSG_PRECISE_CALL_STATE_CHANGED"); 129 updateState(); 130 break; 131 case MSG_HANDOVER_STATE_CHANGED: 132 case MSG_REDIAL_CONNECTION_CHANGED: 133 String what = (msg.what == MSG_HANDOVER_STATE_CHANGED) 134 ? "MSG_HANDOVER_STATE_CHANGED" : "MSG_REDIAL_CONNECTION_CHANGED"; 135 Log.v(TelephonyConnection.this, what); 136 AsyncResult ar = (AsyncResult) msg.obj; 137 com.android.internal.telephony.Connection connection = 138 (com.android.internal.telephony.Connection) ar.result; 139 if (connection == null) { 140 setDisconnected(DisconnectCauseUtil 141 .toTelecomDisconnectCause(DisconnectCause.OUT_OF_NETWORK, 142 "handover failure, no connection")); 143 close(); 144 break; 145 } 146 if (mOriginalConnection != null) { 147 if (connection != null && 148 ((connection.getAddress() != null && 149 mOriginalConnection.getAddress() != null && 150 mOriginalConnection.getAddress().equals(connection.getAddress())) || 151 connection.getState() == mOriginalConnection.getStateBeforeHandover())) { 152 Log.d(TelephonyConnection.this, 153 "SettingOriginalConnection " + mOriginalConnection.toString() 154 + " with " + connection.toString()); 155 setOriginalConnection(connection); 156 mWasImsConnection = false; 157 } 158 } else { 159 Log.w(TelephonyConnection.this, 160 what + ": mOriginalConnection==null - invalid state (not cleaned up)"); 161 } 162 break; 163 case MSG_RINGBACK_TONE: 164 Log.v(TelephonyConnection.this, "MSG_RINGBACK_TONE"); 165 // TODO: This code assumes that there is only one connection in the foreground 166 // call, in other words, it punts on network-mediated conference calling. 167 if (getOriginalConnection() != getForegroundConnection()) { 168 Log.v(TelephonyConnection.this, "handleMessage, original connection is " + 169 "not foreground connection, skipping"); 170 return; 171 } 172 boolean ringback = (Boolean) ((AsyncResult) msg.obj).result; 173 setRingbackRequested(ringback); 174 notifyRingbackRequested(ringback); 175 break; 176 case MSG_DISCONNECT: 177 updateState(); 178 break; 179 case MSG_MULTIPARTY_STATE_CHANGED: 180 boolean isMultiParty = (Boolean) msg.obj; 181 Log.i(this, "Update multiparty state to %s", isMultiParty ? "Y" : "N"); 182 mIsMultiParty = isMultiParty; 183 if (isMultiParty) { 184 notifyConferenceStarted(); 185 } 186 break; 187 case MSG_CONFERENCE_MERGE_FAILED: 188 notifyConferenceMergeFailed(); 189 break; 190 case MSG_SUPP_SERVICE_NOTIFY: 191 Phone phone = getPhone(); 192 Log.v(TelephonyConnection.this, "MSG_SUPP_SERVICE_NOTIFY on phoneId : " 193 + (phone != null ? Integer.toString(phone.getPhoneId()) 194 : "null")); 195 SuppServiceNotification mSsNotification = null; 196 if (msg.obj != null && ((AsyncResult) msg.obj).result != null) { 197 mSsNotification = 198 (SuppServiceNotification)((AsyncResult) msg.obj).result; 199 if (mOriginalConnection != null) { 200 handleSuppServiceNotification(mSsNotification); 201 } 202 } 203 break; 204 205 case MSG_SET_VIDEO_STATE: 206 int videoState = (int) msg.obj; 207 setTelephonyVideoState(videoState); 208 209 // A change to the video state of the call can influence whether or not it 210 // can be part of a conference, whether another call can be added, and 211 // whether the call should have the HD audio property set. 212 refreshConferenceSupported(); 213 refreshDisableAddCall(); 214 refreshHoldSupported(); 215 updateConnectionProperties(); 216 break; 217 218 case MSG_SET_VIDEO_PROVIDER: 219 VideoProvider videoProvider = (VideoProvider) msg.obj; 220 setTelephonyVideoProvider(videoProvider); 221 break; 222 223 case MSG_SET_AUDIO_QUALITY: 224 int audioQuality = (int) msg.obj; 225 setAudioQuality(audioQuality); 226 break; 227 228 case MSG_SET_CONFERENCE_PARTICIPANTS: 229 List<ConferenceParticipant> participants = (List<ConferenceParticipant>) msg.obj; 230 updateConferenceParticipants(participants); 231 break; 232 233 case MSG_CONNECTION_EXTRAS_CHANGED: 234 final Bundle extras = (Bundle) msg.obj; 235 updateExtras(extras); 236 break; 237 238 case MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES: 239 setOriginalConnectionCapabilities(msg.arg1); 240 break; 241 242 case MSG_ON_HOLD_TONE: 243 AsyncResult asyncResult = (AsyncResult) msg.obj; 244 Pair<com.android.internal.telephony.Connection, Boolean> heldInfo = 245 (Pair<com.android.internal.telephony.Connection, Boolean>) 246 asyncResult.result; 247 248 // Determines if the hold tone is starting or stopping. 249 boolean playTone = ((Boolean) (heldInfo.second)).booleanValue(); 250 251 // Determine which connection the hold tone is stopping or starting for 252 com.android.internal.telephony.Connection heldConnection = heldInfo.first; 253 254 // Only start or stop the hold tone if this is the connection which is starting 255 // or stopping the hold tone. 256 if (heldConnection == mOriginalConnection) { 257 // If starting the hold tone, send a connection event to Telecom which will 258 // cause it to play the on hold tone. 259 if (playTone) { 260 sendTelephonyConnectionEvent(EVENT_ON_HOLD_TONE_START, null); 261 } else { 262 sendTelephonyConnectionEvent(EVENT_ON_HOLD_TONE_END, null); 263 } 264 } 265 break; 266 267 case MSG_CDMA_VOICE_PRIVACY_ON: 268 Log.d(this, "MSG_CDMA_VOICE_PRIVACY_ON received"); 269 setCdmaVoicePrivacy(true); 270 break; 271 case MSG_CDMA_VOICE_PRIVACY_OFF: 272 Log.d(this, "MSG_CDMA_VOICE_PRIVACY_OFF received"); 273 setCdmaVoicePrivacy(false); 274 break; 275 case MSG_HANGUP: 276 int cause = (int) msg.obj; 277 hangup(cause); 278 break; 279 case MSG_REJECT: 280 int rejectReason = (int) msg.obj; 281 reject(rejectReason); 282 break; 283 284 case MSG_SET_CALL_RADIO_TECH: 285 int vrat = (int) msg.obj; 286 // Check whether Wi-Fi call tech is changed, it means call radio tech is: 287 // a) changed from IWLAN to other value, or 288 // b) changed from other value to IWLAN. 289 // 290 // In other word, below conditions are all met: 291 // 1) {@link #getCallRadioTech} is different from new vrat 292 // 2) Current call radio technology indicates Wi-Fi call, i.e. {@link #isWifi} 293 // is true, or new vrat indicates Wi-Fi call. 294 boolean isWifiTechChange = getCallRadioTech() != vrat 295 && (isWifi() || vrat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN); 296 297 // Step 1) Updates call radio tech firstly, so that afterwards Wi-Fi related 298 // update actions are taken correctly. 299 setCallRadioTech(vrat); 300 301 // Step 2) Handles Wi-Fi call tech change. 302 if (isWifiTechChange) { 303 updateConnectionProperties(); 304 updateStatusHints(); 305 refreshDisableAddCall(); 306 } 307 break; 308 case MSG_ON_CONNECTION_EVENT: 309 SomeArgs args = (SomeArgs) msg.obj; 310 try { 311 sendTelephonyConnectionEvent((String) args.arg1, (Bundle) args.arg2); 312 313 } finally { 314 args.recycle(); 315 } 316 break; 317 } 318 } 319 }; 320 321 /** 322 * Handles {@link SuppServiceNotification}s pertinent to Telephony. 323 * @param ssn the notification. 324 */ handleSuppServiceNotification(SuppServiceNotification ssn)325 private void handleSuppServiceNotification(SuppServiceNotification ssn) { 326 Log.i(this, "handleSuppServiceNotification: type=%d, code=%d", ssn.notificationType, 327 ssn.code); 328 if (ssn.notificationType == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1 329 && ssn.code == SuppServiceNotification.CODE_1_CALL_FORWARDED) { 330 sendTelephonyConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null); 331 } 332 sendSuppServiceNotificationEvent(ssn.notificationType, ssn.code); 333 } 334 335 /** 336 * Sends a supplementary service notification connection event. 337 * This connection event includes the type and code, as well as a human readable message which 338 * is suitable for display to the user if the UI chooses to do so. 339 * @param type the {@link SuppServiceNotification#type}. 340 * @param code the {@link SuppServiceNotification#code}. 341 */ sendSuppServiceNotificationEvent(int type, int code)342 private void sendSuppServiceNotificationEvent(int type, int code) { 343 Bundle extras = new Bundle(); 344 extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_TYPE, type); 345 extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_CODE, code); 346 extras.putCharSequence(TelephonyManager.EXTRA_NOTIFICATION_MESSAGE, 347 getSuppServiceMessage(type, code)); 348 sendTelephonyConnectionEvent(TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION, 349 extras); 350 } 351 352 /** 353 * Retrieves a human-readable message for a supplementary service notification. 354 * This message is suitable for display to the user. 355 * @param type the code group. 356 * @param code the code. 357 * @return A {@link CharSequence} containing the message, or {@code null} if none defined. 358 */ getSuppServiceMessage(int type, int code)359 private CharSequence getSuppServiceMessage(int type, int code) { 360 int messageId = -1; 361 if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1) { 362 switch (code) { 363 case SuppServiceNotification.CODE_1_CALL_DEFLECTED: 364 messageId = R.string.supp_service_notification_call_deflected; 365 break; 366 case SuppServiceNotification.CODE_1_CALL_FORWARDED: 367 messageId = R.string.supp_service_notification_call_forwarded; 368 break; 369 case SuppServiceNotification.CODE_1_CALL_IS_WAITING: 370 messageId = R.string.supp_service_notification_call_waiting; 371 break; 372 case SuppServiceNotification.CODE_1_CLIR_SUPPRESSION_REJECTED: 373 messageId = R.string.supp_service_clir_suppression_rejected; 374 break; 375 case SuppServiceNotification.CODE_1_CUG_CALL: 376 messageId = R.string.supp_service_closed_user_group_call; 377 break; 378 case SuppServiceNotification.CODE_1_INCOMING_CALLS_BARRED: 379 messageId = R.string.supp_service_incoming_calls_barred; 380 break; 381 case SuppServiceNotification.CODE_1_OUTGOING_CALLS_BARRED: 382 messageId = R.string.supp_service_outgoing_calls_barred; 383 break; 384 case SuppServiceNotification.CODE_1_SOME_CF_ACTIVE: 385 // Intentional fall through. 386 case SuppServiceNotification.CODE_1_UNCONDITIONAL_CF_ACTIVE: 387 messageId = R.string.supp_service_call_forwarding_active; 388 break; 389 } 390 } else if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_2) { 391 switch (code) { 392 case SuppServiceNotification.CODE_2_ADDITIONAL_CALL_FORWARDED: 393 messageId = R.string.supp_service_additional_call_forwarded; 394 break; 395 case SuppServiceNotification.CODE_2_CALL_CONNECTED_ECT: 396 messageId = R.string.supp_service_additional_ect_connected; 397 break; 398 case SuppServiceNotification.CODE_2_CALL_CONNECTING_ECT: 399 messageId = R.string.supp_service_additional_ect_connecting; 400 break; 401 case SuppServiceNotification.CODE_2_CALL_ON_HOLD: 402 messageId = R.string.supp_service_call_on_hold; 403 break; 404 case SuppServiceNotification.CODE_2_CALL_RETRIEVED: 405 messageId = R.string.supp_service_call_resumed; 406 break; 407 case SuppServiceNotification.CODE_2_CUG_CALL: 408 messageId = R.string.supp_service_closed_user_group_call; 409 break; 410 case SuppServiceNotification.CODE_2_DEFLECTED_CALL: 411 messageId = R.string.supp_service_deflected_call; 412 break; 413 case SuppServiceNotification.CODE_2_FORWARDED_CALL: 414 messageId = R.string.supp_service_forwarded_call; 415 break; 416 case SuppServiceNotification.CODE_2_MULTI_PARTY_CALL: 417 messageId = R.string.supp_service_conference_call; 418 break; 419 case SuppServiceNotification.CODE_2_ON_HOLD_CALL_RELEASED: 420 messageId = R.string.supp_service_held_call_released; 421 break; 422 } 423 } 424 if (messageId != -1 && getPhone() != null && getPhone().getContext() != null) { 425 return getResourceText(messageId); 426 } else { 427 return null; 428 } 429 } 430 431 @VisibleForTesting getResourceText(int id)432 public CharSequence getResourceText(int id) { 433 Resources resources = SubscriptionManager.getResourcesForSubId(getPhone().getContext(), 434 getPhone().getSubId()); 435 return resources.getText(id); 436 } 437 438 @VisibleForTesting getResourceString(int id)439 public String getResourceString(int id) { 440 Resources resources = SubscriptionManager.getResourcesForSubId(getPhone().getContext(), 441 getPhone().getSubId()); 442 return resources.getString(id); 443 } 444 445 /** 446 * @return {@code true} if carrier video conferencing is supported, {@code false} otherwise. 447 */ isCarrierVideoConferencingSupported()448 public boolean isCarrierVideoConferencingSupported() { 449 return mIsCarrierVideoConferencingSupported; 450 } 451 452 /** 453 * A listener/callback mechanism that is specific communication from TelephonyConnections 454 * to TelephonyConnectionService (for now). It is more specific that Connection.Listener 455 * because it is only exposed in Telephony. 456 */ 457 public abstract static class TelephonyConnectionListener { onOriginalConnectionConfigured(TelephonyConnection c)458 public void onOriginalConnectionConfigured(TelephonyConnection c) {} onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure)459 public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {} onConferenceParticipantsChanged(Connection c, List<ConferenceParticipant> participants)460 public void onConferenceParticipantsChanged(Connection c, 461 List<ConferenceParticipant> participants) {} onConferenceStarted()462 public void onConferenceStarted() {} onConferenceSupportedChanged(Connection c, boolean isConferenceSupported)463 public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {} 464 onConnectionCapabilitiesChanged(Connection c, int connectionCapabilities)465 public void onConnectionCapabilitiesChanged(Connection c, int connectionCapabilities) {} onConnectionEvent(Connection c, String event, Bundle extras)466 public void onConnectionEvent(Connection c, String event, Bundle extras) {} onConnectionPropertiesChanged(Connection c, int connectionProperties)467 public void onConnectionPropertiesChanged(Connection c, int connectionProperties) {} onExtrasChanged(Connection c, Bundle extras)468 public void onExtrasChanged(Connection c, Bundle extras) {} onExtrasRemoved(Connection c, List<String> keys)469 public void onExtrasRemoved(Connection c, List<String> keys) {} onStateChanged(android.telecom.Connection c, int state)470 public void onStateChanged(android.telecom.Connection c, int state) {} onStatusHintsChanged(Connection c, StatusHints statusHints)471 public void onStatusHintsChanged(Connection c, StatusHints statusHints) {} onDestroyed(Connection c)472 public void onDestroyed(Connection c) {} onDisconnected(android.telecom.Connection c, android.telecom.DisconnectCause disconnectCause)473 public void onDisconnected(android.telecom.Connection c, 474 android.telecom.DisconnectCause disconnectCause) {} onVideoProviderChanged(android.telecom.Connection c, Connection.VideoProvider videoProvider)475 public void onVideoProviderChanged(android.telecom.Connection c, 476 Connection.VideoProvider videoProvider) {} onVideoStateChanged(android.telecom.Connection c, int videoState)477 public void onVideoStateChanged(android.telecom.Connection c, int videoState) {} onRingbackRequested(Connection c, boolean ringback)478 public void onRingbackRequested(Connection c, boolean ringback) {} 479 } 480 481 private final PostDialListener mPostDialListener = new PostDialListener() { 482 @Override 483 public void onPostDialWait() { 484 Log.v(TelephonyConnection.this, "onPostDialWait"); 485 if (mOriginalConnection != null) { 486 setPostDialWait(mOriginalConnection.getRemainingPostDialString()); 487 } 488 } 489 490 @Override 491 public void onPostDialChar(char c) { 492 Log.v(TelephonyConnection.this, "onPostDialChar: %s", c); 493 if (mOriginalConnection != null) { 494 setNextPostDialChar(c); 495 } 496 } 497 }; 498 499 /** 500 * Listener for listening to events in the {@link com.android.internal.telephony.Connection}. 501 */ 502 private final com.android.internal.telephony.Connection.Listener mOriginalConnectionListener = 503 new com.android.internal.telephony.Connection.ListenerBase() { 504 @Override 505 public void onVideoStateChanged(int videoState) { 506 mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState).sendToTarget(); 507 } 508 509 /* 510 * The {@link com.android.internal.telephony.Connection} has reported a change in 511 * connection capability. 512 * @param capabilities bit mask containing voice or video or both capabilities. 513 */ 514 @Override 515 public void onConnectionCapabilitiesChanged(int capabilities) { 516 mHandler.obtainMessage(MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES, 517 capabilities, 0).sendToTarget(); 518 } 519 520 /** 521 * The {@link com.android.internal.telephony.Connection} has reported a change in the 522 * video call provider. 523 * 524 * @param videoProvider The video call provider. 525 */ 526 @Override 527 public void onVideoProviderChanged(VideoProvider videoProvider) { 528 mHandler.obtainMessage(MSG_SET_VIDEO_PROVIDER, videoProvider).sendToTarget(); 529 } 530 531 /** 532 * Used by {@link com.android.internal.telephony.Connection} to report a change for 533 * the call radio technology. 534 * 535 * @param vrat the RIL Voice Radio Technology used for current connection. 536 */ 537 @Override 538 public void onCallRadioTechChanged(@RilRadioTechnology int vrat) { 539 mHandler.obtainMessage(MSG_SET_CALL_RADIO_TECH, vrat).sendToTarget(); 540 } 541 542 /** 543 * Used by the {@link com.android.internal.telephony.Connection} to report a change in the 544 * audio quality for the current call. 545 * 546 * @param audioQuality The audio quality. 547 */ 548 @Override 549 public void onAudioQualityChanged(int audioQuality) { 550 mHandler.obtainMessage(MSG_SET_AUDIO_QUALITY, audioQuality).sendToTarget(); 551 } 552 /** 553 * Handles a change in the state of conference participant(s), as reported by the 554 * {@link com.android.internal.telephony.Connection}. 555 * 556 * @param participants The participant(s) which changed. 557 */ 558 @Override 559 public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants) { 560 mHandler.obtainMessage(MSG_SET_CONFERENCE_PARTICIPANTS, participants).sendToTarget(); 561 } 562 563 /* 564 * Handles a change to the multiparty state for this connection. 565 * 566 * @param isMultiParty {@code true} if the call became multiparty, {@code false} 567 * otherwise. 568 */ 569 @Override 570 public void onMultipartyStateChanged(boolean isMultiParty) { 571 handleMultipartyStateChange(isMultiParty); 572 } 573 574 /** 575 * Handles the event that the request to merge calls failed. 576 */ 577 @Override 578 public void onConferenceMergedFailed() { 579 handleConferenceMergeFailed(); 580 } 581 582 @Override 583 public void onExtrasChanged(Bundle extras) { 584 mHandler.obtainMessage(MSG_CONNECTION_EXTRAS_CHANGED, extras).sendToTarget(); 585 } 586 587 /** 588 * Handles the phone exiting ECM mode by updating the connection capabilities. During an 589 * ongoing call, if ECM mode is exited, we will re-enable mute for CDMA calls. 590 */ 591 @Override 592 public void onExitedEcmMode() { 593 handleExitedEcmMode(); 594 } 595 596 /** 597 * Called from {@link ImsPhoneCallTracker} when a request to pull an external call has 598 * failed. 599 * @param externalConnection 600 */ 601 @Override 602 public void onCallPullFailed(com.android.internal.telephony.Connection externalConnection) { 603 if (externalConnection == null) { 604 return; 605 } 606 607 Log.i(this, "onCallPullFailed - pull failed; swapping back to call: %s", 608 externalConnection); 609 610 // Inform the InCallService of the fact that the call pull failed (it may choose to 611 // display a message informing the user of the pull failure). 612 sendTelephonyConnectionEvent(Connection.EVENT_CALL_PULL_FAILED, null); 613 614 // Swap the ImsPhoneConnection we used to do the pull for the ImsExternalConnection 615 // which originally represented the call. 616 setOriginalConnection(externalConnection); 617 618 // Set our state to active again since we're no longer pulling. 619 setActiveInternal(); 620 } 621 622 /** 623 * Called from {@link ImsPhoneCallTracker} when a handover to WIFI has failed. 624 */ 625 @Override 626 public void onHandoverToWifiFailed() { 627 sendTelephonyConnectionEvent(TelephonyManager.EVENT_HANDOVER_TO_WIFI_FAILED, null); 628 } 629 630 /** 631 * Informs the {@link android.telecom.ConnectionService} of a connection event raised by the 632 * original connection. 633 * @param event The connection event. 634 * @param extras The extras. 635 */ 636 @Override 637 public void onConnectionEvent(String event, Bundle extras) { 638 SomeArgs args = SomeArgs.obtain(); 639 args.arg1 = event; 640 args.arg2 = extras; 641 mHandler.obtainMessage(MSG_ON_CONNECTION_EVENT, args).sendToTarget(); 642 } 643 644 @Override 645 public void onRttModifyRequestReceived() { 646 sendRemoteRttRequest(); 647 } 648 649 @Override 650 public void onRttModifyResponseReceived(int status) { 651 updateConnectionProperties(); 652 refreshConferenceSupported(); 653 if (status == RttModifyStatus.SESSION_MODIFY_REQUEST_SUCCESS) { 654 sendRttInitiationSuccess(); 655 } else { 656 sendRttInitiationFailure(status); 657 } 658 } 659 660 @Override 661 public void onDisconnect(int cause) { 662 Log.i(this, "onDisconnect: callId=%s, cause=%s", getTelecomCallId(), 663 DisconnectCause.toString(cause)); 664 mHandler.obtainMessage(MSG_DISCONNECT).sendToTarget(); 665 } 666 667 @Override 668 public void onRttInitiated() { 669 if (mOriginalConnection != null) { 670 // if mOriginalConnection is null, the properties will get set when 671 // mOriginalConnection gets set. 672 updateConnectionProperties(); 673 refreshConferenceSupported(); 674 } 675 sendRttInitiationSuccess(); 676 } 677 678 @Override 679 public void onRttTerminated() { 680 updateConnectionProperties(); 681 refreshConferenceSupported(); 682 sendRttSessionRemotelyTerminated(); 683 } 684 685 @Override 686 public void onOriginalConnectionReplaced( 687 com.android.internal.telephony.Connection newConnection) { 688 setOriginalConnection(newConnection); 689 } 690 691 @Override 692 public void onIsNetworkEmergencyCallChanged(boolean isEmergencyCall) { 693 setIsNetworkIdentifiedEmergencyCall(isEmergencyCall); 694 } 695 }; 696 697 private TelephonyConnectionService mTelephonyConnectionService; 698 protected com.android.internal.telephony.Connection mOriginalConnection; 699 private Call.State mConnectionState = Call.State.IDLE; 700 private Bundle mOriginalConnectionExtras = new Bundle(); 701 private boolean mIsStateOverridden = false; 702 private Call.State mOriginalConnectionState = Call.State.IDLE; 703 private Call.State mConnectionOverriddenState = Call.State.IDLE; 704 private RttTextStream mRttTextStream = null; 705 706 private boolean mWasImsConnection; 707 708 /** 709 * Tracks the multiparty state of the ImsCall so that changes in the bit state can be detected. 710 */ 711 private boolean mIsMultiParty = false; 712 713 /** 714 * The {@link com.android.internal.telephony.Connection} capabilities associated with the 715 * current {@link #mOriginalConnection}. 716 */ 717 private int mOriginalConnectionCapabilities; 718 719 /** 720 * Determines the audio quality is high for the {@link TelephonyConnection}. 721 * This is used when {@link TelephonyConnection#updateConnectionProperties}} is called to 722 * indicate whether a call has the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property. 723 */ 724 private boolean mHasHighDefAudio; 725 726 /** 727 * Indicates that the connection should be treated as an emergency call because the 728 * number dialed matches an internal list of emergency numbers. Does not guarantee whether 729 * the network will treat the call as an emergency call. 730 */ 731 private boolean mTreatAsEmergencyCall; 732 733 /** 734 * Indicates whether the network has identified this call as an emergency call. Where 735 * {@link #mTreatAsEmergencyCall} is based on comparing dialed numbers to a list of known 736 * emergency numbers, this property is based on whether the network itself has identified the 737 * call as an emergency call (which can be the case for an incoming call from emergency 738 * services). 739 */ 740 private boolean mIsNetworkIdentifiedEmergencyCall; 741 742 /** 743 * For video calls, indicates whether the outgoing video for the call can be paused using 744 * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState. 745 */ 746 private boolean mIsVideoPauseSupported; 747 748 /** 749 * Indicates whether this connection supports being a part of a conference.. 750 */ 751 private boolean mIsConferenceSupported; 752 753 /** 754 * Indicates whether managing conference call is supported after this connection being 755 * a part of a IMS conference. 756 */ 757 private boolean mIsManageImsConferenceCallSupported; 758 759 /** 760 * Indicates whether the carrier supports video conferencing; captures the current state of the 761 * carrier config 762 * {@link android.telephony.CarrierConfigManager#KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL}. 763 */ 764 private boolean mIsCarrierVideoConferencingSupported; 765 766 /** 767 * Indicates whether or not this connection has CDMA Enhanced Voice Privacy enabled. 768 */ 769 private boolean mIsCdmaVoicePrivacyEnabled; 770 771 /** 772 * Indicates whether the connection can be held. This filed combined with the state of the 773 * connection can determine whether {@link Connection#CAPABILITY_HOLD} should be added to the 774 * connection. 775 */ 776 private boolean mIsHoldable; 777 778 /** 779 * Indicates whether TTY is enabled; used to determine whether a call is VT capable. 780 */ 781 private boolean mIsTtyEnabled; 782 783 /** 784 * Indicates whether this call is using assisted dialing. 785 */ 786 private boolean mIsUsingAssistedDialing; 787 788 /** 789 * Indicates whether this connection supports showing preciese call failed cause. 790 */ 791 private boolean mShowPreciseFailedCause; 792 793 /** 794 * Provides a DisconnectCause associated with a hang up request. 795 */ 796 private int mHangupDisconnectCause = DisconnectCause.NOT_VALID; 797 798 /** 799 * Listeners to our TelephonyConnection specific callbacks 800 */ 801 private final Set<TelephonyConnectionListener> mTelephonyListeners = Collections.newSetFromMap( 802 new ConcurrentHashMap<TelephonyConnectionListener, Boolean>(8, 0.9f, 1)); 803 TelephonyConnection(com.android.internal.telephony.Connection originalConnection, String callId, @android.telecom.Call.Details.CallDirection int callDirection)804 protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection, 805 String callId, @android.telecom.Call.Details.CallDirection int callDirection) { 806 setCallDirection(callDirection); 807 setTelecomCallId(callId); 808 if (originalConnection != null) { 809 setOriginalConnection(originalConnection); 810 } 811 } 812 813 /** 814 * Creates a clone of the current {@link TelephonyConnection}. 815 * 816 * @return The clone. 817 */ cloneConnection()818 public abstract TelephonyConnection cloneConnection(); 819 820 @Override onCallAudioStateChanged(CallAudioState audioState)821 public void onCallAudioStateChanged(CallAudioState audioState) { 822 // TODO: update TTY mode. 823 if (getPhone() != null) { 824 getPhone().setEchoSuppressionEnabled(); 825 } 826 } 827 828 @Override onStateChanged(int state)829 public void onStateChanged(int state) { 830 Log.v(this, "onStateChanged, state: " + Connection.stateToString(state)); 831 updateStatusHints(); 832 } 833 834 @Override onDisconnect()835 public void onDisconnect() { 836 Log.v(this, "onDisconnect"); 837 mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget(); 838 } 839 840 /** 841 * Notifies this Connection of a request to disconnect a participant of the conference managed 842 * by the connection. 843 * 844 * @param endpoint the {@link Uri} of the participant to disconnect. 845 */ 846 @Override onDisconnectConferenceParticipant(Uri endpoint)847 public void onDisconnectConferenceParticipant(Uri endpoint) { 848 Log.v(this, "onDisconnectConferenceParticipant %s", endpoint); 849 850 if (mOriginalConnection == null) { 851 return; 852 } 853 854 mOriginalConnection.onDisconnectConferenceParticipant(endpoint); 855 } 856 857 @Override onSeparate()858 public void onSeparate() { 859 Log.v(this, "onSeparate"); 860 if (mOriginalConnection != null) { 861 try { 862 mOriginalConnection.separate(); 863 } catch (CallStateException e) { 864 Log.e(this, e, "Call to Connection.separate failed with exception"); 865 } 866 } 867 } 868 869 @Override onAddConferenceParticipants(List<Uri> participants)870 public void onAddConferenceParticipants(List<Uri> participants) { 871 performAddConferenceParticipants(participants); 872 } 873 874 @Override onAbort()875 public void onAbort() { 876 Log.v(this, "onAbort"); 877 mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget(); 878 } 879 880 @Override onHold()881 public void onHold() { 882 performHold(); 883 } 884 885 @Override onUnhold()886 public void onUnhold() { 887 performUnhold(); 888 } 889 890 @Override onAnswer(int videoState)891 public void onAnswer(int videoState) { 892 performAnswer(videoState); 893 } 894 895 @Override onDeflect(Uri address)896 public void onDeflect(Uri address) { 897 Log.v(this, "onDeflect"); 898 if (mOriginalConnection != null && isValidRingingCall()) { 899 if (address == null) { 900 Log.w(this, "call deflect address uri is null"); 901 return; 902 } 903 String scheme = address.getScheme(); 904 String deflectNumber = ""; 905 String uriString = address.getSchemeSpecificPart(); 906 if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) { 907 if (!PhoneAccount.SCHEME_TEL.equals(scheme)) { 908 Log.w(this, "onDeflect, address scheme is not of type tel instead: " + 909 scheme); 910 return; 911 } 912 if (PhoneNumberUtils.isUriNumber(uriString)) { 913 Log.w(this, "Invalid deflect address. Not a legal PSTN number."); 914 return; 915 } 916 deflectNumber = PhoneNumberUtils.convertAndStrip(uriString); 917 if (TextUtils.isEmpty(deflectNumber)) { 918 Log.w(this, "Empty deflect number obtained from address uri"); 919 return; 920 } 921 } else { 922 Log.w(this, "Cannot deflect to voicemail uri"); 923 return; 924 } 925 926 try { 927 mOriginalConnection.deflect(deflectNumber); 928 } catch (CallStateException e) { 929 Log.e(this, e, "Failed to deflect call."); 930 } 931 } 932 } 933 934 @Override onReject()935 public void onReject() { 936 performReject(android.telecom.Call.REJECT_REASON_DECLINED); 937 } 938 939 @Override onReject(@ndroid.telecom.Call.RejectReason int rejectReason)940 public void onReject(@android.telecom.Call.RejectReason int rejectReason) { 941 performReject(rejectReason); 942 } 943 performReject(int rejectReason)944 public void performReject(int rejectReason) { 945 Log.v(this, "performReject"); 946 if (isValidRingingCall()) { 947 mHandler.obtainMessage(MSG_REJECT, rejectReason) 948 .sendToTarget(); 949 } 950 super.onReject(); 951 } 952 953 @Override onTransfer(Uri number, boolean isConfirmationRequired)954 public void onTransfer(Uri number, boolean isConfirmationRequired) { 955 Log.v(this, "onTransfer"); 956 if (mOriginalConnection != null) { 957 if (number == null) { 958 Log.w(this, "call transfer uri is null"); 959 return; 960 } 961 String scheme = number.getScheme(); 962 String transferNumber = ""; 963 String uriString = number.getSchemeSpecificPart(); 964 if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) { 965 if (!PhoneAccount.SCHEME_TEL.equals(scheme)) { 966 Log.w(this, "onTransfer, number scheme is not of type tel instead: " 967 + scheme); 968 return; 969 } 970 if (PhoneNumberUtils.isUriNumber(uriString)) { 971 Log.w(this, "Invalid transfer address. Not a legal PSTN number."); 972 return; 973 } 974 transferNumber = PhoneNumberUtils.convertAndStrip(uriString); 975 if (TextUtils.isEmpty(transferNumber)) { 976 Log.w(this, "Empty transfer number obtained from uri"); 977 return; 978 } 979 } else { 980 Log.w(this, "Cannot transfer to voicemail uri"); 981 return; 982 } 983 984 try { 985 mOriginalConnection.transfer(transferNumber, isConfirmationRequired); 986 } catch (CallStateException e) { 987 Log.e(this, e, "Failed to transfer call."); 988 } 989 } 990 } 991 992 @Override onTransfer(Connection otherConnection)993 public void onTransfer(Connection otherConnection) { 994 Log.v(this, "onConsultativeTransfer"); 995 if (mOriginalConnection != null && (otherConnection instanceof TelephonyConnection)) { 996 try { 997 mOriginalConnection.consultativeTransfer( 998 ((TelephonyConnection) otherConnection).getOriginalConnection()); 999 } catch (CallStateException e) { 1000 Log.e(this, e, "Failed to transfer call."); 1001 } 1002 } 1003 } 1004 1005 @Override onPostDialContinue(boolean proceed)1006 public void onPostDialContinue(boolean proceed) { 1007 Log.v(this, "onPostDialContinue, proceed: " + proceed); 1008 if (mOriginalConnection != null) { 1009 if (proceed) { 1010 mOriginalConnection.proceedAfterWaitChar(); 1011 } else { 1012 mOriginalConnection.cancelPostDial(); 1013 } 1014 } 1015 } 1016 1017 /** 1018 * Handles requests to pull an external call. 1019 */ 1020 @Override onPullExternalCall()1021 public void onPullExternalCall() { 1022 if ((getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) != 1023 Connection.PROPERTY_IS_EXTERNAL_CALL) { 1024 Log.w(this, "onPullExternalCall - cannot pull non-external call"); 1025 return; 1026 } 1027 1028 if (mOriginalConnection != null) { 1029 mOriginalConnection.pullExternalCall(); 1030 } 1031 } 1032 1033 @Override onStartRtt(RttTextStream textStream)1034 public void onStartRtt(RttTextStream textStream) { 1035 if (isImsConnection()) { 1036 ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection; 1037 if (originalConnection.isRttEnabledForCall()) { 1038 originalConnection.setCurrentRttTextStream(textStream); 1039 } else { 1040 originalConnection.startRtt(textStream); 1041 } 1042 } else { 1043 Log.w(this, "onStartRtt - not in IMS, so RTT cannot be enabled."); 1044 } 1045 } 1046 1047 @Override onStopRtt()1048 public void onStopRtt() { 1049 if (isImsConnection()) { 1050 ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection; 1051 if (originalConnection.isRttEnabledForCall()) { 1052 originalConnection.stopRtt(); 1053 } else { 1054 Log.w(this, "onStopRtt - not in RTT call, ignoring"); 1055 } 1056 } else { 1057 Log.w(this, "onStopRtt - not in IMS, ignoring"); 1058 } 1059 } 1060 1061 @Override handleRttUpgradeResponse(RttTextStream textStream)1062 public void handleRttUpgradeResponse(RttTextStream textStream) { 1063 if (!isImsConnection()) { 1064 Log.w(this, "handleRttUpgradeResponse - not in IMS, so RTT cannot be enabled."); 1065 return; 1066 } 1067 ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection; 1068 originalConnection.sendRttModifyResponse(textStream); 1069 } 1070 performAnswer(int videoState)1071 public void performAnswer(int videoState) { 1072 Log.v(this, "performAnswer"); 1073 if (isValidRingingCall() && getPhone() != null) { 1074 try { 1075 getPhone().acceptCall(videoState); 1076 } catch (CallStateException e) { 1077 Log.e(this, e, "Failed to accept call."); 1078 } 1079 } 1080 } 1081 performHold()1082 public void performHold() { 1083 Log.v(this, "performHold"); 1084 // TODO: Can dialing calls be put on hold as well since they take up the 1085 // foreground call slot? 1086 if (Call.State.ACTIVE == mConnectionState) { 1087 Log.v(this, "Holding active call"); 1088 try { 1089 Phone phone = mOriginalConnection.getCall().getPhone(); 1090 1091 Call ringingCall = phone.getRingingCall(); 1092 1093 // Although the method says switchHoldingAndActive, it eventually calls a RIL method 1094 // called switchWaitingOrHoldingAndActive. What this means is that if we try to put 1095 // a call on hold while a call-waiting call exists, it'll end up accepting the 1096 // call-waiting call, which is bad if that was not the user's intention. We are 1097 // cheating here and simply skipping it because we know any attempt to hold a call 1098 // while a call-waiting call is happening is likely a request from Telecom prior to 1099 // accepting the call-waiting call. 1100 // TODO: Investigate a better solution. It would be great here if we 1101 // could "fake" hold by silencing the audio and microphone streams for this call 1102 // instead of actually putting it on hold. 1103 if (ringingCall.getState() != Call.State.WAITING) { 1104 // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic. 1105 if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) { 1106 ImsPhone imsPhone = (ImsPhone) phone; 1107 imsPhone.holdActiveCall(); 1108 return; 1109 } 1110 phone.switchHoldingAndActive(); 1111 } 1112 1113 // TODO: Cdma calls are slightly different. 1114 } catch (CallStateException e) { 1115 Log.e(this, e, "Exception occurred while trying to put call on hold."); 1116 } 1117 } else { 1118 Log.w(this, "Cannot put a call that is not currently active on hold."); 1119 } 1120 } 1121 performUnhold()1122 public void performUnhold() { 1123 Log.v(this, "performUnhold"); 1124 if (Call.State.HOLDING == mConnectionState) { 1125 try { 1126 Phone phone = mOriginalConnection.getCall().getPhone(); 1127 // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic. 1128 if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) { 1129 ImsPhone imsPhone = (ImsPhone) phone; 1130 imsPhone.unholdHeldCall(); 1131 return; 1132 } 1133 // Here's the deal--Telephony hold/unhold is weird because whenever there exists 1134 // more than one call, one of them must always be active. In other words, if you 1135 // have an active call and holding call, and you put the active call on hold, it 1136 // will automatically activate the holding call. This is weird with how Telecom 1137 // sends its commands. When a user opts to "unhold" a background call, telecom 1138 // issues hold commands to all active calls, and then the unhold command to the 1139 // background call. This means that we get two commands...each of which reduces to 1140 // switchHoldingAndActive(). The result is that they simply cancel each other out. 1141 // To fix this so that it works well with telecom we add a minor hack. If we 1142 // have one telephony call, everything works as normally expected. But if we have 1143 // two or more calls, we will ignore all requests to "unhold" knowing that the hold 1144 // requests already do what we want. If you've read up to this point, I'm very sorry 1145 // that we are doing this. I didn't think of a better solution that wouldn't also 1146 // make the Telecom APIs very ugly. 1147 1148 if (!hasMultipleTopLevelCalls()) { 1149 mOriginalConnection.getCall().getPhone().switchHoldingAndActive(); 1150 } else { 1151 Log.i(this, "Skipping unhold command for %s", this); 1152 } 1153 } catch (CallStateException e) { 1154 Log.e(this, e, "Exception occurred while trying to release call from hold."); 1155 } 1156 } else { 1157 Log.w(this, "Cannot release a call that is not already on hold from hold."); 1158 } 1159 } 1160 performConference(Connection otherConnection)1161 public void performConference(Connection otherConnection) { 1162 Log.d(this, "performConference - %s", this); 1163 if (getPhone() != null) { 1164 try { 1165 // We dont use the "other" connection because there is no concept of that in the 1166 // implementation of calls inside telephony. Basically, you can "conference" and it 1167 // will conference with the background call. We know that otherConnection is the 1168 // background call because it would never have called setConferenceableConnections() 1169 // otherwise. 1170 getPhone().conference(); 1171 } catch (CallStateException e) { 1172 Log.e(this, e, "Failed to conference call."); 1173 } 1174 } 1175 } 1176 getAddConferenceParticipants(List<Uri> participants)1177 private String[] getAddConferenceParticipants(List<Uri> participants) { 1178 String[] addConfParticipants = new String[participants.size()]; 1179 int i = 0; 1180 for (Uri participant : participants) { 1181 addConfParticipants[i] = participant.getSchemeSpecificPart(); 1182 i++; 1183 } 1184 return addConfParticipants; 1185 } 1186 performAddConferenceParticipants(List<Uri> participants)1187 public void performAddConferenceParticipants(List<Uri> participants) { 1188 Log.v(this, "performAddConferenceParticipants"); 1189 if (mOriginalConnection.getCall() instanceof ImsPhoneCall) { 1190 ImsPhoneCall imsPhoneCall = (ImsPhoneCall)mOriginalConnection.getCall(); 1191 try { 1192 imsPhoneCall.getImsCall().inviteParticipants( 1193 getAddConferenceParticipants(participants)); 1194 } catch(ImsException e) { 1195 Log.e(this, e, "failed to add conference participants"); 1196 } 1197 } 1198 } 1199 1200 /** 1201 * Builds connection capabilities common to all TelephonyConnections. Namely, apply IMS-based 1202 * capabilities. 1203 */ buildConnectionCapabilities()1204 protected int buildConnectionCapabilities() { 1205 int callCapabilities = 0; 1206 if (mOriginalConnection != null && mOriginalConnection.isIncoming()) { 1207 callCapabilities |= CAPABILITY_SPEED_UP_MT_AUDIO; 1208 } 1209 if (!shouldTreatAsEmergencyCall() && isImsConnection() && canHoldImsCalls()) { 1210 callCapabilities |= CAPABILITY_SUPPORT_HOLD; 1211 if (mIsHoldable && (getState() == STATE_ACTIVE || getState() == STATE_HOLDING)) { 1212 callCapabilities |= CAPABILITY_HOLD; 1213 } 1214 } 1215 1216 Log.d(this, "buildConnectionCapabilities: isHoldable = " 1217 + mIsHoldable + " State = " + getState() + " capabilities = " + callCapabilities); 1218 1219 return callCapabilities; 1220 } 1221 updateConnectionCapabilities()1222 protected final void updateConnectionCapabilities() { 1223 int newCapabilities = buildConnectionCapabilities(); 1224 1225 newCapabilities = applyOriginalConnectionCapabilities(newCapabilities); 1226 newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PAUSE_VIDEO, 1227 mIsVideoPauseSupported && isVideoCapable()); 1228 newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PULL_CALL, 1229 isExternalConnection() && isPullable()); 1230 newCapabilities = applyConferenceTerminationCapabilities(newCapabilities); 1231 newCapabilities = changeBitmask(newCapabilities, CAPABILITY_SUPPORT_DEFLECT, 1232 isImsConnection() && canDeflectImsCalls()); 1233 1234 newCapabilities = applyAddParticipantCapabilities(newCapabilities); 1235 newCapabilities = changeBitmask(newCapabilities, CAPABILITY_TRANSFER_CONSULTATIVE, 1236 isImsConnection() && canConsultativeTransfer()); 1237 newCapabilities = changeBitmask(newCapabilities, CAPABILITY_TRANSFER, 1238 isImsConnection() && canTransferToNumber()); 1239 1240 if (getConnectionCapabilities() != newCapabilities) { 1241 setConnectionCapabilities(newCapabilities); 1242 notifyConnectionCapabilitiesChanged(newCapabilities); 1243 } 1244 } 1245 buildConnectionProperties()1246 protected int buildConnectionProperties() { 1247 int connectionProperties = 0; 1248 1249 // If the phone is in ECM mode, mark the call to indicate that the callback number should be 1250 // shown. 1251 Phone phone = getPhone(); 1252 if (phone != null && phone.isInEcm()) { 1253 connectionProperties |= PROPERTY_EMERGENCY_CALLBACK_MODE; 1254 } 1255 1256 return connectionProperties; 1257 } 1258 1259 /** 1260 * Updates the properties of the connection. 1261 */ updateConnectionProperties()1262 protected final void updateConnectionProperties() { 1263 int newProperties = buildConnectionProperties(); 1264 1265 newProperties = changeBitmask(newProperties, PROPERTY_HIGH_DEF_AUDIO, 1266 hasHighDefAudioProperty()); 1267 newProperties = changeBitmask(newProperties, PROPERTY_WIFI, isWifi()); 1268 newProperties = changeBitmask(newProperties, PROPERTY_IS_EXTERNAL_CALL, 1269 isExternalConnection()); 1270 newProperties = changeBitmask(newProperties, PROPERTY_HAS_CDMA_VOICE_PRIVACY, 1271 mIsCdmaVoicePrivacyEnabled); 1272 newProperties = changeBitmask(newProperties, PROPERTY_ASSISTED_DIALING, 1273 mIsUsingAssistedDialing); 1274 newProperties = changeBitmask(newProperties, PROPERTY_IS_RTT, isRtt()); 1275 newProperties = changeBitmask(newProperties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL, 1276 isNetworkIdentifiedEmergencyCall()); 1277 newProperties = changeBitmask(newProperties, PROPERTY_IS_ADHOC_CONFERENCE, 1278 isAdhocConferenceCall()); 1279 1280 if (getConnectionProperties() != newProperties) { 1281 setTelephonyConnectionProperties(newProperties); 1282 } 1283 } 1284 setTelephonyConnectionProperties(int newProperties)1285 public void setTelephonyConnectionProperties(int newProperties) { 1286 setConnectionProperties(newProperties); 1287 notifyConnectionPropertiesChanged(newProperties); 1288 } 1289 updateAddress()1290 protected final void updateAddress() { 1291 updateConnectionCapabilities(); 1292 updateConnectionProperties(); 1293 if (mOriginalConnection != null) { 1294 Uri address; 1295 if (isShowingOriginalDialString() 1296 && mOriginalConnection.getOrigDialString() != null) { 1297 address = getAddressFromNumber(mOriginalConnection.getOrigDialString()); 1298 } else { 1299 address = getAddressFromNumber(mOriginalConnection.getAddress()); 1300 } 1301 int presentation = mOriginalConnection.getNumberPresentation(); 1302 if (!Objects.equals(address, getAddress()) || 1303 presentation != getAddressPresentation()) { 1304 Log.v(this, "updateAddress, address changed"); 1305 if ((getConnectionProperties() & PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0) { 1306 address = null; 1307 } 1308 setAddress(address, presentation); 1309 } 1310 1311 String name = filterCnapName(mOriginalConnection.getCnapName()); 1312 int namePresentation = mOriginalConnection.getCnapNamePresentation(); 1313 if (!Objects.equals(name, getCallerDisplayName()) || 1314 namePresentation != getCallerDisplayNamePresentation()) { 1315 Log.v(this, "updateAddress, caller display name changed"); 1316 setCallerDisplayName(name, namePresentation); 1317 } 1318 1319 if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) { 1320 mTreatAsEmergencyCall = true; 1321 } 1322 1323 // Changing the address of the connection can change whether it is an emergency call or 1324 // not, which can impact whether it can be part of a conference. 1325 refreshConferenceSupported(); 1326 } 1327 } 1328 onRemovedFromCallService()1329 void onRemovedFromCallService() { 1330 // Subclass can override this to do cleanup. 1331 } 1332 setOriginalConnection(com.android.internal.telephony.Connection originalConnection)1333 void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) { 1334 Log.v(this, "new TelephonyConnection, originalConnection: " + originalConnection); 1335 if (mOriginalConnection != null && originalConnection != null 1336 && !originalConnection.isIncoming() 1337 && originalConnection.getOrigDialString() == null 1338 && isShowingOriginalDialString()) { 1339 Log.i(this, "new original dial string is null, convert to: " 1340 + mOriginalConnection.getOrigDialString()); 1341 originalConnection.restoreDialedNumberAfterConversion( 1342 mOriginalConnection.getOrigDialString()); 1343 } 1344 1345 clearOriginalConnection(); 1346 mOriginalConnectionExtras.clear(); 1347 mOriginalConnection = originalConnection; 1348 mOriginalConnection.setTelecomCallId(getTelecomCallId()); 1349 getPhone().registerForPreciseCallStateChanged( 1350 mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null); 1351 getPhone().registerForHandoverStateChanged( 1352 mHandler, MSG_HANDOVER_STATE_CHANGED, null); 1353 getPhone().registerForRedialConnectionChanged( 1354 mHandler, MSG_REDIAL_CONNECTION_CHANGED, null); 1355 getPhone().registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null); 1356 getPhone().registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null); 1357 getPhone().registerForOnHoldTone(mHandler, MSG_ON_HOLD_TONE, null); 1358 getPhone().registerForInCallVoicePrivacyOn(mHandler, MSG_CDMA_VOICE_PRIVACY_ON, null); 1359 getPhone().registerForInCallVoicePrivacyOff(mHandler, MSG_CDMA_VOICE_PRIVACY_OFF, null); 1360 mOriginalConnection.addPostDialListener(mPostDialListener); 1361 mOriginalConnection.addListener(mOriginalConnectionListener); 1362 1363 // Set video state and capabilities 1364 setTelephonyVideoState(mOriginalConnection.getVideoState()); 1365 setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities()); 1366 setIsNetworkIdentifiedEmergencyCall(mOriginalConnection.isNetworkIdentifiedEmergencyCall()); 1367 setIsAdhocConferenceCall(mOriginalConnection.isAdhocConference()); 1368 setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip()); 1369 setTelephonyVideoProvider(mOriginalConnection.getVideoProvider()); 1370 setAudioQuality(mOriginalConnection.getAudioQuality()); 1371 setTechnologyTypeExtra(); 1372 1373 setCallRadioTech(mOriginalConnection.getCallRadioTech()); 1374 1375 // Post update of extras to the handler; extras are updated via the handler to ensure thread 1376 // safety. The Extras Bundle is cloned in case the original extras are modified while they 1377 // are being added to mOriginalConnectionExtras in updateExtras. 1378 Bundle connExtras = mOriginalConnection.getConnectionExtras(); 1379 mHandler.obtainMessage(MSG_CONNECTION_EXTRAS_CHANGED, connExtras == null ? null : 1380 new Bundle(connExtras)).sendToTarget(); 1381 1382 if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) { 1383 mTreatAsEmergencyCall = true; 1384 } 1385 // Propagate VERSTAT for IMS calls. 1386 setCallerNumberVerificationStatus(mOriginalConnection.getNumberVerificationStatus()); 1387 1388 if (isImsConnection()) { 1389 mWasImsConnection = true; 1390 } 1391 mIsMultiParty = mOriginalConnection.isMultiparty(); 1392 1393 Bundle extrasToPut = new Bundle(); 1394 List<String> extrasToRemove = new ArrayList<>(); 1395 if (mOriginalConnection.isActiveCallDisconnectedOnAnswer()) { 1396 extrasToPut.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true); 1397 } else { 1398 extrasToRemove.add(Connection.EXTRA_ANSWERING_DROPS_FG_CALL); 1399 } 1400 1401 if (shouldSetDisableAddCallExtra()) { 1402 extrasToPut.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true); 1403 } else { 1404 extrasToRemove.add(Connection.EXTRA_DISABLE_ADD_CALL); 1405 } 1406 putTelephonyExtras(extrasToPut); 1407 removeTelephonyExtras(extrasToRemove); 1408 1409 // updateState can set mOriginalConnection to null if its state is DISCONNECTED, so this 1410 // should be executed *after* the above setters have run. 1411 updateState(); 1412 if (mOriginalConnection == null) { 1413 Log.w(this, "original Connection was nulled out as part of setOriginalConnection. " + 1414 originalConnection); 1415 } 1416 1417 fireOnOriginalConnectionConfigured(); 1418 } 1419 1420 /** 1421 * Filters the CNAP name to not include a list of names that are unhelpful to the user for 1422 * Caller ID purposes. 1423 */ filterCnapName(final String cnapName)1424 private String filterCnapName(final String cnapName) { 1425 if (cnapName == null) { 1426 return null; 1427 } 1428 PersistableBundle carrierConfig = getCarrierConfig(); 1429 String[] filteredCnapNames = null; 1430 if (carrierConfig != null) { 1431 filteredCnapNames = carrierConfig.getStringArray( 1432 CarrierConfigManager.KEY_FILTERED_CNAP_NAMES_STRING_ARRAY); 1433 } 1434 if (filteredCnapNames != null) { 1435 long cnapNameMatches = Arrays.asList(filteredCnapNames) 1436 .stream() 1437 .filter(filteredCnapName -> filteredCnapName.equals(cnapName.toUpperCase())) 1438 .count(); 1439 if (cnapNameMatches > 0) { 1440 Log.i(this, "filterCnapName: Filtered CNAP Name: " + cnapName); 1441 return ""; 1442 } 1443 } 1444 return cnapName; 1445 } 1446 1447 /** 1448 * Sets the EXTRA_CALL_TECHNOLOGY_TYPE extra on the connection to report back to Telecom. 1449 */ setTechnologyTypeExtra()1450 private void setTechnologyTypeExtra() { 1451 if (getPhone() != null) { 1452 Bundle newExtras = getExtras(); 1453 if (newExtras == null) { 1454 newExtras = new Bundle(); 1455 } 1456 newExtras.putInt(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE, getPhone().getPhoneType()); 1457 putTelephonyExtras(newExtras); 1458 } 1459 } 1460 refreshHoldSupported()1461 private void refreshHoldSupported() { 1462 if (mOriginalConnection == null) { 1463 Log.w(this, "refreshHoldSupported org conn is null"); 1464 return; 1465 } 1466 1467 if (!mOriginalConnection.shouldAllowHoldingVideoCall() && canHoldImsCalls() != 1468 ((getConnectionCapabilities() & (CAPABILITY_HOLD | CAPABILITY_SUPPORT_HOLD)) != 0)) { 1469 updateConnectionCapabilities(); 1470 } 1471 } 1472 refreshDisableAddCall()1473 private void refreshDisableAddCall() { 1474 if (shouldSetDisableAddCallExtra()) { 1475 Bundle newExtras = getExtras(); 1476 if (newExtras == null) { 1477 newExtras = new Bundle(); 1478 } 1479 newExtras.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true); 1480 putTelephonyExtras(newExtras); 1481 } else { 1482 removeExtras(Connection.EXTRA_DISABLE_ADD_CALL); 1483 } 1484 } 1485 refreshCodecType()1486 private void refreshCodecType() { 1487 Bundle newExtras = getExtras(); 1488 if (newExtras == null) { 1489 newExtras = new Bundle(); 1490 } 1491 int newCodecType; 1492 if (isImsConnection()) { 1493 newCodecType = transformCodec(getOriginalConnection().getAudioCodec()); 1494 } else { 1495 // For SRVCC, report AUDIO_CODEC_NONE. 1496 newCodecType = Connection.AUDIO_CODEC_NONE; 1497 } 1498 int oldCodecType = newExtras.getInt(Connection.EXTRA_AUDIO_CODEC, 1499 Connection.AUDIO_CODEC_NONE); 1500 if (newCodecType != oldCodecType) { 1501 newExtras.putInt(Connection.EXTRA_AUDIO_CODEC, newCodecType); 1502 putTelephonyExtras(newExtras); 1503 } 1504 } 1505 transformCodec(int codec)1506 private int transformCodec(int codec) { 1507 switch (codec) { 1508 case ImsStreamMediaProfile.AUDIO_QUALITY_NONE: 1509 return Connection.AUDIO_CODEC_NONE; 1510 case ImsStreamMediaProfile.AUDIO_QUALITY_AMR: 1511 return Connection.AUDIO_CODEC_AMR; 1512 case ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB: 1513 return Connection.AUDIO_CODEC_AMR_WB; 1514 case ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K: 1515 return Connection.AUDIO_CODEC_QCELP13K; 1516 case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC: 1517 return Connection.AUDIO_CODEC_EVRC; 1518 case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B: 1519 return Connection.AUDIO_CODEC_EVRC_B; 1520 case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB: 1521 return Connection.AUDIO_CODEC_EVRC_WB; 1522 case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW: 1523 return Connection.AUDIO_CODEC_EVRC_NW; 1524 case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR: 1525 return Connection.AUDIO_CODEC_GSM_EFR; 1526 case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR: 1527 return Connection.AUDIO_CODEC_GSM_FR; 1528 case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR: 1529 return Connection.AUDIO_CODEC_GSM_HR; 1530 case ImsStreamMediaProfile.AUDIO_QUALITY_G711U: 1531 return Connection.AUDIO_CODEC_G711U; 1532 case ImsStreamMediaProfile.AUDIO_QUALITY_G723: 1533 return Connection.AUDIO_CODEC_G723; 1534 case ImsStreamMediaProfile.AUDIO_QUALITY_G711A: 1535 return Connection.AUDIO_CODEC_G711A; 1536 case ImsStreamMediaProfile.AUDIO_QUALITY_G722: 1537 return Connection.AUDIO_CODEC_G722; 1538 case ImsStreamMediaProfile.AUDIO_QUALITY_G711AB: 1539 return Connection.AUDIO_CODEC_G711AB; 1540 case ImsStreamMediaProfile.AUDIO_QUALITY_G729: 1541 return Connection.AUDIO_CODEC_G729; 1542 case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB: 1543 return Connection.AUDIO_CODEC_EVS_NB; 1544 case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB: 1545 return Connection.AUDIO_CODEC_EVS_WB; 1546 case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB: 1547 return Connection.AUDIO_CODEC_EVS_SWB; 1548 case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB: 1549 return Connection.AUDIO_CODEC_EVS_FB; 1550 default: 1551 return Connection.AUDIO_CODEC_NONE; 1552 } 1553 } 1554 shouldSetDisableAddCallExtra()1555 private boolean shouldSetDisableAddCallExtra() { 1556 if (mOriginalConnection == null) { 1557 return false; 1558 } 1559 boolean carrierShouldAllowAddCall = mOriginalConnection.shouldAllowAddCallDuringVideoCall(); 1560 if (carrierShouldAllowAddCall) { 1561 return false; 1562 } 1563 Phone phone = getPhone(); 1564 if (phone == null) { 1565 return false; 1566 } 1567 boolean isCurrentVideoCall = false; 1568 boolean wasVideoCall = false; 1569 boolean isVowifiEnabled = false; 1570 if (phone instanceof ImsPhone) { 1571 ImsPhoneCall foregroundCall = ((ImsPhone) phone).getForegroundCall(); 1572 if (foregroundCall != null) { 1573 ImsCall call = foregroundCall.getImsCall(); 1574 if (call != null) { 1575 isCurrentVideoCall = call.isVideoCall(); 1576 wasVideoCall = call.wasVideoCall(); 1577 } 1578 } 1579 1580 isVowifiEnabled = ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId()); 1581 } 1582 1583 if (isCurrentVideoCall) { 1584 return true; 1585 } else if (wasVideoCall && isWifi() && !isVowifiEnabled) { 1586 return true; 1587 } 1588 return false; 1589 } 1590 hasHighDefAudioProperty()1591 private boolean hasHighDefAudioProperty() { 1592 if (!mHasHighDefAudio) { 1593 return false; 1594 } 1595 1596 boolean isVideoCall = VideoProfile.isVideo(getVideoState()); 1597 1598 PersistableBundle b = getCarrierConfig(); 1599 boolean canWifiCallsBeHdAudio = 1600 b != null && b.getBoolean(CarrierConfigManager.KEY_WIFI_CALLS_CAN_BE_HD_AUDIO); 1601 boolean canVideoCallsBeHdAudio = 1602 b != null && b.getBoolean(CarrierConfigManager.KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO); 1603 boolean canGsmCdmaCallsBeHdAudio = 1604 b != null && b.getBoolean(CarrierConfigManager.KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO); 1605 boolean shouldDisplayHdAudio = 1606 b != null && b.getBoolean(CarrierConfigManager.KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL); 1607 1608 if (!shouldDisplayHdAudio) { 1609 return false; 1610 } 1611 1612 if (isGsmCdmaConnection() && !canGsmCdmaCallsBeHdAudio) { 1613 return false; 1614 } 1615 1616 if (isVideoCall && !canVideoCallsBeHdAudio) { 1617 return false; 1618 } 1619 1620 if (isWifi() && !canWifiCallsBeHdAudio) { 1621 return false; 1622 } 1623 1624 return true; 1625 } 1626 1627 /** 1628 * @return The address's to which this Connection is currently communicating. 1629 */ getParticipants()1630 public final @Nullable List<Uri> getParticipants() { 1631 return mParticipants; 1632 } 1633 1634 /** 1635 * Sets the value of the {@link #getParticipants()} property. 1636 * 1637 * @param address The participant address's. 1638 */ setParticipants(@ullable List<Uri> address)1639 public final void setParticipants(@Nullable List<Uri> address) { 1640 mParticipants = address; 1641 } 1642 1643 /** 1644 * @return true if connection is adhocConference call else false. 1645 */ isAdhocConferenceCall()1646 public final boolean isAdhocConferenceCall() { 1647 return mIsAdhocConferenceCall; 1648 } 1649 1650 /** 1651 * Sets the value of the {@link #isAdhocConferenceCall()} property. 1652 * 1653 * @param isAdhocConferenceCall represents if the call is adhoc conference call or not. 1654 */ setIsAdhocConferenceCall(boolean isAdhocConferenceCall)1655 public void setIsAdhocConferenceCall(boolean isAdhocConferenceCall) { 1656 mIsAdhocConferenceCall = isAdhocConferenceCall; 1657 updateConnectionProperties(); 1658 } 1659 canHoldImsCalls()1660 private boolean canHoldImsCalls() { 1661 PersistableBundle b = getCarrierConfig(); 1662 // Return true if the CarrierConfig is unavailable 1663 return (!doesDeviceRespectHoldCarrierConfig() || b == null || 1664 b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL)) && 1665 ((mOriginalConnection != null && mOriginalConnection.shouldAllowHoldingVideoCall()) 1666 || !VideoProfile.isVideo(getVideoState())); 1667 } 1668 isConferenceHosted()1669 private boolean isConferenceHosted() { 1670 boolean isHosted = false; 1671 if (getTelephonyConnectionService() != null) { 1672 for (Conference current : getTelephonyConnectionService().getAllConferences()) { 1673 if (current instanceof ImsConference) { 1674 ImsConference other = (ImsConference) current; 1675 if (getState() == current.getState()) { 1676 continue; 1677 } 1678 if (other.isConferenceHost()) { 1679 isHosted = true; 1680 break; 1681 } 1682 } 1683 } 1684 } 1685 return isHosted; 1686 } 1687 isAddParticipantCapable()1688 private boolean isAddParticipantCapable() { 1689 // not add participant capable for non ims phones 1690 if (getPhone() == null || getPhone().getPhoneType() != PhoneConstants.PHONE_TYPE_IMS) { 1691 return false; 1692 } 1693 1694 if (!getCarrierConfig() 1695 .getBoolean(CarrierConfigManager.KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL)) { 1696 return false; 1697 } 1698 1699 boolean isCapable = !mTreatAsEmergencyCall && (mConnectionState == Call.State.ACTIVE || 1700 mConnectionState == Call.State.HOLDING); 1701 1702 // add participant capable if current connection is a host connection or 1703 // if conference is not hosted on the device 1704 isCapable = isCapable && ((mOriginalConnection != null && 1705 mOriginalConnection.isConferenceHost()) || 1706 !isConferenceHosted()); 1707 1708 /** 1709 * For individual IMS calls, if the extra for remote conference support is 1710 * - indicated, then consider the same for add participant capability 1711 * - not indicated, then the add participant capability is same as before. 1712 */ 1713 if (isCapable && (mOriginalConnection != null) && !mIsMultiParty) { 1714 isCapable = mOriginalConnectionExtras.getBoolean( 1715 ImsCallProfile.EXTRA_CONFERENCE_AVAIL, isCapable); 1716 } 1717 return isCapable; 1718 } 1719 1720 /** 1721 * Applies the add participant capabilities to the {@code CallCapabilities} bit-mask. 1722 * 1723 * @param callCapabilities The {@code CallCapabilities} bit-mask. 1724 * @return The capabilities with the add participant capabilities applied. 1725 */ applyAddParticipantCapabilities(int callCapabilities)1726 private int applyAddParticipantCapabilities(int callCapabilities) { 1727 int currentCapabilities = callCapabilities; 1728 if (isAddParticipantCapable()) { 1729 currentCapabilities = changeBitmask(currentCapabilities, 1730 Connection.CAPABILITY_ADD_PARTICIPANT, true); 1731 } else { 1732 currentCapabilities = changeBitmask(currentCapabilities, 1733 Connection.CAPABILITY_ADD_PARTICIPANT, false); 1734 } 1735 return currentCapabilities; 1736 } 1737 1738 @VisibleForTesting getCarrierConfig()1739 public PersistableBundle getCarrierConfig() { 1740 Phone phone = getPhone(); 1741 if (phone == null) { 1742 return null; 1743 } 1744 return PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId()); 1745 } 1746 canDeflectImsCalls()1747 private boolean canDeflectImsCalls() { 1748 PersistableBundle b = getCarrierConfig(); 1749 // Return false if the CarrierConfig is unavailable 1750 if (b != null) { 1751 return b.getBoolean( 1752 CarrierConfigManager.KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL) && 1753 isValidRingingCall(); 1754 } 1755 return false; 1756 } 1757 isCallTransferSupported()1758 private boolean isCallTransferSupported() { 1759 PersistableBundle b = getCarrierConfig(); 1760 // Return false if the CarrierConfig is unavailable 1761 if (b != null) { 1762 return b.getBoolean(CarrierConfigManager.KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL); 1763 } 1764 return false; 1765 } 1766 canTransfer(TelephonyConnection c)1767 private boolean canTransfer(TelephonyConnection c) { 1768 com.android.internal.telephony.Connection connection = c.getOriginalConnection(); 1769 return (connection != null && !connection.isMultiparty() 1770 && (c.getState() == STATE_ACTIVE || c.getState() == STATE_HOLDING)); 1771 } 1772 canTransferToNumber()1773 private boolean canTransferToNumber() { 1774 if (!isCallTransferSupported()) { 1775 return false; 1776 } 1777 return canTransfer(this); 1778 } 1779 canConsultativeTransfer()1780 private boolean canConsultativeTransfer() { 1781 if (!isCallTransferSupported()) { 1782 return false; 1783 } 1784 if (!canTransfer(this)) { 1785 return false; 1786 } 1787 boolean canConsultativeTransfer = false; 1788 if (getTelephonyConnectionService() != null) { 1789 for (Connection current : getTelephonyConnectionService().getAllConnections()) { 1790 if (current != this && current instanceof TelephonyConnection) { 1791 TelephonyConnection other = (TelephonyConnection) current; 1792 if (canTransfer(other)) { 1793 canConsultativeTransfer = true; 1794 break; 1795 } 1796 } 1797 } 1798 } 1799 return canConsultativeTransfer; 1800 } 1801 1802 /** 1803 * Determines if the device will respect the value of the 1804 * {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} configuration option. 1805 * 1806 * @return {@code false} if the device always supports holding IMS calls, {@code true} if it 1807 * will use {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} to determine if 1808 * hold is supported. 1809 */ doesDeviceRespectHoldCarrierConfig()1810 private boolean doesDeviceRespectHoldCarrierConfig() { 1811 Phone phone = getPhone(); 1812 if (phone == null) { 1813 return true; 1814 } 1815 return phone.getContext().getResources().getBoolean( 1816 com.android.internal.R.bool.config_device_respects_hold_carrier_config); 1817 } 1818 1819 /** 1820 * Whether the connection should be treated as an emergency. 1821 * @return {@code true} if the connection should be treated as an emergency call based 1822 * on the number dialed, {@code false} otherwise. 1823 */ shouldTreatAsEmergencyCall()1824 protected boolean shouldTreatAsEmergencyCall() { 1825 return mTreatAsEmergencyCall; 1826 } 1827 1828 /** 1829 * Un-sets the underlying radio connection. 1830 */ clearOriginalConnection()1831 void clearOriginalConnection() { 1832 if (mOriginalConnection != null) { 1833 if (getPhone() != null) { 1834 getPhone().unregisterForPreciseCallStateChanged(mHandler); 1835 getPhone().unregisterForRingbackTone(mHandler); 1836 getPhone().unregisterForHandoverStateChanged(mHandler); 1837 getPhone().unregisterForRedialConnectionChanged(mHandler); 1838 getPhone().unregisterForDisconnect(mHandler); 1839 getPhone().unregisterForSuppServiceNotification(mHandler); 1840 getPhone().unregisterForOnHoldTone(mHandler); 1841 getPhone().unregisterForInCallVoicePrivacyOn(mHandler); 1842 getPhone().unregisterForInCallVoicePrivacyOff(mHandler); 1843 } 1844 mOriginalConnection.removePostDialListener(mPostDialListener); 1845 mOriginalConnection.removeListener(mOriginalConnectionListener); 1846 mOriginalConnection = null; 1847 } 1848 } 1849 hangup(int telephonyDisconnectCode)1850 protected void hangup(int telephonyDisconnectCode) { 1851 if (mOriginalConnection != null) { 1852 mHangupDisconnectCause = telephonyDisconnectCode; 1853 try { 1854 // Hanging up a ringing call requires that we invoke call.hangup() as opposed to 1855 // connection.hangup(). Without this change, the party originating the call 1856 // will not get sent to voicemail if the user opts to reject the call. 1857 if (isValidRingingCall()) { 1858 Call call = getCall(); 1859 if (call != null) { 1860 call.hangup(); 1861 } else { 1862 Log.w(this, "Attempting to hangup a connection without backing call."); 1863 } 1864 } else { 1865 // We still prefer to call connection.hangup() for non-ringing calls 1866 // in order to support hanging-up specific calls within a conference call. 1867 // If we invoked call.hangup() while in a conference, we would end up 1868 // hanging up the entire conference call instead of the specific connection. 1869 mOriginalConnection.hangup(); 1870 } 1871 } catch (CallStateException e) { 1872 Log.e(this, e, "Call to Connection.hangup failed with exception"); 1873 } 1874 } else { 1875 if (getState() == STATE_DISCONNECTED) { 1876 Log.i(this, "hangup called on an already disconnected call!"); 1877 close(); 1878 } else { 1879 // There are a few cases where mOriginalConnection has not been set yet. For 1880 // example, when the radio has to be turned on to make an emergency call, 1881 // mOriginalConnection could not be set for many seconds. 1882 setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause( 1883 android.telephony.DisconnectCause.LOCAL, 1884 "Local Disconnect before connection established.")); 1885 close(); 1886 } 1887 } 1888 } 1889 reject(@ndroid.telecom.Call.RejectReason int rejectReason)1890 protected void reject(@android.telecom.Call.RejectReason int rejectReason) { 1891 if (mOriginalConnection != null) { 1892 mHangupDisconnectCause = android.telephony.DisconnectCause.INCOMING_REJECTED; 1893 try { 1894 // Hanging up a ringing call requires that we invoke call.hangup() as opposed to 1895 // connection.hangup(). Without this change, the party originating the call 1896 // will not get sent to voicemail if the user opts to reject the call. 1897 if (isValidRingingCall()) { 1898 Call call = getCall(); 1899 if (call != null) { 1900 call.hangup(rejectReason); 1901 } else { 1902 Log.w(this, "Attempting to hangup a connection without backing call."); 1903 } 1904 } else { 1905 // We still prefer to call connection.hangup() for non-ringing calls 1906 // in order to support hanging-up specific calls within a conference call. 1907 // If we invoked call.hangup() while in a conference, we would end up 1908 // hanging up the entire conference call instead of the specific connection. 1909 mOriginalConnection.hangup(); 1910 } 1911 } catch (CallStateException e) { 1912 Log.e(this, e, "Call to Connection.hangup failed with exception"); 1913 } 1914 } else { 1915 if (getState() == STATE_DISCONNECTED) { 1916 Log.i(this, "hangup called on an already disconnected call!"); 1917 close(); 1918 } else { 1919 // There are a few cases where mOriginalConnection has not been set yet. For 1920 // example, when the radio has to be turned on to make an emergency call, 1921 // mOriginalConnection could not be set for many seconds. 1922 setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause( 1923 android.telephony.DisconnectCause.LOCAL, 1924 "Local Disconnect before connection established.")); 1925 close(); 1926 } 1927 } 1928 } 1929 getOriginalConnection()1930 com.android.internal.telephony.Connection getOriginalConnection() { 1931 return mOriginalConnection; 1932 } 1933 getCall()1934 protected Call getCall() { 1935 if (mOriginalConnection != null) { 1936 return mOriginalConnection.getCall(); 1937 } 1938 return null; 1939 } 1940 getPhone()1941 Phone getPhone() { 1942 Call call = getCall(); 1943 if (call != null) { 1944 return call.getPhone(); 1945 } 1946 return null; 1947 } 1948 hasMultipleTopLevelCalls()1949 private boolean hasMultipleTopLevelCalls() { 1950 int numCalls = 0; 1951 Phone phone = getPhone(); 1952 if (phone != null) { 1953 if (!phone.getRingingCall().isIdle()) { 1954 numCalls++; 1955 } 1956 if (!phone.getForegroundCall().isIdle()) { 1957 numCalls++; 1958 } 1959 if (!phone.getBackgroundCall().isIdle()) { 1960 numCalls++; 1961 } 1962 } 1963 return numCalls > 1; 1964 } 1965 getForegroundConnection()1966 private com.android.internal.telephony.Connection getForegroundConnection() { 1967 if (getPhone() != null) { 1968 return getPhone().getForegroundCall().getEarliestConnection(); 1969 } 1970 return null; 1971 } 1972 1973 /** 1974 * Checks for and returns the list of conference participants 1975 * associated with this connection. 1976 */ getConferenceParticipants()1977 public List<ConferenceParticipant> getConferenceParticipants() { 1978 if (mOriginalConnection == null) { 1979 Log.w(this, "Null mOriginalConnection, cannot get conf participants."); 1980 return null; 1981 } 1982 return mOriginalConnection.getConferenceParticipants(); 1983 } 1984 1985 /** 1986 * Checks to see the original connection corresponds to an active incoming call. Returns false 1987 * if there is no such actual call, or if the associated call is not incoming (See 1988 * {@link Call.State#isRinging}). 1989 */ isValidRingingCall()1990 private boolean isValidRingingCall() { 1991 if (getPhone() == null) { 1992 Log.v(this, "isValidRingingCall, phone is null"); 1993 return false; 1994 } 1995 1996 Call ringingCall = getPhone().getRingingCall(); 1997 if (!ringingCall.getState().isRinging()) { 1998 Log.v(this, "isValidRingingCall, ringing call is not in ringing state"); 1999 return false; 2000 } 2001 2002 if (ringingCall.getEarliestConnection() != mOriginalConnection) { 2003 Log.v(this, "isValidRingingCall, ringing call connection does not match"); 2004 return false; 2005 } 2006 2007 Log.v(this, "isValidRingingCall, returning true"); 2008 return true; 2009 } 2010 2011 // Make sure the extras being passed into this method is a COPY of the original extras Bundle. 2012 // We do not want the extras to be cleared or modified during mOriginalConnectionExtras.putAll 2013 // below. updateExtras(Bundle extras)2014 protected void updateExtras(Bundle extras) { 2015 if (mOriginalConnection != null) { 2016 if (extras != null) { 2017 // Check if extras have changed and need updating. 2018 if (!areBundlesEqual(mOriginalConnectionExtras, extras)) { 2019 if (Log.DEBUG) { 2020 Log.d(TelephonyConnection.this, "Updating extras:"); 2021 for (String key : extras.keySet()) { 2022 Object value = extras.get(key); 2023 if (value instanceof String) { 2024 Log.d(this, "updateExtras Key=" + Rlog.pii(LOG_TAG, key) 2025 + " value=" + Rlog.pii(LOG_TAG, value)); 2026 } 2027 } 2028 } 2029 mOriginalConnectionExtras.clear(); 2030 2031 mOriginalConnectionExtras.putAll(extras); 2032 2033 // Remap any string extras that have a remapping defined. 2034 for (String key : mOriginalConnectionExtras.keySet()) { 2035 if (sExtrasMap.containsKey(key)) { 2036 String newKey = sExtrasMap.get(key); 2037 mOriginalConnectionExtras.putString(newKey, extras.getString(key)); 2038 mOriginalConnectionExtras.remove(key); 2039 } 2040 } 2041 2042 // Ensure extras are propagated to Telecom. 2043 putTelephonyExtras(mOriginalConnectionExtras); 2044 // If extras contain Conference support information, 2045 // then ensure capabilities are updated. 2046 if (mOriginalConnectionExtras.containsKey( 2047 ImsCallProfile.EXTRA_CONFERENCE_AVAIL)) { 2048 updateConnectionCapabilities(); 2049 } 2050 } else { 2051 Log.d(this, "Extras update not required"); 2052 } 2053 } else { 2054 Log.d(this, "updateExtras extras: " + Rlog.pii(LOG_TAG, extras)); 2055 } 2056 } 2057 } 2058 areBundlesEqual(Bundle extras, Bundle newExtras)2059 private static boolean areBundlesEqual(Bundle extras, Bundle newExtras) { 2060 if (extras == null || newExtras == null) { 2061 return extras == newExtras; 2062 } 2063 2064 if (extras.size() != newExtras.size()) { 2065 return false; 2066 } 2067 2068 for(String key : extras.keySet()) { 2069 if (key != null) { 2070 final Object value = extras.get(key); 2071 final Object newValue = newExtras.get(key); 2072 if (!Objects.equals(value, newValue)) { 2073 return false; 2074 } 2075 } 2076 } 2077 return true; 2078 } 2079 setStateOverride(Call.State state)2080 void setStateOverride(Call.State state) { 2081 mIsStateOverridden = true; 2082 mConnectionOverriddenState = state; 2083 // Need to keep track of the original connection's state before override. 2084 mOriginalConnectionState = mOriginalConnection.getState(); 2085 updateStateInternal(); 2086 } 2087 resetStateOverride()2088 void resetStateOverride() { 2089 mIsStateOverridden = false; 2090 updateStateInternal(); 2091 } 2092 updateStateInternal()2093 void updateStateInternal() { 2094 if (mOriginalConnection == null) { 2095 return; 2096 } 2097 Call.State newState; 2098 // If the state is overridden and the state of the original connection hasn't changed since, 2099 // then we continue in the overridden state, else we go to the original connection's state. 2100 if (mIsStateOverridden && mOriginalConnectionState == mOriginalConnection.getState()) { 2101 newState = mConnectionOverriddenState; 2102 } else { 2103 newState = mOriginalConnection.getState(); 2104 } 2105 int cause = mOriginalConnection.getDisconnectCause(); 2106 Log.v(this, "Update state from %s to %s for %s", mConnectionState, newState, 2107 getTelecomCallId()); 2108 2109 if (mConnectionState != newState) { 2110 mConnectionState = newState; 2111 switch (newState) { 2112 case IDLE: 2113 break; 2114 case ACTIVE: 2115 setActiveInternal(); 2116 break; 2117 case HOLDING: 2118 setTelephonyConnectionOnHold(); 2119 break; 2120 case DIALING: 2121 case ALERTING: 2122 if (mOriginalConnection != null && mOriginalConnection.isPulledCall()) { 2123 setTelephonyConnectionPulling(); 2124 } else { 2125 setTelephonyConnectionDialing(); 2126 } 2127 break; 2128 case INCOMING: 2129 case WAITING: 2130 setTelephonyConnectionRinging(); 2131 break; 2132 case DISCONNECTED: 2133 if (shouldTreatAsEmergencyCall() 2134 && (cause 2135 == android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE 2136 || cause 2137 == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE)) { 2138 // We can get into a situation where the radio wants us to redial the 2139 // same emergency call on the other available slot. This will not set 2140 // the state to disconnected and will instead tell the 2141 // TelephonyConnectionService to 2142 // create a new originalConnection using the new Slot. 2143 fireOnOriginalConnectionRetryDial(cause 2144 == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE); 2145 } else { 2146 int preciseDisconnectCause = CallFailCause.NOT_VALID; 2147 if (mShowPreciseFailedCause) { 2148 preciseDisconnectCause = 2149 mOriginalConnection.getPreciseDisconnectCause(); 2150 } 2151 int disconnectCause = mOriginalConnection.getDisconnectCause(); 2152 if ((mHangupDisconnectCause != DisconnectCause.NOT_VALID) 2153 && (mHangupDisconnectCause != disconnectCause)) { 2154 Log.i(LOG_TAG, "setDisconnected: override cause: " + disconnectCause 2155 + " -> " + mHangupDisconnectCause); 2156 disconnectCause = mHangupDisconnectCause; 2157 } 2158 setTelephonyConnectionDisconnected( 2159 DisconnectCauseUtil.toTelecomDisconnectCause( 2160 disconnectCause, 2161 preciseDisconnectCause, 2162 mOriginalConnection.getVendorDisconnectCause(), 2163 getPhone().getPhoneId())); 2164 close(); 2165 } 2166 break; 2167 case DISCONNECTING: 2168 break; 2169 } 2170 } 2171 } 2172 updateState()2173 void updateState() { 2174 if (mOriginalConnection == null) { 2175 return; 2176 } 2177 2178 updateStateInternal(); 2179 updateStatusHints(); 2180 updateConnectionCapabilities(); 2181 updateConnectionProperties(); 2182 updateAddress(); 2183 updateMultiparty(); 2184 refreshDisableAddCall(); 2185 refreshCodecType(); 2186 } 2187 2188 /** 2189 * Checks for changes to the multiparty bit. If a conference has started, informs listeners. 2190 */ updateMultiparty()2191 private void updateMultiparty() { 2192 if (mOriginalConnection == null) { 2193 return; 2194 } 2195 2196 if (mIsMultiParty != mOriginalConnection.isMultiparty()) { 2197 mIsMultiParty = mOriginalConnection.isMultiparty(); 2198 2199 if (mIsMultiParty) { 2200 notifyConferenceStarted(); 2201 } 2202 } 2203 } 2204 2205 /** 2206 * Handles a failure when merging calls into a conference. 2207 * {@link com.android.internal.telephony.Connection.Listener#onConferenceMergedFailed()} 2208 * listener. 2209 */ handleConferenceMergeFailed()2210 private void handleConferenceMergeFailed(){ 2211 mHandler.obtainMessage(MSG_CONFERENCE_MERGE_FAILED).sendToTarget(); 2212 } 2213 2214 /** 2215 * Handles requests to update the multiparty state received via the 2216 * {@link com.android.internal.telephony.Connection.Listener#onMultipartyStateChanged(boolean)} 2217 * listener. 2218 * <p> 2219 * Note: We post this to the mHandler to ensure that if a conference must be created as a 2220 * result of the multiparty state change, the conference creation happens on the correct 2221 * thread. This ensures that the thread check in 2222 * {@link com.android.internal.telephony.Phone#checkCorrectThread(android.os.Handler)} 2223 * does not fire. 2224 * 2225 * @param isMultiParty {@code true} if this connection is multiparty, {@code false} otherwise. 2226 */ handleMultipartyStateChange(boolean isMultiParty)2227 private void handleMultipartyStateChange(boolean isMultiParty) { 2228 Log.i(this, "Update multiparty state to %s", isMultiParty ? "Y" : "N"); 2229 mHandler.obtainMessage(MSG_MULTIPARTY_STATE_CHANGED, isMultiParty).sendToTarget(); 2230 } 2231 setActiveInternal()2232 private void setActiveInternal() { 2233 if (getState() == STATE_ACTIVE) { 2234 Log.w(this, "Should not be called if this is already ACTIVE"); 2235 return; 2236 } 2237 2238 // When we set a call to active, we need to make sure that there are no other active 2239 // calls. However, the ordering of state updates to connections can be non-deterministic 2240 // since all connections register for state changes on the phone independently. 2241 // To "optimize", we check here to see if there already exists any active calls. If so, 2242 // we issue an update for those calls first to make sure we only have one top-level 2243 // active call. 2244 if (getTelephonyConnectionService() != null) { 2245 for (Connection current : getTelephonyConnectionService().getAllConnections()) { 2246 if (current != this && current instanceof TelephonyConnection) { 2247 TelephonyConnection other = (TelephonyConnection) current; 2248 if (other.getState() == STATE_ACTIVE) { 2249 other.updateState(); 2250 } 2251 } 2252 } 2253 } 2254 setTelephonyConnectionActive(); 2255 } 2256 close()2257 public void close() { 2258 Log.v(this, "close"); 2259 clearOriginalConnection(); 2260 destroy(); 2261 if (mTelephonyConnectionService != null) { 2262 removeTelephonyConnectionListener( 2263 mTelephonyConnectionService.getTelephonyConnectionListener()); 2264 } 2265 notifyDestroyed(); 2266 } 2267 2268 /** 2269 * Determines if the current connection is video capable. 2270 * 2271 * A connection is deemed to be video capable if the original connection capabilities state that 2272 * both local and remote video is supported. 2273 * 2274 * @return {@code true} if the connection is video capable, {@code false} otherwise. 2275 */ isVideoCapable()2276 private boolean isVideoCapable() { 2277 return (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL) 2278 == Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL 2279 && (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL) 2280 == Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL; 2281 } 2282 2283 /** 2284 * Determines if the current connection is an external connection. 2285 * 2286 * A connection is deemed to be external if the original connection capabilities state that it 2287 * is. 2288 * 2289 * @return {@code true} if the connection is external, {@code false} otherwise. 2290 */ isExternalConnection()2291 private boolean isExternalConnection() { 2292 return (mOriginalConnectionCapabilities 2293 & Capability.IS_EXTERNAL_CONNECTION) == Capability.IS_EXTERNAL_CONNECTION; 2294 } 2295 2296 /** 2297 * Determines if the current connection has RTT enabled. 2298 */ isRtt()2299 private boolean isRtt() { 2300 return mOriginalConnection != null 2301 && mOriginalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS 2302 && mOriginalConnection instanceof ImsPhoneConnection 2303 && ((ImsPhoneConnection) mOriginalConnection).isRttEnabledForCall(); 2304 } 2305 2306 /** 2307 * Determines if the current connection is pullable. 2308 * 2309 * A connection is deemed to be pullable if the original connection capabilities state that it 2310 * is. 2311 * 2312 * @return {@code true} if the connection is pullable, {@code false} otherwise. 2313 */ isPullable()2314 private boolean isPullable() { 2315 return (mOriginalConnectionCapabilities & Capability.IS_EXTERNAL_CONNECTION) 2316 == Capability.IS_EXTERNAL_CONNECTION 2317 && (mOriginalConnectionCapabilities & Capability.IS_PULLABLE) 2318 == Capability.IS_PULLABLE; 2319 } 2320 2321 /** 2322 * Sets whether or not CDMA enhanced call privacy is enabled for this connection. 2323 */ setCdmaVoicePrivacy(boolean isEnabled)2324 private void setCdmaVoicePrivacy(boolean isEnabled) { 2325 if(mIsCdmaVoicePrivacyEnabled != isEnabled) { 2326 mIsCdmaVoicePrivacyEnabled = isEnabled; 2327 updateConnectionProperties(); 2328 } 2329 } 2330 2331 /** 2332 * Applies capabilities specific to conferences termination to the 2333 * {@code ConnectionCapabilities} bit-mask. 2334 * 2335 * @param capabilities The {@code ConnectionCapabilities} bit-mask. 2336 * @return The capabilities with the IMS conference capabilities applied. 2337 */ applyConferenceTerminationCapabilities(int capabilities)2338 private int applyConferenceTerminationCapabilities(int capabilities) { 2339 int currentCapabilities = capabilities; 2340 2341 // An IMS call cannot be individually disconnected or separated from its parent conference. 2342 // If the call was IMS, even if it hands over to GMS, these capabilities are not supported. 2343 if (!mWasImsConnection) { 2344 currentCapabilities |= CAPABILITY_DISCONNECT_FROM_CONFERENCE; 2345 currentCapabilities |= CAPABILITY_SEPARATE_FROM_CONFERENCE; 2346 } 2347 2348 return currentCapabilities; 2349 } 2350 2351 /** 2352 * Stores the new original connection capabilities, and applies them to the current connection, 2353 * notifying any listeners as necessary. 2354 * 2355 * @param connectionCapabilities The original connection capabilties. 2356 */ setOriginalConnectionCapabilities(int connectionCapabilities)2357 public void setOriginalConnectionCapabilities(int connectionCapabilities) { 2358 mOriginalConnectionCapabilities = connectionCapabilities; 2359 updateConnectionCapabilities(); 2360 updateConnectionProperties(); 2361 } 2362 2363 /** 2364 * Called to apply the capabilities present in the {@link #mOriginalConnection} to this 2365 * {@link Connection}. Provides a mapping between the capabilities present in the original 2366 * connection (see {@link com.android.internal.telephony.Connection.Capability}) and those in 2367 * this {@link Connection}. 2368 * 2369 * @param capabilities The capabilities bitmask from the {@link Connection}. 2370 * @return the capabilities bitmask with the original connection capabilities remapped and 2371 * applied. 2372 */ applyOriginalConnectionCapabilities(int capabilities)2373 public int applyOriginalConnectionCapabilities(int capabilities) { 2374 // We only support downgrading to audio if both the remote and local side support 2375 // downgrading to audio. 2376 int supportsDowngrade = Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL 2377 | Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE; 2378 boolean supportsDowngradeToAudio = 2379 (mOriginalConnectionCapabilities & supportsDowngrade) == supportsDowngrade; 2380 capabilities = changeBitmask(capabilities, 2381 CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO, !supportsDowngradeToAudio); 2382 2383 capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL, 2384 (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL) 2385 == Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL); 2386 2387 boolean isLocalVideoSupported = (mOriginalConnectionCapabilities 2388 & Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL) 2389 == Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL && !mIsTtyEnabled; 2390 capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL, 2391 isLocalVideoSupported); 2392 2393 return capabilities; 2394 } 2395 2396 /** 2397 * Whether the call is using wifi. 2398 */ isWifi()2399 boolean isWifi() { 2400 return getCallRadioTech() == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; 2401 } 2402 2403 /** 2404 * Sets whether this call has been identified by the network as an emergency call. 2405 * @param isNetworkIdentifiedEmergencyCall {@code true} if the network has identified this call 2406 * as an emergency call, {@code false} otherwise. 2407 */ setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall)2408 public void setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall) { 2409 Log.d(this, "setIsNetworkIdentifiedEmergencyCall; callId=%s, " 2410 + "isNetworkIdentifiedEmergencyCall=%b", getTelecomCallId(), 2411 isNetworkIdentifiedEmergencyCall); 2412 mIsNetworkIdentifiedEmergencyCall = isNetworkIdentifiedEmergencyCall; 2413 updateConnectionProperties(); 2414 } 2415 2416 /** 2417 * @return {@code true} if the network has identified this call as an emergency call, 2418 * {@code false} otherwise. 2419 */ isNetworkIdentifiedEmergencyCall()2420 public boolean isNetworkIdentifiedEmergencyCall() { 2421 return mIsNetworkIdentifiedEmergencyCall; 2422 } 2423 2424 /** 2425 * @return {@code true} if this is an outgoing call, {@code false} otherwise. 2426 */ isOutgoingCall()2427 public boolean isOutgoingCall() { 2428 return getCallDirection() == android.telecom.Call.Details.DIRECTION_OUTGOING; 2429 } 2430 2431 /** 2432 * Sets the current call audio quality. Used during rebuild of the properties 2433 * to set or unset the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property. 2434 * 2435 * @param audioQuality The audio quality. 2436 */ setAudioQuality(int audioQuality)2437 public void setAudioQuality(int audioQuality) { 2438 mHasHighDefAudio = audioQuality == 2439 com.android.internal.telephony.Connection.AUDIO_QUALITY_HIGH_DEFINITION; 2440 updateConnectionProperties(); 2441 } 2442 resetStateForConference()2443 void resetStateForConference() { 2444 if (getState() == Connection.STATE_HOLDING) { 2445 resetStateOverride(); 2446 } 2447 } 2448 setHoldingForConference()2449 boolean setHoldingForConference() { 2450 if (getState() == Connection.STATE_ACTIVE) { 2451 setStateOverride(Call.State.HOLDING); 2452 return true; 2453 } 2454 return false; 2455 } 2456 setRttTextStream(RttTextStream s)2457 public void setRttTextStream(RttTextStream s) { 2458 mRttTextStream = s; 2459 } 2460 getRttTextStream()2461 public RttTextStream getRttTextStream() { 2462 return mRttTextStream; 2463 } 2464 2465 /** 2466 * For video calls, sets whether this connection supports pausing the outgoing video for the 2467 * call using the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState. 2468 * 2469 * @param isVideoPauseSupported {@code true} if pause state supported, {@code false} otherwise. 2470 */ setVideoPauseSupported(boolean isVideoPauseSupported)2471 public void setVideoPauseSupported(boolean isVideoPauseSupported) { 2472 mIsVideoPauseSupported = isVideoPauseSupported; 2473 } 2474 2475 /** 2476 * @return {@code true} if this connection supports pausing the outgoing video using the 2477 * {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState. 2478 */ getVideoPauseSupported()2479 public boolean getVideoPauseSupported() { 2480 return mIsVideoPauseSupported; 2481 } 2482 2483 /** 2484 * Sets whether this connection supports conference calling. 2485 * @param isConferenceSupported {@code true} if conference calling is supported by this 2486 * connection, {@code false} otherwise. 2487 */ setConferenceSupported(boolean isConferenceSupported)2488 public void setConferenceSupported(boolean isConferenceSupported) { 2489 mIsConferenceSupported = isConferenceSupported; 2490 } 2491 2492 /** 2493 * @return {@code true} if this connection supports merging calls into a conference. 2494 */ isConferenceSupported()2495 public boolean isConferenceSupported() { 2496 return mIsConferenceSupported; 2497 } 2498 2499 /** 2500 * Sets whether managing conference call is supported after this connection being a part of a 2501 * Ims conference. 2502 * 2503 * @param isManageImsConferenceCallSupported {@code true} if manage conference calling is 2504 * supported after this connection being a part of a IMS conference, 2505 * {@code false} otherwise. 2506 */ setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported)2507 public void setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported) { 2508 mIsManageImsConferenceCallSupported = isManageImsConferenceCallSupported; 2509 } 2510 2511 /** 2512 * @return {@code true} if manage conference calling is supported after this connection being a 2513 * part of a IMS conference. 2514 */ isManageImsConferenceCallSupported()2515 public boolean isManageImsConferenceCallSupported() { 2516 return mIsManageImsConferenceCallSupported; 2517 } 2518 2519 /** 2520 * Sets whether this connection supports showing precise call disconnect cause. 2521 * @param showPreciseFailedCause {@code true} if showing precise call 2522 * disconnect cause is supported by this connection, {@code false} otherwise. 2523 */ setShowPreciseFailedCause(boolean showPreciseFailedCause)2524 public void setShowPreciseFailedCause(boolean showPreciseFailedCause) { 2525 mShowPreciseFailedCause = showPreciseFailedCause; 2526 } 2527 2528 /** 2529 * Sets whether TTY is enabled or not. 2530 * @param isTtyEnabled 2531 */ setTtyEnabled(boolean isTtyEnabled)2532 public void setTtyEnabled(boolean isTtyEnabled) { 2533 mIsTtyEnabled = isTtyEnabled; 2534 updateConnectionCapabilities(); 2535 } 2536 2537 /** 2538 * Whether the original connection is an IMS connection. 2539 * @return {@code True} if the original connection is an IMS connection, {@code false} 2540 * otherwise. 2541 */ isImsConnection()2542 protected boolean isImsConnection() { 2543 com.android.internal.telephony.Connection originalConnection = getOriginalConnection(); 2544 return originalConnection != null && 2545 originalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS; 2546 } 2547 2548 /** 2549 * Whether the original connection is an GSM/CDMA connection. 2550 * @return {@code True} if the original connection is an GSM/CDMA connection, {@code false} 2551 * otherwise. 2552 */ isGsmCdmaConnection()2553 protected boolean isGsmCdmaConnection() { 2554 Phone phone = getPhone(); 2555 if (phone != null) { 2556 switch (phone.getPhoneType()) { 2557 case PhoneConstants.PHONE_TYPE_GSM: 2558 case PhoneConstants.PHONE_TYPE_CDMA: 2559 return true; 2560 default: 2561 return false; 2562 } 2563 } 2564 return false; 2565 } 2566 2567 /** 2568 * Whether the original connection was ever an IMS connection, either before or now. 2569 * @return {@code True} if the original connection was ever an IMS connection, {@code false} 2570 * otherwise. 2571 */ wasImsConnection()2572 public boolean wasImsConnection() { 2573 return mWasImsConnection; 2574 } 2575 getIsUsingAssistedDialing()2576 boolean getIsUsingAssistedDialing() { 2577 return mIsUsingAssistedDialing; 2578 } 2579 setIsUsingAssistedDialing(Boolean isUsingAssistedDialing)2580 void setIsUsingAssistedDialing(Boolean isUsingAssistedDialing) { 2581 mIsUsingAssistedDialing = isUsingAssistedDialing; 2582 updateConnectionProperties(); 2583 } 2584 getAddressFromNumber(String number)2585 private static Uri getAddressFromNumber(String number) { 2586 // Address can be null for blocked calls. 2587 if (number == null) { 2588 number = ""; 2589 } 2590 return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null); 2591 } 2592 2593 /** 2594 * Changes a capabilities bit-mask to add or remove a capability. 2595 * 2596 * @param bitmask The bit-mask. 2597 * @param bitfield The bit-field to change. 2598 * @param enabled Whether the bit-field should be set or removed. 2599 * @return The bit-mask with the bit-field changed. 2600 */ changeBitmask(int bitmask, int bitfield, boolean enabled)2601 private int changeBitmask(int bitmask, int bitfield, boolean enabled) { 2602 if (enabled) { 2603 return bitmask | bitfield; 2604 } else { 2605 return bitmask & ~bitfield; 2606 } 2607 } 2608 updateStatusHints()2609 private void updateStatusHints() { 2610 if (isWifi() && getPhone() != null) { 2611 int labelId = isValidRingingCall() 2612 ? R.string.status_hint_label_incoming_wifi_call 2613 : R.string.status_hint_label_wifi_call; 2614 2615 Context context = getPhone().getContext(); 2616 setTelephonyStatusHints(new StatusHints( 2617 getResourceString(labelId), 2618 Icon.createWithResource( 2619 context, R.drawable.ic_signal_wifi_4_bar_24dp), 2620 null /* extras */)); 2621 } else { 2622 setTelephonyStatusHints(null); 2623 } 2624 } 2625 2626 /** 2627 * Register a listener for {@link TelephonyConnection} specific triggers. 2628 * @param l The instance of the listener to add 2629 * @return The connection being listened to 2630 */ addTelephonyConnectionListener(TelephonyConnectionListener l)2631 public final TelephonyConnection addTelephonyConnectionListener(TelephonyConnectionListener l) { 2632 mTelephonyListeners.add(l); 2633 // If we already have an original connection, let's call back immediately. 2634 // This would be the case for incoming calls. 2635 if (mOriginalConnection != null) { 2636 fireOnOriginalConnectionConfigured(); 2637 } 2638 return this; 2639 } 2640 2641 /** 2642 * Remove a listener for {@link TelephonyConnection} specific triggers. 2643 * @param l The instance of the listener to remove 2644 * @return The connection being listened to 2645 */ removeTelephonyConnectionListener( TelephonyConnectionListener l)2646 public final TelephonyConnection removeTelephonyConnectionListener( 2647 TelephonyConnectionListener l) { 2648 if (l != null) { 2649 mTelephonyListeners.remove(l); 2650 } 2651 return this; 2652 } 2653 2654 @Override setHoldable(boolean isHoldable)2655 public void setHoldable(boolean isHoldable) { 2656 mIsHoldable = isHoldable; 2657 updateConnectionCapabilities(); 2658 } 2659 2660 @Override isChildHoldable()2661 public boolean isChildHoldable() { 2662 return getConference() != null; 2663 } 2664 isHoldable()2665 public boolean isHoldable() { 2666 return mIsHoldable; 2667 } 2668 2669 /** 2670 * Fire a callback to the various listeners for when the original connection is 2671 * set in this {@link TelephonyConnection} 2672 */ fireOnOriginalConnectionConfigured()2673 private final void fireOnOriginalConnectionConfigured() { 2674 for (TelephonyConnectionListener l : mTelephonyListeners) { 2675 l.onOriginalConnectionConfigured(this); 2676 } 2677 } 2678 fireOnOriginalConnectionRetryDial(boolean isPermanentFailure)2679 private final void fireOnOriginalConnectionRetryDial(boolean isPermanentFailure) { 2680 for (TelephonyConnectionListener l : mTelephonyListeners) { 2681 l.onOriginalConnectionRetry(this, isPermanentFailure); 2682 } 2683 } 2684 2685 /** 2686 * Handles exiting ECM mode. 2687 */ handleExitedEcmMode()2688 protected void handleExitedEcmMode() { 2689 updateConnectionProperties(); 2690 } 2691 2692 /** 2693 * Determines whether the connection supports conference calling. A connection supports 2694 * conference calling if it: 2695 * 1. Is not an emergency call. 2696 * 2. Carrier supports conference calls. 2697 * 3. If call is a video call, carrier supports video conference calls. 2698 * 4. If call is a wifi call and VoWIFI is disabled and carrier supports merging these calls. 2699 */ 2700 @VisibleForTesting refreshConferenceSupported()2701 void refreshConferenceSupported() { 2702 boolean isVideoCall = VideoProfile.isVideo(getVideoState()); 2703 Phone phone = getPhone(); 2704 if (phone == null) { 2705 Log.w(this, "refreshConferenceSupported = false; phone is null"); 2706 if (isConferenceSupported()) { 2707 setConferenceSupported(false); 2708 notifyConferenceSupportedChanged(false); 2709 } 2710 return; 2711 } 2712 2713 boolean isIms = phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS; 2714 boolean isVoWifiEnabled = false; 2715 if (isIms) { 2716 isVoWifiEnabled = ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId()); 2717 } 2718 boolean isRttMergeSupported = getCarrierConfig() 2719 .getBoolean(CarrierConfigManager.KEY_ALLOW_MERGING_RTT_CALLS_BOOL); 2720 PhoneAccountHandle phoneAccountHandle = isIms ? PhoneUtils 2721 .makePstnPhoneAccountHandle(phone.getDefaultPhone()) 2722 : PhoneUtils.makePstnPhoneAccountHandle(phone); 2723 TelecomAccountRegistry telecomAccountRegistry = TelecomAccountRegistry 2724 .getInstance(getPhone().getContext()); 2725 boolean isConferencingSupported = telecomAccountRegistry 2726 .isMergeCallSupported(phoneAccountHandle); 2727 boolean isImsConferencingSupported = telecomAccountRegistry 2728 .isMergeImsCallSupported(phoneAccountHandle); 2729 mIsCarrierVideoConferencingSupported = telecomAccountRegistry 2730 .isVideoConferencingSupported(phoneAccountHandle); 2731 boolean isMergeOfWifiCallsAllowedWhenVoWifiOff = telecomAccountRegistry 2732 .isMergeOfWifiCallsAllowedWhenVoWifiOff(phoneAccountHandle); 2733 2734 Log.v(this, "refreshConferenceSupported : isConfSupp=%b, isImsConfSupp=%b, " + 2735 "isVidConfSupp=%b, isMergeOfWifiAllowed=%b, " + 2736 "isWifi=%b, isVoWifiEnabled=%b", 2737 isConferencingSupported, isImsConferencingSupported, 2738 mIsCarrierVideoConferencingSupported, isMergeOfWifiCallsAllowedWhenVoWifiOff, 2739 isWifi(), isVoWifiEnabled); 2740 boolean isConferenceSupported = true; 2741 if (mTreatAsEmergencyCall) { 2742 isConferenceSupported = false; 2743 Log.d(this, "refreshConferenceSupported = false; emergency call"); 2744 } else if (isRtt() && !isRttMergeSupported) { 2745 isConferenceSupported = false; 2746 Log.d(this, "refreshConferenceSupported = false; rtt call"); 2747 } else if (!isConferencingSupported || isIms && !isImsConferencingSupported) { 2748 isConferenceSupported = false; 2749 Log.d(this, "refreshConferenceSupported = false; carrier doesn't support conf."); 2750 } else if (isVideoCall && !mIsCarrierVideoConferencingSupported) { 2751 isConferenceSupported = false; 2752 Log.d(this, "refreshConferenceSupported = false; video conf not supported."); 2753 } else if (!isMergeOfWifiCallsAllowedWhenVoWifiOff && isWifi() && !isVoWifiEnabled) { 2754 isConferenceSupported = false; 2755 Log.d(this, 2756 "refreshConferenceSupported = false; can't merge wifi calls when voWifi off."); 2757 } else { 2758 Log.d(this, "refreshConferenceSupported = true."); 2759 } 2760 2761 if (isConferenceSupported != isConferenceSupported()) { 2762 setConferenceSupported(isConferenceSupported); 2763 notifyConferenceSupportedChanged(isConferenceSupported); 2764 } 2765 } 2766 /** 2767 * Provides a mapping from extras keys which may be found in the 2768 * {@link com.android.internal.telephony.Connection} to their equivalents defined in 2769 * {@link android.telecom.Connection}. 2770 * 2771 * @return Map containing key mappings. 2772 */ createExtrasMap()2773 private static Map<String, String> createExtrasMap() { 2774 Map<String, String> result = new HashMap<String, String>(); 2775 result.put(ImsCallProfile.EXTRA_CHILD_NUMBER, 2776 android.telecom.Connection.EXTRA_CHILD_ADDRESS); 2777 result.put(ImsCallProfile.EXTRA_DISPLAY_TEXT, 2778 android.telecom.Connection.EXTRA_CALL_SUBJECT); 2779 result.put(ImsCallProfile.EXTRA_ADDITIONAL_SIP_INVITE_FIELDS, 2780 android.telecom.Connection.EXTRA_SIP_INVITE); 2781 return Collections.unmodifiableMap(result); 2782 } 2783 isShowingOriginalDialString()2784 private boolean isShowingOriginalDialString() { 2785 boolean showOrigDialString = false; 2786 Phone phone = getPhone(); 2787 if (phone != null && (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) 2788 && !mOriginalConnection.isIncoming()) { 2789 PersistableBundle pb = getCarrierConfig(); 2790 if (pb != null) { 2791 showOrigDialString = pb.getBoolean(CarrierConfigManager 2792 .KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL); 2793 Log.d(this, "showOrigDialString: " + showOrigDialString); 2794 } 2795 } 2796 return showOrigDialString; 2797 } 2798 2799 /** 2800 * Creates a string representation of this {@link TelephonyConnection}. Primarily intended for 2801 * use in log statements. 2802 * 2803 * @return String representation of the connection. 2804 */ 2805 @Override toString()2806 public String toString() { 2807 StringBuilder sb = new StringBuilder(); 2808 sb.append("[TelephonyConnection objId:"); 2809 sb.append(System.identityHashCode(this)); 2810 sb.append(" telecomCallID:"); 2811 sb.append(getTelecomCallId()); 2812 sb.append(" type:"); 2813 if (isImsConnection()) { 2814 sb.append("ims"); 2815 } else if (this instanceof com.android.services.telephony.GsmConnection) { 2816 sb.append("gsm"); 2817 } else if (this instanceof CdmaConnection) { 2818 sb.append("cdma"); 2819 } 2820 sb.append(" state:"); 2821 sb.append(Connection.stateToString(getState())); 2822 sb.append(" capabilities:"); 2823 sb.append(capabilitiesToString(getConnectionCapabilities())); 2824 sb.append(" properties:"); 2825 sb.append(propertiesToString(getConnectionProperties())); 2826 sb.append(" address:"); 2827 sb.append(Rlog.pii(LOG_TAG, getAddress())); 2828 sb.append(" originalConnection:"); 2829 sb.append(mOriginalConnection); 2830 sb.append(" partOfConf:"); 2831 if (getConference() == null) { 2832 sb.append("N"); 2833 } else { 2834 sb.append("Y"); 2835 } 2836 sb.append(" confSupported:"); 2837 sb.append(mIsConferenceSupported ? "Y" : "N"); 2838 sb.append(" isAdhocConf:"); 2839 sb.append(isAdhocConferenceCall() ? "Y" : "N"); 2840 sb.append("]"); 2841 return sb.toString(); 2842 } 2843 setTelephonyConnectionService(TelephonyConnectionService connectionService)2844 public final void setTelephonyConnectionService(TelephonyConnectionService connectionService) { 2845 mTelephonyConnectionService = connectionService; 2846 } 2847 getTelephonyConnectionService()2848 public final TelephonyConnectionService getTelephonyConnectionService() { 2849 return mTelephonyConnectionService; 2850 } 2851 2852 /** 2853 * Set this {@link TelephonyConnection} to an active state. 2854 * <p> 2855 * Note: This should be used instead of {@link #setActive()} to ensure listeners are notified. 2856 */ setTelephonyConnectionActive()2857 public void setTelephonyConnectionActive() { 2858 setActive(); 2859 notifyStateChanged(getState()); 2860 } 2861 2862 /** 2863 * Set this {@link TelephonyConnection} to a ringing state. 2864 * <p> 2865 * Note: This should be used instead of {@link #setRinging()} to ensure listeners are notified. 2866 */ setTelephonyConnectionRinging()2867 public void setTelephonyConnectionRinging() { 2868 setRinging(); 2869 notifyStateChanged(getState()); 2870 } 2871 2872 /** 2873 * Set this {@link TelephonyConnection} to an initializing state. 2874 * <p> 2875 * Note: This should be used instead of {@link #setInitializing()} to ensure listeners are 2876 * notified. 2877 */ setTelephonyConnectionInitializing()2878 public void setTelephonyConnectionInitializing() { 2879 setInitializing(); 2880 notifyStateChanged(getState()); 2881 } 2882 2883 /** 2884 * Set this {@link TelephonyConnection} to a dialing state. 2885 * <p> 2886 * Note: This should be used instead of {@link #setDialing()} to ensure listeners are notified. 2887 */ setTelephonyConnectionDialing()2888 public void setTelephonyConnectionDialing() { 2889 setDialing(); 2890 notifyStateChanged(getState()); 2891 } 2892 2893 /** 2894 * Set this {@link TelephonyConnection} to a pulling state. 2895 * <p> 2896 * Note: This should be used instead of {@link #setPulling()} to ensure listeners are notified. 2897 */ setTelephonyConnectionPulling()2898 public void setTelephonyConnectionPulling() { 2899 setPulling(); 2900 notifyStateChanged(getState()); 2901 } 2902 2903 /** 2904 * Set this {@link TelephonyConnection} to a held state. 2905 * <p> 2906 * Note: This should be used instead of {@link #setOnHold()} to ensure listeners are notified. 2907 */ setTelephonyConnectionOnHold()2908 public void setTelephonyConnectionOnHold() { 2909 setOnHold(); 2910 notifyStateChanged(getState()); 2911 } 2912 2913 /** 2914 * Set this {@link TelephonyConnection} to a held state. 2915 * <p> 2916 * Note: This should be used instead of 2917 * {@link #setDisconnected(android.telecom.DisconnectCause)} to ensure listeners are notified. 2918 */ setTelephonyConnectionDisconnected(@onNull android.telecom.DisconnectCause disconnectCause)2919 public void setTelephonyConnectionDisconnected(@NonNull 2920 android.telecom.DisconnectCause disconnectCause) { 2921 setDisconnected(disconnectCause); 2922 notifyDisconnected(disconnectCause); 2923 notifyStateChanged(getState()); 2924 } 2925 2926 /** 2927 * Sends a connection event for this {@link TelephonyConnection}. 2928 * <p> 2929 * Note: This should be used instead of {@link #sendConnectionEvent(String, Bundle)} to ensure 2930 * listeners are notified. 2931 */ sendTelephonyConnectionEvent(@onNull String event, @Nullable Bundle extras)2932 public void sendTelephonyConnectionEvent(@NonNull String event, @Nullable Bundle extras) { 2933 sendConnectionEvent(event, extras); 2934 notifyTelephonyConnectionEvent(event, extras); 2935 } 2936 2937 /** 2938 * Sets the extras associated with this {@link TelephonyConnection}. 2939 * <p> 2940 * Note: This should be used instead of {@link #putExtras(Bundle)} to ensure listeners are 2941 * notified. 2942 */ putTelephonyExtras(@onNull Bundle extras)2943 public void putTelephonyExtras(@NonNull Bundle extras) { 2944 putExtras(extras); 2945 notifyPutExtras(extras); 2946 } 2947 2948 /** 2949 * Removes the specified extras associated with this {@link TelephonyConnection}. 2950 * <p> 2951 * Note: This should be used instead of {@link #removeExtras(String...)} to ensure listeners are 2952 * notified. 2953 */ removeTelephonyExtras(@onNull List<String> keys)2954 public void removeTelephonyExtras(@NonNull List<String> keys) { 2955 removeExtras(keys); 2956 notifyRemoveExtras(keys); 2957 } 2958 2959 /** 2960 * Sets the video state associated with this {@link TelephonyConnection}. 2961 * <p> 2962 * Note: This should be used instead of {@link #setVideoState(int)} to ensure listeners are 2963 * notified. 2964 * @param videoState The new video state. Valid values: 2965 * {@link VideoProfile#STATE_AUDIO_ONLY}, 2966 * {@link VideoProfile#STATE_BIDIRECTIONAL}, 2967 * {@link VideoProfile#STATE_TX_ENABLED}, 2968 * {@link VideoProfile#STATE_RX_ENABLED}. 2969 */ setTelephonyVideoState(int videoState)2970 public void setTelephonyVideoState(int videoState) { 2971 setVideoState(videoState); 2972 notifyVideoStateChanged(videoState); 2973 } 2974 2975 /** 2976 * Sets the video provider associated with this {@link TelephonyConnection}. 2977 * <p> 2978 * Note: This should be used instead of {@link #setVideoProvider(VideoProvider)} to ensure 2979 * listeners are notified. 2980 */ setTelephonyVideoProvider(@ullable VideoProvider videoProvider)2981 public void setTelephonyVideoProvider(@Nullable VideoProvider videoProvider) { 2982 setVideoProvider(videoProvider); 2983 notifyVideoProviderChanged(videoProvider); 2984 } 2985 2986 /** 2987 * Sets the status hints associated with this {@link TelephonyConnection}. 2988 * <p> 2989 * Note: This should be used instead of {@link #setStatusHints(StatusHints)} to ensure listeners 2990 * are notified. 2991 */ setTelephonyStatusHints(@ullable StatusHints statusHints)2992 public void setTelephonyStatusHints(@Nullable StatusHints statusHints) { 2993 setStatusHints(statusHints); 2994 notifyStatusHintsChanged(statusHints); 2995 } 2996 2997 /** 2998 * Sets RIL voice radio technology used for current connection. 2999 * <p> 3000 * This property is set by the Telephony {@link ConnectionService}. 3001 * 3002 * @param vrat the RIL Voice Radio Technology used for current connection, 3003 * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}. 3004 */ setCallRadioTech(@ilRadioTechnology int vrat)3005 public final void setCallRadioTech(@RilRadioTechnology int vrat) { 3006 Bundle extras = getExtras(); 3007 if (extras == null) { 3008 extras = new Bundle(); 3009 } 3010 extras.putInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE, 3011 ServiceState.rilRadioTechnologyToNetworkType(vrat)); 3012 putExtras(extras); 3013 // Propagates the call radio technology to its parent {@link android.telecom.Conference} 3014 // This action only covers non-IMS CS conference calls. 3015 // For IMS PS call conference call, it can be updated via its host connection 3016 // {@link #Listener.onExtrasChanged} event. 3017 if (getConference() != null) { 3018 Bundle newExtras = new Bundle(); 3019 newExtras.putInt( 3020 TelecomManager.EXTRA_CALL_NETWORK_TYPE, 3021 ServiceState.rilRadioTechnologyToNetworkType(vrat)); 3022 getConference().putExtras(newExtras); 3023 } 3024 } 3025 3026 /** 3027 * Returns RIL voice radio technology used for current connection. 3028 * <p> 3029 * Used by the Telephony {@link ConnectionService}. 3030 * 3031 * @return the RIL voice radio technology used for current connection, 3032 * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}. 3033 */ getCallRadioTech()3034 public final @RilRadioTechnology int getCallRadioTech() { 3035 int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; 3036 Bundle extras = getExtras(); 3037 if (extras != null) { 3038 voiceNetworkType = extras.getInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE, 3039 TelephonyManager.NETWORK_TYPE_UNKNOWN); 3040 } 3041 return ServiceState.networkTypeToRilRadioTechnology(voiceNetworkType); 3042 } 3043 3044 /** 3045 * Notifies {@link TelephonyConnectionListener}s of a change to conference participant data 3046 * received via the {@link ImsConference} (i.e. conference event package). 3047 * 3048 * @param conferenceParticipants The participants. 3049 */ updateConferenceParticipants( @onNull List<ConferenceParticipant> conferenceParticipants)3050 private void updateConferenceParticipants( 3051 @NonNull List<ConferenceParticipant> conferenceParticipants) { 3052 for (TelephonyConnectionListener l : mTelephonyListeners) { 3053 l.onConferenceParticipantsChanged(this, conferenceParticipants); 3054 } 3055 } 3056 3057 /** 3058 * Called by a {@link ConnectionService} to notify Telecom that a {@link Conference#onMerge()} 3059 * operation has started. 3060 */ notifyConferenceStarted()3061 protected void notifyConferenceStarted() { 3062 for (TelephonyConnectionListener l : mTelephonyListeners) { 3063 l.onConferenceStarted(); 3064 } 3065 } 3066 3067 /** 3068 * Notifies {@link TelephonyConnectionListener}s when a change has occurred to the Connection 3069 * which impacts its ability to be a part of a conference call. 3070 * @param isConferenceSupported {@code true} if the connection supports being part of a 3071 * conference call, {@code false} otherwise. 3072 */ notifyConferenceSupportedChanged(boolean isConferenceSupported)3073 private void notifyConferenceSupportedChanged(boolean isConferenceSupported) { 3074 for (TelephonyConnectionListener l : mTelephonyListeners) { 3075 l.onConferenceSupportedChanged(this, isConferenceSupported); 3076 } 3077 } 3078 3079 /** 3080 * Notifies {@link TelephonyConnectionListener}s of changes to the connection capabilities. 3081 * @param newCapabilities the new capabilities. 3082 */ notifyConnectionCapabilitiesChanged(int newCapabilities)3083 private void notifyConnectionCapabilitiesChanged(int newCapabilities) { 3084 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3085 listener.onConnectionCapabilitiesChanged(this, newCapabilities); 3086 } 3087 } 3088 3089 /** 3090 * Notifies {@link TelephonyConnectionListener}s of changes to the connection properties. 3091 * @param newProperties the new properties. 3092 */ notifyConnectionPropertiesChanged(int newProperties)3093 private void notifyConnectionPropertiesChanged(int newProperties) { 3094 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3095 listener.onConnectionPropertiesChanged(this, newProperties); 3096 } 3097 } 3098 3099 /** 3100 * Notifies {@link TelephonyConnectionListener}s when a connection is destroyed. 3101 */ notifyDestroyed()3102 private void notifyDestroyed() { 3103 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3104 listener.onDestroyed(this); 3105 } 3106 } 3107 3108 /** 3109 * Notifies {@link TelephonyConnectionListener}s when a connection disconnects. 3110 * @param cause The disconnect cause. 3111 */ notifyDisconnected(android.telecom.DisconnectCause cause)3112 private void notifyDisconnected(android.telecom.DisconnectCause cause) { 3113 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3114 listener.onDisconnected(this, cause); 3115 } 3116 } 3117 3118 /** 3119 * Notifies {@link TelephonyConnectionListener}s of connection state changes. 3120 * @param newState The new state. 3121 */ notifyStateChanged(int newState)3122 private void notifyStateChanged(int newState) { 3123 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3124 listener.onStateChanged(this, newState); 3125 } 3126 } 3127 3128 /** 3129 * Notifies {@link TelephonyConnectionListener}s of telephony connection events. 3130 * @param event The event. 3131 * @param extras Any extras. 3132 */ notifyTelephonyConnectionEvent(String event, Bundle extras)3133 private void notifyTelephonyConnectionEvent(String event, Bundle extras) { 3134 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3135 listener.onConnectionEvent(this, event, extras); 3136 } 3137 } 3138 3139 /** 3140 * Notifies {@link TelephonyConnectionListener}s when extras are added to the connection. 3141 * @param extras The new extras. 3142 */ notifyPutExtras(Bundle extras)3143 private void notifyPutExtras(Bundle extras) { 3144 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3145 listener.onExtrasChanged(this, extras); 3146 } 3147 } 3148 3149 /** 3150 * Notifies {@link TelephonyConnectionListener}s when extra keys are removed from a connection. 3151 * @param keys The removed keys. 3152 */ notifyRemoveExtras(List<String> keys)3153 private void notifyRemoveExtras(List<String> keys) { 3154 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3155 listener.onExtrasRemoved(this, keys); 3156 } 3157 } 3158 3159 /** 3160 * Notifies {@link TelephonyConnectionListener}s of a change to the video state of a connection. 3161 * @param videoState The new video state. Valid values: 3162 * {@link VideoProfile#STATE_AUDIO_ONLY}, 3163 * {@link VideoProfile#STATE_BIDIRECTIONAL}, 3164 * {@link VideoProfile#STATE_TX_ENABLED}, 3165 * {@link VideoProfile#STATE_RX_ENABLED}. 3166 */ notifyVideoStateChanged(int videoState)3167 private void notifyVideoStateChanged(int videoState) { 3168 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3169 listener.onVideoStateChanged(this, videoState); 3170 } 3171 } 3172 3173 /** 3174 * Notifies {@link TelephonyConnectionListener}s of a whether to play Ringback Tone or not. 3175 * @param ringback Whether the ringback tone is to be played 3176 */ notifyRingbackRequested(boolean ringback)3177 private void notifyRingbackRequested(boolean ringback) { 3178 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3179 listener.onRingbackRequested(this, ringback); 3180 } 3181 } 3182 3183 /** 3184 * Notifies {@link TelephonyConnectionListener}s of changes to the video provider for a 3185 * connection. 3186 * @param videoProvider The new video provider. 3187 */ notifyVideoProviderChanged(VideoProvider videoProvider)3188 private void notifyVideoProviderChanged(VideoProvider videoProvider) { 3189 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3190 listener.onVideoProviderChanged(this, videoProvider); 3191 } 3192 } 3193 3194 /** 3195 * Notifies {@link TelephonyConnectionListener}s of changes to the status hints for a 3196 * connection. 3197 * @param statusHints The new status hints. 3198 */ notifyStatusHintsChanged(StatusHints statusHints)3199 private void notifyStatusHintsChanged(StatusHints statusHints) { 3200 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3201 listener.onStatusHintsChanged(this, statusHints); 3202 } 3203 } 3204 } 3205