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 capabilities = changeBitmask(capabilities, CAPABILITY_REMOTE_PARTY_SUPPORTS_RTT, 2711 (mOriginalConnectionCapabilities & Capability.SUPPORTS_RTT_REMOTE) 2712 == Capability.SUPPORTS_RTT_REMOTE); 2713 2714 return capabilities; 2715 } 2716 2717 /** 2718 * Whether the call is using wifi. 2719 */ isWifi()2720 boolean isWifi() { 2721 return getCallRadioTech() == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; 2722 } 2723 2724 /** 2725 * Sets whether this call has been identified by the network as an emergency call. 2726 * @param isNetworkIdentifiedEmergencyCall {@code true} if the network has identified this call 2727 * as an emergency call, {@code false} otherwise. 2728 */ setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall)2729 public void setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall) { 2730 Log.d(this, "setIsNetworkIdentifiedEmergencyCall; callId=%s, " 2731 + "isNetworkIdentifiedEmergencyCall=%b", getTelecomCallId(), 2732 isNetworkIdentifiedEmergencyCall); 2733 mIsNetworkIdentifiedEmergencyCall = isNetworkIdentifiedEmergencyCall; 2734 updateConnectionProperties(); 2735 } 2736 2737 /** 2738 * @return {@code true} if the network has identified this call as an emergency call, 2739 * {@code false} otherwise. 2740 */ isNetworkIdentifiedEmergencyCall()2741 public boolean isNetworkIdentifiedEmergencyCall() { 2742 return mIsNetworkIdentifiedEmergencyCall; 2743 } 2744 2745 /** 2746 * @return {@code true} if this is an outgoing call, {@code false} otherwise. 2747 */ isOutgoingCall()2748 public boolean isOutgoingCall() { 2749 return getCallDirection() == android.telecom.Call.Details.DIRECTION_OUTGOING; 2750 } 2751 2752 /** 2753 * Sets the current call audio quality. Used during rebuild of the properties 2754 * to set or unset the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property. 2755 * 2756 * @param audioQuality The audio quality. 2757 */ setAudioQuality(int audioQuality)2758 public void setAudioQuality(int audioQuality) { 2759 mHasHighDefAudio = audioQuality == 2760 com.android.internal.telephony.Connection.AUDIO_QUALITY_HIGH_DEFINITION; 2761 updateConnectionProperties(); 2762 } 2763 resetStateForConference()2764 void resetStateForConference() { 2765 if (getState() == Connection.STATE_HOLDING) { 2766 resetStateOverride(); 2767 } 2768 } 2769 setHoldingForConference()2770 boolean setHoldingForConference() { 2771 if (getState() == Connection.STATE_ACTIVE) { 2772 setStateOverride(Call.State.HOLDING); 2773 return true; 2774 } 2775 return false; 2776 } 2777 setRttTextStream(RttTextStream s)2778 public void setRttTextStream(RttTextStream s) { 2779 mRttTextStream = s; 2780 } 2781 getRttTextStream()2782 public RttTextStream getRttTextStream() { 2783 return mRttTextStream; 2784 } 2785 2786 /** 2787 * For video calls, sets whether this connection supports pausing the outgoing video for the 2788 * call using the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState. 2789 * 2790 * @param isVideoPauseSupported {@code true} if pause state supported, {@code false} otherwise. 2791 */ setVideoPauseSupported(boolean isVideoPauseSupported)2792 public void setVideoPauseSupported(boolean isVideoPauseSupported) { 2793 mIsVideoPauseSupported = isVideoPauseSupported; 2794 } 2795 2796 /** 2797 * @return {@code true} if this connection supports pausing the outgoing video using the 2798 * {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState. 2799 */ getVideoPauseSupported()2800 public boolean getVideoPauseSupported() { 2801 return mIsVideoPauseSupported; 2802 } 2803 2804 /** 2805 * Sets whether this connection supports conference calling. 2806 * @param isConferenceSupported {@code true} if conference calling is supported by this 2807 * connection, {@code false} otherwise. 2808 */ setConferenceSupported(boolean isConferenceSupported)2809 public void setConferenceSupported(boolean isConferenceSupported) { 2810 mIsConferenceSupported = isConferenceSupported; 2811 } 2812 2813 /** 2814 * @return {@code true} if this connection supports merging calls into a conference. 2815 */ isConferenceSupported()2816 public boolean isConferenceSupported() { 2817 return mIsConferenceSupported; 2818 } 2819 2820 /** 2821 * Sets whether managing conference call is supported after this connection being a part of a 2822 * Ims conference. 2823 * 2824 * @param isManageImsConferenceCallSupported {@code true} if manage conference calling is 2825 * supported after this connection being a part of a IMS conference, 2826 * {@code false} otherwise. 2827 */ setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported)2828 public void setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported) { 2829 mIsManageImsConferenceCallSupported = isManageImsConferenceCallSupported; 2830 } 2831 2832 /** 2833 * @return {@code true} if manage conference calling is supported after this connection being a 2834 * part of a IMS conference. 2835 */ isManageImsConferenceCallSupported()2836 public boolean isManageImsConferenceCallSupported() { 2837 return mIsManageImsConferenceCallSupported; 2838 } 2839 2840 /** 2841 * Sets whether this connection supports showing precise call disconnect cause. 2842 * @param showPreciseFailedCause {@code true} if showing precise call 2843 * disconnect cause is supported by this connection, {@code false} otherwise. 2844 */ setShowPreciseFailedCause(boolean showPreciseFailedCause)2845 public void setShowPreciseFailedCause(boolean showPreciseFailedCause) { 2846 mShowPreciseFailedCause = showPreciseFailedCause; 2847 } 2848 2849 /** 2850 * Sets whether TTY is enabled or not. 2851 * @param isTtyEnabled 2852 */ setTtyEnabled(boolean isTtyEnabled)2853 public void setTtyEnabled(boolean isTtyEnabled) { 2854 mIsTtyEnabled = isTtyEnabled; 2855 updateConnectionCapabilities(); 2856 } 2857 2858 /** 2859 * Whether the original connection is an IMS connection. 2860 * @return {@code True} if the original connection is an IMS connection, {@code false} 2861 * otherwise. 2862 */ isImsConnection()2863 protected boolean isImsConnection() { 2864 com.android.internal.telephony.Connection originalConnection = getOriginalConnection(); 2865 2866 return originalConnection != null 2867 && originalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS 2868 && originalConnection instanceof ImsPhoneConnection; 2869 } 2870 2871 /** 2872 * Whether the original connection is an GSM/CDMA connection. 2873 * @return {@code True} if the original connection is an GSM/CDMA connection, {@code false} 2874 * otherwise. 2875 */ isGsmCdmaConnection()2876 protected boolean isGsmCdmaConnection() { 2877 Phone phone = getPhone(); 2878 if (phone != null) { 2879 switch (phone.getPhoneType()) { 2880 case PhoneConstants.PHONE_TYPE_GSM: 2881 case PhoneConstants.PHONE_TYPE_CDMA: 2882 return true; 2883 default: 2884 return false; 2885 } 2886 } 2887 return false; 2888 } 2889 2890 /** 2891 * Whether the original connection was ever an IMS connection, either before or now. 2892 * @return {@code True} if the original connection was ever an IMS connection, {@code false} 2893 * otherwise. 2894 */ wasImsConnection()2895 public boolean wasImsConnection() { 2896 return mWasImsConnection; 2897 } 2898 getIsUsingAssistedDialing()2899 boolean getIsUsingAssistedDialing() { 2900 return mIsUsingAssistedDialing; 2901 } 2902 setIsUsingAssistedDialing(Boolean isUsingAssistedDialing)2903 void setIsUsingAssistedDialing(Boolean isUsingAssistedDialing) { 2904 mIsUsingAssistedDialing = isUsingAssistedDialing; 2905 updateConnectionProperties(); 2906 } 2907 getAddressFromNumber(String number)2908 private static Uri getAddressFromNumber(String number) { 2909 // Address can be null for blocked calls. 2910 if (number == null) { 2911 number = ""; 2912 } 2913 return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null); 2914 } 2915 2916 /** 2917 * Changes a capabilities bit-mask to add or remove a capability. 2918 * 2919 * @param bitmask The bit-mask. 2920 * @param bitfield The bit-field to change. 2921 * @param enabled Whether the bit-field should be set or removed. 2922 * @return The bit-mask with the bit-field changed. 2923 */ changeBitmask(int bitmask, int bitfield, boolean enabled)2924 private int changeBitmask(int bitmask, int bitfield, boolean enabled) { 2925 if (enabled) { 2926 return bitmask | bitfield; 2927 } else { 2928 return bitmask & ~bitfield; 2929 } 2930 } 2931 updateStatusHints()2932 private void updateStatusHints() { 2933 if (isWifi() && !isCrossSimCall() && getPhone() != null) { 2934 int labelId = isValidRingingCall() 2935 ? R.string.status_hint_label_incoming_wifi_call 2936 : R.string.status_hint_label_wifi_call; 2937 2938 Context context = getPhone().getContext(); 2939 setTelephonyStatusHints(new StatusHints( 2940 getResourceString(labelId), 2941 Icon.createWithResource( 2942 context, R.drawable.ic_signal_wifi_4_bar_24dp), 2943 null /* extras */)); 2944 } else { 2945 setTelephonyStatusHints(null); 2946 } 2947 } 2948 2949 /** 2950 * Register a listener for {@link TelephonyConnection} specific triggers. 2951 * @param l The instance of the listener to add 2952 * @return The connection being listened to 2953 */ addTelephonyConnectionListener(TelephonyConnectionListener l)2954 public final TelephonyConnection addTelephonyConnectionListener(TelephonyConnectionListener l) { 2955 mTelephonyListeners.add(l); 2956 // If we already have an original connection, let's call back immediately. 2957 // This would be the case for incoming calls. 2958 if (mOriginalConnection != null) { 2959 fireOnOriginalConnectionConfigured(); 2960 } 2961 return this; 2962 } 2963 2964 /** 2965 * Remove a listener for {@link TelephonyConnection} specific triggers. 2966 * @param l The instance of the listener to remove 2967 * @return The connection being listened to 2968 */ removeTelephonyConnectionListener( TelephonyConnectionListener l)2969 public final TelephonyConnection removeTelephonyConnectionListener( 2970 TelephonyConnectionListener l) { 2971 if (l != null) { 2972 mTelephonyListeners.remove(l); 2973 } 2974 return this; 2975 } 2976 2977 @Override setHoldable(boolean isHoldable)2978 public void setHoldable(boolean isHoldable) { 2979 mIsHoldable = isHoldable; 2980 updateConnectionCapabilities(); 2981 } 2982 2983 @Override isChildHoldable()2984 public boolean isChildHoldable() { 2985 return getConference() != null; 2986 } 2987 isHoldable()2988 public boolean isHoldable() { 2989 return mIsHoldable; 2990 } 2991 2992 /** 2993 * Fire a callback to the various listeners for when the original connection is 2994 * set in this {@link TelephonyConnection} 2995 */ fireOnOriginalConnectionConfigured()2996 private final void fireOnOriginalConnectionConfigured() { 2997 for (TelephonyConnectionListener l : mTelephonyListeners) { 2998 l.onOriginalConnectionConfigured(this); 2999 } 3000 } 3001 fireOnOriginalConnectionRetryDial(boolean isPermanentFailure)3002 private final void fireOnOriginalConnectionRetryDial(boolean isPermanentFailure) { 3003 for (TelephonyConnectionListener l : mTelephonyListeners) { 3004 l.onOriginalConnectionRetry(this, isPermanentFailure); 3005 } 3006 } 3007 3008 /** 3009 * Handles exiting ECM mode. 3010 */ handleExitedEcmMode()3011 protected void handleExitedEcmMode() { 3012 updateConnectionProperties(); 3013 } 3014 3015 /** 3016 * Determines whether the connection supports conference calling. A connection supports 3017 * conference calling if it: 3018 * 1. Is not an emergency call. 3019 * 2. Carrier supports conference calls. 3020 * 3. If call is a video call, carrier supports video conference calls. 3021 * 4. If call is a wifi call and VoWIFI is disabled and carrier supports merging these calls. 3022 */ 3023 @VisibleForTesting refreshConferenceSupported()3024 void refreshConferenceSupported() { 3025 boolean isVideoCall = VideoProfile.isVideo(getVideoState()); 3026 Phone phone = getPhone(); 3027 if (phone == null) { 3028 Log.w(this, "refreshConferenceSupported = false; phone is null"); 3029 if (isConferenceSupported()) { 3030 setConferenceSupported(false); 3031 notifyConferenceSupportedChanged(false); 3032 } 3033 return; 3034 } 3035 3036 boolean isIms = phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS; 3037 boolean isVoWifiEnabled = false; 3038 if (isIms) { 3039 isVoWifiEnabled = isWfcEnabled(phone); 3040 } 3041 boolean isRttMergeSupported = getCarrierConfig() 3042 .getBoolean(CarrierConfigManager.KEY_ALLOW_MERGING_RTT_CALLS_BOOL); 3043 PhoneAccountHandle phoneAccountHandle = isIms ? PhoneUtils 3044 .makePstnPhoneAccountHandle(phone.getDefaultPhone()) 3045 : PhoneUtils.makePstnPhoneAccountHandle(phone); 3046 TelecomAccountRegistry telecomAccountRegistry = getTelecomAccountRegistry( 3047 getPhone().getContext()); 3048 boolean isConferencingSupported = telecomAccountRegistry 3049 .isMergeCallSupported(phoneAccountHandle); 3050 boolean isImsConferencingSupported = telecomAccountRegistry 3051 .isMergeImsCallSupported(phoneAccountHandle); 3052 mIsCarrierVideoConferencingSupported = telecomAccountRegistry 3053 .isVideoConferencingSupported(phoneAccountHandle); 3054 boolean isMergeOfWifiCallsAllowedWhenVoWifiOff = telecomAccountRegistry 3055 .isMergeOfWifiCallsAllowedWhenVoWifiOff(phoneAccountHandle); 3056 ImsCall imsCall = isImsConnection() 3057 ? ((ImsPhoneConnection) getOriginalConnection()).getImsCall() 3058 : null; 3059 CarrierConfigManager configManager = (CarrierConfigManager) phone.getContext() 3060 .getSystemService(Context.CARRIER_CONFIG_SERVICE); 3061 boolean downGradedVideoCall = false; 3062 if (configManager != null) { 3063 PersistableBundle config = configManager.getConfigForSubId(phone.getSubId()); 3064 if (config != null) { 3065 downGradedVideoCall = config.getBoolean( 3066 CarrierConfigManager.KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL); 3067 } 3068 } 3069 3070 Log.v(this, "refreshConferenceSupported : isConfSupp=%b, isImsConfSupp=%b, " + 3071 "isVidConfSupp=%b, isMergeOfWifiAllowed=%b, " + 3072 "isWifi=%b, isVoWifiEnabled=%b", 3073 isConferencingSupported, isImsConferencingSupported, 3074 mIsCarrierVideoConferencingSupported, isMergeOfWifiCallsAllowedWhenVoWifiOff, 3075 isWifi(), isVoWifiEnabled); 3076 boolean isConferenceSupported = true; 3077 if (mTreatAsEmergencyCall) { 3078 isConferenceSupported = false; 3079 Log.d(this, "refreshConferenceSupported = false; emergency call"); 3080 } else if (isRtt() && !isRttMergeSupported) { 3081 isConferenceSupported = false; 3082 Log.d(this, "refreshConferenceSupported = false; rtt call"); 3083 } else if (!isConferencingSupported || isIms && !isImsConferencingSupported) { 3084 isConferenceSupported = false; 3085 Log.d(this, "refreshConferenceSupported = false; carrier doesn't support conf."); 3086 } else if (isVideoCall && !mIsCarrierVideoConferencingSupported) { 3087 isConferenceSupported = false; 3088 Log.d(this, "refreshConferenceSupported = false; video conf not supported."); 3089 } else if ((imsCall != null) && (imsCall.wasVideoCall() && downGradedVideoCall) 3090 && !mIsCarrierVideoConferencingSupported) { 3091 isConferenceSupported = false; 3092 Log.d(this, 3093 "refreshConferenceSupported = false;" 3094 + " video conf not supported for downgraded audio call."); 3095 } else if (!isMergeOfWifiCallsAllowedWhenVoWifiOff && isWifi() && !isVoWifiEnabled) { 3096 isConferenceSupported = false; 3097 Log.d(this, 3098 "refreshConferenceSupported = false; can't merge wifi calls when voWifi off."); 3099 } else { 3100 Log.d(this, "refreshConferenceSupported = true."); 3101 } 3102 3103 if (isConferenceSupported != isConferenceSupported()) { 3104 setConferenceSupported(isConferenceSupported); 3105 notifyConferenceSupportedChanged(isConferenceSupported); 3106 } 3107 } 3108 3109 @VisibleForTesting isWfcEnabled(Phone phone)3110 boolean isWfcEnabled(Phone phone) { 3111 return ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId()); 3112 } 3113 3114 /** 3115 * Provides a mapping from extras keys which may be found in the 3116 * {@link com.android.internal.telephony.Connection} to their equivalents defined in 3117 * {@link android.telecom.Connection}. 3118 * 3119 * @return Map containing key mappings. 3120 */ createExtrasMap()3121 private static Map<String, String> createExtrasMap() { 3122 Map<String, String> result = new HashMap<String, String>(); 3123 result.put(ImsCallProfile.EXTRA_CHILD_NUMBER, 3124 android.telecom.Connection.EXTRA_CHILD_ADDRESS); 3125 result.put(ImsCallProfile.EXTRA_DISPLAY_TEXT, 3126 android.telecom.Connection.EXTRA_CALL_SUBJECT); 3127 result.put(ImsCallProfile.EXTRA_ADDITIONAL_SIP_INVITE_FIELDS, 3128 android.telecom.Connection.EXTRA_SIP_INVITE); 3129 return Collections.unmodifiableMap(result); 3130 } 3131 isShowingOriginalDialString()3132 private boolean isShowingOriginalDialString() { 3133 boolean showOrigDialString = false; 3134 Phone phone = getPhone(); 3135 if (phone != null && (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) 3136 && !mOriginalConnection.isIncoming()) { 3137 PersistableBundle pb = getCarrierConfig(); 3138 if (pb != null) { 3139 showOrigDialString = pb.getBoolean(CarrierConfigManager 3140 .KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL); 3141 Log.d(this, "showOrigDialString: " + showOrigDialString); 3142 } 3143 } 3144 return showOrigDialString; 3145 } 3146 3147 /** 3148 * Creates a string representation of this {@link TelephonyConnection}. Primarily intended for 3149 * use in log statements. 3150 * 3151 * @return String representation of the connection. 3152 */ 3153 @Override toString()3154 public String toString() { 3155 StringBuilder sb = new StringBuilder(); 3156 sb.append("[TelephonyConnection objId:"); 3157 sb.append(System.identityHashCode(this)); 3158 sb.append(" telecomCallID:"); 3159 sb.append(getTelecomCallId()); 3160 sb.append(" type:"); 3161 if (isImsConnection()) { 3162 sb.append("ims"); 3163 } else if (this instanceof com.android.services.telephony.GsmConnection) { 3164 sb.append("gsm"); 3165 } else if (this instanceof CdmaConnection) { 3166 sb.append("cdma"); 3167 } 3168 sb.append(" state:"); 3169 sb.append(Connection.stateToString(getState())); 3170 sb.append(" capabilities:"); 3171 sb.append(capabilitiesToString(getConnectionCapabilities())); 3172 sb.append(" properties:"); 3173 sb.append(propertiesToString(getConnectionProperties())); 3174 sb.append(" address:"); 3175 sb.append(Rlog.pii(LOG_TAG, getAddress())); 3176 sb.append(" originalConnection:"); 3177 sb.append(mOriginalConnection); 3178 sb.append(" partOfConf:"); 3179 if (getConference() == null) { 3180 sb.append("N"); 3181 } else { 3182 sb.append("Y"); 3183 } 3184 sb.append(" confSupported:"); 3185 sb.append(mIsConferenceSupported ? "Y" : "N"); 3186 sb.append(" isAdhocConf:"); 3187 sb.append(isAdhocConferenceCall() ? "Y" : "N"); 3188 sb.append("]"); 3189 return sb.toString(); 3190 } 3191 setTelephonyConnectionService(TelephonyConnectionService connectionService)3192 public final void setTelephonyConnectionService(TelephonyConnectionService connectionService) { 3193 mTelephonyConnectionService = connectionService; 3194 } 3195 getTelephonyConnectionService()3196 public final TelephonyConnectionService getTelephonyConnectionService() { 3197 return mTelephonyConnectionService; 3198 } 3199 3200 /** 3201 * Set this {@link TelephonyConnection} to an active state. 3202 * <p> 3203 * Note: This should be used instead of {@link #setActive()} to ensure listeners are notified. 3204 */ setTelephonyConnectionActive()3205 public void setTelephonyConnectionActive() { 3206 setActive(); 3207 notifyStateChanged(getState()); 3208 } 3209 3210 /** 3211 * Set this {@link TelephonyConnection} to a ringing state. 3212 * <p> 3213 * Note: This should be used instead of {@link #setRinging()} to ensure listeners are notified. 3214 */ setTelephonyConnectionRinging()3215 public void setTelephonyConnectionRinging() { 3216 setRinging(); 3217 notifyStateChanged(getState()); 3218 } 3219 3220 /** 3221 * Set this {@link TelephonyConnection} to an initializing state. 3222 * <p> 3223 * Note: This should be used instead of {@link #setInitializing()} to ensure listeners are 3224 * notified. 3225 */ setTelephonyConnectionInitializing()3226 public void setTelephonyConnectionInitializing() { 3227 setInitializing(); 3228 notifyStateChanged(getState()); 3229 } 3230 3231 /** 3232 * Set this {@link TelephonyConnection} to a dialing state. 3233 * <p> 3234 * Note: This should be used instead of {@link #setDialing()} to ensure listeners are notified. 3235 */ setTelephonyConnectionDialing()3236 public void setTelephonyConnectionDialing() { 3237 setDialing(); 3238 notifyStateChanged(getState()); 3239 } 3240 3241 /** 3242 * Set this {@link TelephonyConnection} to a pulling state. 3243 * <p> 3244 * Note: This should be used instead of {@link #setPulling()} to ensure listeners are notified. 3245 */ setTelephonyConnectionPulling()3246 public void setTelephonyConnectionPulling() { 3247 setPulling(); 3248 notifyStateChanged(getState()); 3249 } 3250 3251 /** 3252 * Set this {@link TelephonyConnection} to a held state. 3253 * <p> 3254 * Note: This should be used instead of {@link #setOnHold()} to ensure listeners are notified. 3255 */ setTelephonyConnectionOnHold()3256 public void setTelephonyConnectionOnHold() { 3257 setOnHold(); 3258 notifyStateChanged(getState()); 3259 } 3260 3261 /** 3262 * Set this {@link TelephonyConnection} to a disconnected state. 3263 * <p> 3264 * Note: This should be used instead of 3265 * {@link #setDisconnected(android.telecom.DisconnectCause)} to ensure listeners are notified. 3266 */ setTelephonyConnectionDisconnected(@onNull android.telecom.DisconnectCause disconnectCause)3267 public void setTelephonyConnectionDisconnected(@NonNull 3268 android.telecom.DisconnectCause disconnectCause) { 3269 setDisconnected(disconnectCause); 3270 notifyDisconnected(disconnectCause); 3271 notifyStateChanged(getState()); 3272 } 3273 3274 /** 3275 * Sends a connection event for this {@link TelephonyConnection}. 3276 * <p> 3277 * Note: This should be used instead of {@link #sendConnectionEvent(String, Bundle)} to ensure 3278 * listeners are notified. 3279 */ sendTelephonyConnectionEvent(@onNull String event, @Nullable Bundle extras)3280 public void sendTelephonyConnectionEvent(@NonNull String event, @Nullable Bundle extras) { 3281 sendConnectionEvent(event, extras); 3282 notifyTelephonyConnectionEvent(event, extras); 3283 } 3284 3285 /** 3286 * Sets the extras associated with this {@link TelephonyConnection}. 3287 * <p> 3288 * Note: This should be used instead of {@link #putExtras(Bundle)} to ensure listeners are 3289 * notified. 3290 */ putTelephonyExtras(@onNull Bundle extras)3291 public void putTelephonyExtras(@NonNull Bundle extras) { 3292 putExtras(extras); 3293 notifyPutExtras(extras); 3294 } 3295 3296 /** 3297 * Removes the specified extras associated with this {@link TelephonyConnection}. 3298 * <p> 3299 * Note: This should be used instead of {@link #removeExtras(String...)} to ensure listeners are 3300 * notified. 3301 */ removeTelephonyExtras(@onNull List<String> keys)3302 public void removeTelephonyExtras(@NonNull List<String> keys) { 3303 removeExtras(keys); 3304 notifyRemoveExtras(keys); 3305 } 3306 3307 /** 3308 * Sets the video state associated with this {@link TelephonyConnection}. 3309 * <p> 3310 * Note: This should be used instead of {@link #setVideoState(int)} to ensure listeners are 3311 * notified. 3312 * @param videoState The new video state. Valid values: 3313 * {@link VideoProfile#STATE_AUDIO_ONLY}, 3314 * {@link VideoProfile#STATE_BIDIRECTIONAL}, 3315 * {@link VideoProfile#STATE_TX_ENABLED}, 3316 * {@link VideoProfile#STATE_RX_ENABLED}. 3317 */ setTelephonyVideoState(int videoState)3318 public void setTelephonyVideoState(int videoState) { 3319 setVideoState(videoState); 3320 notifyVideoStateChanged(videoState); 3321 } 3322 3323 /** 3324 * Sets the video provider associated with this {@link TelephonyConnection}. 3325 * <p> 3326 * Note: This should be used instead of {@link #setVideoProvider(VideoProvider)} to ensure 3327 * listeners are notified. 3328 */ setTelephonyVideoProvider(@ullable VideoProvider videoProvider)3329 public void setTelephonyVideoProvider(@Nullable VideoProvider videoProvider) { 3330 setVideoProvider(videoProvider); 3331 notifyVideoProviderChanged(videoProvider); 3332 } 3333 3334 /** 3335 * Sets the status hints associated with this {@link TelephonyConnection}. 3336 * <p> 3337 * Note: This should be used instead of {@link #setStatusHints(StatusHints)} to ensure listeners 3338 * are notified. 3339 */ setTelephonyStatusHints(@ullable StatusHints statusHints)3340 public void setTelephonyStatusHints(@Nullable StatusHints statusHints) { 3341 setStatusHints(statusHints); 3342 notifyStatusHintsChanged(statusHints); 3343 } 3344 3345 /** 3346 * Sets RIL voice radio technology used for current connection. 3347 * <p> 3348 * This property is set by the Telephony {@link ConnectionService}. 3349 * 3350 * @param vrat the RIL Voice Radio Technology used for current connection, 3351 * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}. 3352 */ setCallRadioTech(@ilRadioTechnology int vrat)3353 public final void setCallRadioTech(@RilRadioTechnology int vrat) { 3354 Bundle extras = getExtras(); 3355 if (extras == null) { 3356 extras = new Bundle(); 3357 } 3358 extras.putInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE, 3359 ServiceState.rilRadioTechnologyToNetworkType(vrat)); 3360 putExtras(extras); 3361 // Propagates the call radio technology to its parent {@link android.telecom.Conference} 3362 // This action only covers non-IMS CS conference calls. 3363 // For IMS PS call conference call, it can be updated via its host connection 3364 // {@link #Listener.onExtrasChanged} event. 3365 if (getConference() != null) { 3366 Bundle newExtras = new Bundle(); 3367 newExtras.putInt( 3368 TelecomManager.EXTRA_CALL_NETWORK_TYPE, 3369 ServiceState.rilRadioTechnologyToNetworkType(vrat)); 3370 getConference().putExtras(newExtras); 3371 } 3372 } 3373 3374 /** 3375 * Returns RIL voice radio technology used for current connection. 3376 * <p> 3377 * Used by the Telephony {@link ConnectionService}. 3378 * 3379 * @return the RIL voice radio technology used for current connection, 3380 * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}. 3381 */ getCallRadioTech()3382 public final @RilRadioTechnology int getCallRadioTech() { 3383 int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; 3384 Bundle extras = getExtras(); 3385 if (extras != null) { 3386 voiceNetworkType = extras.getInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE, 3387 TelephonyManager.NETWORK_TYPE_UNKNOWN); 3388 } 3389 return ServiceState.networkTypeToRilRadioTechnology(voiceNetworkType); 3390 } 3391 3392 /** 3393 * Notifies {@link TelephonyConnectionListener}s of a change to conference participant data 3394 * received via the {@link ImsConference} (i.e. conference event package). 3395 * 3396 * @param conferenceParticipants The participants. 3397 */ updateConferenceParticipants( @onNull List<ConferenceParticipant> conferenceParticipants)3398 private void updateConferenceParticipants( 3399 @NonNull List<ConferenceParticipant> conferenceParticipants) { 3400 for (TelephonyConnectionListener l : mTelephonyListeners) { 3401 l.onConferenceParticipantsChanged(this, conferenceParticipants); 3402 } 3403 } 3404 3405 /** 3406 * Where device to device communication is available and this is an IMS call, configures the 3407 * D2D communication infrastructure for operation. 3408 */ maybeConfigureDeviceToDeviceCommunication()3409 private void maybeConfigureDeviceToDeviceCommunication() { 3410 if (!getPhone().getContext().getResources().getBoolean( 3411 R.bool.config_use_device_to_device_communication)) { 3412 Log.i(this, "maybeConfigureDeviceToDeviceCommunication: not using D2D."); 3413 notifyD2DAvailabilityChanged(false); 3414 return; 3415 } 3416 if (!isImsConnection()) { 3417 Log.i(this, "maybeConfigureDeviceToDeviceCommunication: not an IMS connection."); 3418 if (mCommunicator != null) { 3419 mCommunicator = null; 3420 } 3421 notifyD2DAvailabilityChanged(false); 3422 return; 3423 } 3424 if (mTreatAsEmergencyCall || mIsNetworkIdentifiedEmergencyCall) { 3425 Log.i(this, "maybeConfigureDeviceToDeviceCommunication: emergency call; no D2D"); 3426 notifyD2DAvailabilityChanged(false); 3427 return; 3428 } 3429 3430 ArrayList<TransportProtocol> supportedTransports = new ArrayList<>(2); 3431 3432 if (supportsD2DUsingRtp()) { 3433 Log.i(this, "maybeConfigureDeviceToDeviceCommunication: carrier supports RTP."); 3434 // Implement abstracted out RTP functionality the RTP transport depends on. 3435 RtpAdapter rtpAdapter = new RtpAdapter() { 3436 @Override 3437 public Set<RtpHeaderExtensionType> getAcceptedRtpHeaderExtensions() { 3438 ImsPhoneConnection originalConnection = 3439 (ImsPhoneConnection) mOriginalConnection; 3440 return originalConnection.getAcceptedRtpHeaderExtensions(); 3441 } 3442 3443 @Override 3444 public void sendRtpHeaderExtensions( 3445 @NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) { 3446 Log.i(TelephonyConnection.this, "sendRtpHeaderExtensions: sending: %s", 3447 rtpHeaderExtensions.stream() 3448 .map(r -> r.toString()) 3449 .collect(Collectors.joining(","))); 3450 ImsPhoneConnection originalConnection = 3451 (ImsPhoneConnection) mOriginalConnection; 3452 originalConnection.sendRtpHeaderExtensions(rtpHeaderExtensions); 3453 } 3454 }; 3455 mRtpTransport = new RtpTransport(rtpAdapter, null /* TODO: not needed yet */, mHandler, 3456 supportsSdpNegotiationOfRtpHeaderExtensions()); 3457 supportedTransports.add(mRtpTransport); 3458 } 3459 if (supportsD2DUsingDtmf()) { 3460 Log.i(this, "maybeConfigureDeviceToDeviceCommunication: carrier supports DTMF."); 3461 DtmfAdapter dtmfAdapter = digit -> { 3462 Log.i(TelephonyConnection.this, "sendDtmf: send digit %c", digit); 3463 ImsPhoneConnection originalConnection = 3464 (ImsPhoneConnection) mOriginalConnection; 3465 Message dtmfComplete = mHandler.obtainMessage(MSG_DTMF_DONE); 3466 dtmfComplete.replyTo = mHandlerMessenger; 3467 originalConnection.getImsCall().sendDtmf(digit, dtmfComplete); 3468 }; 3469 ContentResolver cr = getPhone().getContext().getContentResolver(); 3470 mDtmfTransport = new DtmfTransport(dtmfAdapter, new Timeouts.Adapter(cr), 3471 Executors.newSingleThreadScheduledExecutor()); 3472 supportedTransports.add(mDtmfTransport); 3473 } 3474 if (supportedTransports.size() > 0) { 3475 mCommunicator = new Communicator(supportedTransports, this); 3476 mD2DCallStateAdapter = new D2DCallStateAdapter(mCommunicator); 3477 addTelephonyConnectionListener(mD2DCallStateAdapter); 3478 } else { 3479 Log.i(this, "maybeConfigureDeviceToDeviceCommunication: no transports; disabled."); 3480 notifyD2DAvailabilityChanged(false); 3481 } 3482 } 3483 3484 /** 3485 * Notifies upper layers of the availability of D2D communication. 3486 * @param isAvailable {@code true} if D2D is available, {@code false} otherwise. 3487 */ notifyD2DAvailabilityChanged(boolean isAvailable)3488 private void notifyD2DAvailabilityChanged(boolean isAvailable) { 3489 Bundle extras = new Bundle(); 3490 extras.putBoolean(Connection.EXTRA_IS_DEVICE_TO_DEVICE_COMMUNICATION_AVAILABLE, 3491 isAvailable); 3492 putTelephonyExtras(extras); 3493 } 3494 3495 /** 3496 * @return The D2D communication class, or {@code null} if not set up. 3497 */ getCommunicator()3498 public @Nullable Communicator getCommunicator() { 3499 return mCommunicator; 3500 } 3501 3502 /** 3503 * Called by {@link Communicator} associated with this {@link TelephonyConnection} when there 3504 * are incoming device-to-device messages received. 3505 * @param messages the incoming messages. 3506 */ 3507 @Override onMessagesReceived(@onNull Set<Communicator.Message> messages)3508 public void onMessagesReceived(@NonNull Set<Communicator.Message> messages) { 3509 Log.i(this, "onMessagesReceived: got d2d messages: %s", messages); 3510 // Send connection events up to Telecom so that we can relay the messages to a valid 3511 // CallDiagnosticService which is bound. 3512 for (Communicator.Message msg : messages) { 3513 Integer dcMsgType = MessageTypeAndValueHelper.MSG_TYPE_TO_DC_MSG_TYPE.getValue( 3514 msg.getType()); 3515 if (dcMsgType == null) { 3516 // Invalid msg type, skip. 3517 continue; 3518 } 3519 3520 Integer dcMsgValue; 3521 switch (msg.getType()) { 3522 case CallDiagnostics.MESSAGE_CALL_AUDIO_CODEC: 3523 dcMsgValue = MessageTypeAndValueHelper.CODEC_TO_DC_CODEC.getValue( 3524 msg.getValue()); 3525 break; 3526 case CallDiagnostics.MESSAGE_CALL_NETWORK_TYPE: 3527 dcMsgValue = MessageTypeAndValueHelper.RAT_TYPE_TO_DC_NETWORK_TYPE.getValue( 3528 msg.getValue()); 3529 break; 3530 case CallDiagnostics.MESSAGE_DEVICE_BATTERY_STATE: 3531 dcMsgValue = MessageTypeAndValueHelper.BATTERY_STATE_TO_DC_BATTERY_STATE 3532 .getValue(msg.getValue()); 3533 break; 3534 case CallDiagnostics.MESSAGE_DEVICE_NETWORK_COVERAGE: 3535 dcMsgValue = MessageTypeAndValueHelper.COVERAGE_TO_DC_COVERAGE 3536 .getValue(msg.getValue()); 3537 break; 3538 default: 3539 Log.w(this, "onMessagesReceived: msg=%d - invalid msg", msg.getValue()); 3540 continue; 3541 } 3542 if (dcMsgValue == null) { 3543 Log.w(this, "onMessagesReceived: msg=%d/%d - invalid msg value", msg.getType(), 3544 msg.getValue()); 3545 continue; 3546 } 3547 Bundle extras = new Bundle(); 3548 extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE, dcMsgType); 3549 extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE, dcMsgValue); 3550 sendConnectionEvent(Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE, extras); 3551 } 3552 } 3553 3554 /** 3555 * Handles report from {@link Communicator} when the availability of D2D changes. 3556 * @param isAvailable {@code true} if D2D is available, {@code false} if unavailable. 3557 */ 3558 @Override onD2DAvailabilitychanged(boolean isAvailable)3559 public void onD2DAvailabilitychanged(boolean isAvailable) { 3560 notifyD2DAvailabilityChanged(isAvailable); 3561 } 3562 3563 /** 3564 * Called by a {@link ConnectionService} to notify Telecom that a {@link Conference#onMerge()} 3565 * operation has started. 3566 */ notifyConferenceStarted()3567 protected void notifyConferenceStarted() { 3568 for (TelephonyConnectionListener l : mTelephonyListeners) { 3569 l.onConferenceStarted(); 3570 } 3571 } 3572 3573 /** 3574 * Notifies {@link TelephonyConnectionListener}s when a change has occurred to the Connection 3575 * which impacts its ability to be a part of a conference call. 3576 * @param isConferenceSupported {@code true} if the connection supports being part of a 3577 * conference call, {@code false} otherwise. 3578 */ notifyConferenceSupportedChanged(boolean isConferenceSupported)3579 private void notifyConferenceSupportedChanged(boolean isConferenceSupported) { 3580 for (TelephonyConnectionListener l : mTelephonyListeners) { 3581 l.onConferenceSupportedChanged(this, isConferenceSupported); 3582 } 3583 } 3584 3585 /** 3586 * Notifies {@link TelephonyConnectionListener}s of changes to the connection capabilities. 3587 * @param newCapabilities the new capabilities. 3588 */ notifyConnectionCapabilitiesChanged(int newCapabilities)3589 private void notifyConnectionCapabilitiesChanged(int newCapabilities) { 3590 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3591 listener.onConnectionCapabilitiesChanged(this, newCapabilities); 3592 } 3593 } 3594 3595 /** 3596 * Notifies {@link TelephonyConnectionListener}s of changes to the connection properties. 3597 * @param newProperties the new properties. 3598 */ notifyConnectionPropertiesChanged(int newProperties)3599 private void notifyConnectionPropertiesChanged(int newProperties) { 3600 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3601 listener.onConnectionPropertiesChanged(this, newProperties); 3602 } 3603 } 3604 3605 /** 3606 * Notifies {@link TelephonyConnectionListener}s when a connection is destroyed. 3607 */ notifyDestroyed()3608 private void notifyDestroyed() { 3609 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3610 listener.onDestroyed(this); 3611 } 3612 } 3613 3614 /** 3615 * Notifies {@link TelephonyConnectionListener}s when a connection disconnects. 3616 * @param cause The disconnect cause. 3617 */ notifyDisconnected(android.telecom.DisconnectCause cause)3618 private void notifyDisconnected(android.telecom.DisconnectCause cause) { 3619 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3620 listener.onDisconnected(this, cause); 3621 } 3622 } 3623 3624 /** 3625 * Notifies {@link TelephonyConnectionListener}s of connection state changes. 3626 * @param newState The new state. 3627 */ notifyStateChanged(int newState)3628 private void notifyStateChanged(int newState) { 3629 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3630 listener.onStateChanged(this, newState); 3631 } 3632 } 3633 3634 /** 3635 * Notifies {@link TelephonyConnectionListener}s of telephony connection events. 3636 * @param event The event. 3637 * @param extras Any extras. 3638 */ notifyTelephonyConnectionEvent(String event, Bundle extras)3639 private void notifyTelephonyConnectionEvent(String event, Bundle extras) { 3640 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3641 listener.onConnectionEvent(this, event, extras); 3642 } 3643 } 3644 3645 /** 3646 * Notifies {@link TelephonyConnectionListener}s when extras are added to the connection. 3647 * @param extras The new extras. 3648 */ notifyPutExtras(Bundle extras)3649 private void notifyPutExtras(Bundle extras) { 3650 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3651 listener.onExtrasChanged(this, extras); 3652 } 3653 } 3654 3655 /** 3656 * Notifies {@link TelephonyConnectionListener}s when extra keys are removed from a connection. 3657 * @param keys The removed keys. 3658 */ notifyRemoveExtras(List<String> keys)3659 private void notifyRemoveExtras(List<String> keys) { 3660 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3661 listener.onExtrasRemoved(this, keys); 3662 } 3663 } 3664 3665 /** 3666 * Notifies {@link TelephonyConnectionListener}s of a change to the video state of a connection. 3667 * @param videoState The new video state. Valid values: 3668 * {@link VideoProfile#STATE_AUDIO_ONLY}, 3669 * {@link VideoProfile#STATE_BIDIRECTIONAL}, 3670 * {@link VideoProfile#STATE_TX_ENABLED}, 3671 * {@link VideoProfile#STATE_RX_ENABLED}. 3672 */ notifyVideoStateChanged(int videoState)3673 private void notifyVideoStateChanged(int videoState) { 3674 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3675 listener.onVideoStateChanged(this, videoState); 3676 } 3677 } 3678 3679 /** 3680 * Notifies {@link TelephonyConnectionListener}s of a whether to play Ringback Tone or not. 3681 * @param ringback Whether the ringback tone is to be played 3682 */ notifyRingbackRequested(boolean ringback)3683 private void notifyRingbackRequested(boolean ringback) { 3684 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3685 listener.onRingbackRequested(this, ringback); 3686 } 3687 } 3688 3689 /** 3690 * Notifies {@link TelephonyConnectionListener}s of changes to the video provider for a 3691 * connection. 3692 * @param videoProvider The new video provider. 3693 */ notifyVideoProviderChanged(VideoProvider videoProvider)3694 private void notifyVideoProviderChanged(VideoProvider videoProvider) { 3695 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3696 listener.onVideoProviderChanged(this, videoProvider); 3697 } 3698 } 3699 3700 /** 3701 * Notifies {@link TelephonyConnectionListener}s of changes to the status hints for a 3702 * connection. 3703 * @param statusHints The new status hints. 3704 */ notifyStatusHintsChanged(StatusHints statusHints)3705 private void notifyStatusHintsChanged(StatusHints statusHints) { 3706 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3707 listener.onStatusHintsChanged(this, statusHints); 3708 } 3709 } 3710 3711 /** 3712 * Whether the incoming call number should be formatted to national number for Japan. 3713 * @return {@code true} should be convert to the national format, {@code false} otherwise. 3714 */ isNeededToFormatIncomingNumberForJp()3715 private boolean isNeededToFormatIncomingNumberForJp() { 3716 if (mOriginalConnection.isIncoming() 3717 && !TextUtils.isEmpty(mOriginalConnection.getAddress()) 3718 && mOriginalConnection.getAddress().startsWith(JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN)) { 3719 PersistableBundle b = getCarrierConfig(); 3720 return b != null && b.getBoolean( 3721 CarrierConfigManager.KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL); 3722 } 3723 return false; 3724 } 3725 3726 /** 3727 * Format the incoming call number to national number for Japan. 3728 * @param number 3729 * @return the formatted phone number (e.g, "+819012345678" -> "09012345678") 3730 */ formatIncomingNumberForJp(String number)3731 private String formatIncomingNumberForJp(String number) { 3732 return PhoneNumberUtils.stripSeparators( 3733 PhoneNumberUtils.formatNumber(number, JAPAN_ISO_COUNTRY_CODE)); 3734 } 3735 getTelecomAccountRegistry(Context context)3736 public TelecomAccountRegistry getTelecomAccountRegistry(Context context) { 3737 return TelecomAccountRegistry.getInstance(context); 3738 } 3739 3740 /** 3741 * @return {@code true} if the carrier supports D2D using RTP header extensions, {@code false} 3742 * otherwise. 3743 */ supportsD2DUsingRtp()3744 private boolean supportsD2DUsingRtp() { 3745 PersistableBundle b = getCarrierConfig(); 3746 return b != null && b.getBoolean( 3747 CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL); 3748 } 3749 3750 /** 3751 * @return {@code true} if the carrier supports D2D using DTMF digits, {@code false} otherwise. 3752 */ supportsD2DUsingDtmf()3753 private boolean supportsD2DUsingDtmf() { 3754 PersistableBundle b = getCarrierConfig(); 3755 return b != null && b.getBoolean( 3756 CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL); 3757 } 3758 3759 /** 3760 * @return {@code true} if the carrier supports using SDP negotiation for the RTP header 3761 * extensions used in D2D comms, {@code false} otherwise. 3762 */ supportsSdpNegotiationOfRtpHeaderExtensions()3763 private boolean supportsSdpNegotiationOfRtpHeaderExtensions() { 3764 PersistableBundle b = getCarrierConfig(); 3765 return b != null && b.getBoolean( 3766 CarrierConfigManager 3767 .KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL); 3768 } 3769 3770 /** 3771 * Handles a device to device message which a {@link CallDiagnostics} wishes to send. 3772 * @param extras the call event extras bundle. 3773 */ handleOutgoingDeviceToDeviceMessage(Bundle extras)3774 private void handleOutgoingDeviceToDeviceMessage(Bundle extras) { 3775 int messageType = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE); 3776 int messageValue = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE); 3777 3778 Integer internalMessageValue; 3779 switch (messageType) { 3780 case CallDiagnostics.MESSAGE_CALL_AUDIO_CODEC: 3781 internalMessageValue = MessageTypeAndValueHelper.CODEC_TO_DC_CODEC.getKey( 3782 messageValue); 3783 break; 3784 case CallDiagnostics.MESSAGE_CALL_NETWORK_TYPE: 3785 internalMessageValue = MessageTypeAndValueHelper.RAT_TYPE_TO_DC_NETWORK_TYPE.getKey( 3786 messageValue); 3787 break; 3788 case CallDiagnostics.MESSAGE_DEVICE_BATTERY_STATE: 3789 internalMessageValue = MessageTypeAndValueHelper.BATTERY_STATE_TO_DC_BATTERY_STATE 3790 .getKey(messageValue); 3791 break; 3792 case CallDiagnostics.MESSAGE_DEVICE_NETWORK_COVERAGE: 3793 internalMessageValue = MessageTypeAndValueHelper.COVERAGE_TO_DC_COVERAGE 3794 .getKey(messageValue); 3795 break; 3796 default: 3797 Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d - invalid msg", 3798 messageType); 3799 return; 3800 } 3801 Integer internalMessageType = MessageTypeAndValueHelper.MSG_TYPE_TO_DC_MSG_TYPE.getKey( 3802 messageType); 3803 if (internalMessageValue == null) { 3804 Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d/%d - invalid value", 3805 messageType, messageValue); 3806 return; 3807 } 3808 3809 if (mCommunicator != null) { 3810 Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d/%d - sending", 3811 internalMessageType, internalMessageValue); 3812 Set<Communicator.Message> set = new ArraySet<>(); 3813 set.add(new Communicator.Message(internalMessageType, internalMessageValue)); 3814 mCommunicator.sendMessages(set); 3815 } 3816 } 3817 3818 /** 3819 * Returns the current telephony connection listeners for test purposes. 3820 * @return list of telephony connection listeners. 3821 */ 3822 @VisibleForTesting getTelephonyConnectionListeners()3823 public List<TelephonyConnectionListener> getTelephonyConnectionListeners() { 3824 return new ArrayList<>(mTelephonyListeners); 3825 } 3826 } 3827