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