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