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