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 if (!com.android.server.telecom.flags.Flags.enableCallSequencing()) { 1342 mTelephonyConnectionService.maybeUnholdCallsOnOtherSubs( 1343 getPhoneAccountHandle()); 1344 } 1345 return; 1346 } 1347 phone.switchHoldingAndActive(); 1348 } 1349 1350 // TODO: Cdma calls are slightly different. 1351 } catch (CallStateException e) { 1352 Log.e(this, e, "Exception occurred while trying to put call on hold."); 1353 } 1354 } else { 1355 Log.w(this, "Cannot put a call that is not currently active on hold."); 1356 } 1357 } 1358 performUnhold()1359 public void performUnhold() { 1360 Log.v(this, "performUnhold"); 1361 if (Call.State.HOLDING == mConnectionState) { 1362 try { 1363 Phone phone = mOriginalConnection.getCall().getPhone(); 1364 // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic. 1365 if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) { 1366 ImsPhone imsPhone = (ImsPhone) phone; 1367 imsPhone.unholdHeldCall(); 1368 return; 1369 } 1370 // Here's the deal--Telephony hold/unhold is weird because whenever there exists 1371 // more than one call, one of them must always be active. In other words, if you 1372 // have an active call and holding call, and you put the active call on hold, it 1373 // will automatically activate the holding call. This is weird with how Telecom 1374 // sends its commands. When a user opts to "unhold" a background call, telecom 1375 // issues hold commands to all active calls, and then the unhold command to the 1376 // background call. This means that we get two commands...each of which reduces to 1377 // switchHoldingAndActive(). The result is that they simply cancel each other out. 1378 // To fix this so that it works well with telecom we add a minor hack. If we 1379 // have one telephony call, everything works as normally expected. But if we have 1380 // two or more calls, we will ignore all requests to "unhold" knowing that the hold 1381 // requests already do what we want. If you've read up to this point, I'm very sorry 1382 // that we are doing this. I didn't think of a better solution that wouldn't also 1383 // make the Telecom APIs very ugly. 1384 1385 if (!hasMultipleTopLevelCalls()) { 1386 mOriginalConnection.getCall().getPhone().switchHoldingAndActive(); 1387 } else { 1388 Log.i(this, "Skipping unhold command for %s", this); 1389 } 1390 } catch (CallStateException e) { 1391 Log.e(this, e, "Exception occurred while trying to release call from hold."); 1392 } 1393 } else { 1394 Log.w(this, "Cannot release a call that is not already on hold from hold."); 1395 } 1396 } 1397 performConference(Connection otherConnection)1398 public void performConference(Connection otherConnection) { 1399 Log.d(this, "performConference - %s", this); 1400 if (getPhone() != null) { 1401 try { 1402 // We dont use the "other" connection because there is no concept of that in the 1403 // implementation of calls inside telephony. Basically, you can "conference" and it 1404 // will conference with the background call. We know that otherConnection is the 1405 // background call because it would never have called setConferenceableConnections() 1406 // otherwise. 1407 getPhone().conference(); 1408 } catch (CallStateException e) { 1409 Log.e(this, e, "Failed to conference call."); 1410 } 1411 } 1412 } 1413 getAddConferenceParticipants(List<Uri> participants)1414 private String[] getAddConferenceParticipants(List<Uri> participants) { 1415 String[] addConfParticipants = new String[participants.size()]; 1416 int i = 0; 1417 for (Uri participant : participants) { 1418 addConfParticipants[i] = participant.getSchemeSpecificPart(); 1419 i++; 1420 } 1421 return addConfParticipants; 1422 } 1423 performAddConferenceParticipants(List<Uri> participants)1424 public void performAddConferenceParticipants(List<Uri> participants) { 1425 Log.v(this, "performAddConferenceParticipants"); 1426 if (mOriginalConnection.getCall() instanceof ImsPhoneCall) { 1427 ImsPhoneCall imsPhoneCall = (ImsPhoneCall)mOriginalConnection.getCall(); 1428 try { 1429 imsPhoneCall.getImsCall().inviteParticipants( 1430 getAddConferenceParticipants(participants)); 1431 } catch(ImsException e) { 1432 Log.e(this, e, "failed to add conference participants"); 1433 } 1434 } 1435 } 1436 1437 /** 1438 * Builds connection capabilities common to all TelephonyConnections. Namely, apply IMS-based 1439 * capabilities. 1440 */ buildConnectionCapabilities()1441 protected int buildConnectionCapabilities() { 1442 int callCapabilities = 0; 1443 if (mOriginalConnection != null && mOriginalConnection.isIncoming()) { 1444 callCapabilities |= CAPABILITY_SPEED_UP_MT_AUDIO; 1445 } 1446 if (!shouldTreatAsEmergencyCall() && isImsConnection() && canHoldImsCalls()) { 1447 callCapabilities |= CAPABILITY_SUPPORT_HOLD; 1448 if (mIsHoldable && (getState() == STATE_ACTIVE || getState() == STATE_HOLDING)) { 1449 callCapabilities |= CAPABILITY_HOLD; 1450 } 1451 } 1452 1453 Log.d(this, "buildConnectionCapabilities: isHoldable = " 1454 + mIsHoldable + " State = " + getState() + " capabilities = " + callCapabilities); 1455 1456 return callCapabilities; 1457 } 1458 updateConnectionCapabilities()1459 protected final void updateConnectionCapabilities() { 1460 int newCapabilities = buildConnectionCapabilities(); 1461 1462 newCapabilities = applyOriginalConnectionCapabilities(newCapabilities); 1463 newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PAUSE_VIDEO, 1464 mIsVideoPauseSupported && isVideoCapable()); 1465 newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PULL_CALL, 1466 isExternalConnection() && isPullable()); 1467 newCapabilities = applyConferenceTerminationCapabilities(newCapabilities); 1468 newCapabilities = changeBitmask(newCapabilities, CAPABILITY_SUPPORT_DEFLECT, 1469 isImsConnection() && canDeflectImsCalls()); 1470 1471 newCapabilities = applyAddParticipantCapabilities(newCapabilities); 1472 newCapabilities = changeBitmask(newCapabilities, CAPABILITY_TRANSFER_CONSULTATIVE, 1473 isImsConnection() && canConsultativeTransfer()); 1474 newCapabilities = changeBitmask(newCapabilities, CAPABILITY_TRANSFER, 1475 isImsConnection() && canTransferToNumber()); 1476 1477 if (getConnectionCapabilities() != newCapabilities) { 1478 setConnectionCapabilities(newCapabilities); 1479 notifyConnectionCapabilitiesChanged(newCapabilities); 1480 } 1481 } 1482 buildConnectionProperties()1483 protected int buildConnectionProperties() { 1484 int connectionProperties = 0; 1485 1486 // If the phone is in ECM mode, mark the call to indicate that the callback number should be 1487 // shown. 1488 Phone phone = getPhone(); 1489 if (phone != null && phone.isInEcm()) { 1490 connectionProperties |= PROPERTY_EMERGENCY_CALLBACK_MODE; 1491 } 1492 1493 return connectionProperties; 1494 } 1495 1496 /** 1497 * Updates the properties of the connection. 1498 */ updateConnectionProperties()1499 protected final void updateConnectionProperties() { 1500 int newProperties = buildConnectionProperties(); 1501 1502 newProperties = changeBitmask(newProperties, PROPERTY_HIGH_DEF_AUDIO, 1503 hasHighDefAudioProperty()); 1504 newProperties = changeBitmask(newProperties, PROPERTY_WIFI, isWifi() && !isCrossSimCall()); 1505 newProperties = changeBitmask(newProperties, PROPERTY_IS_EXTERNAL_CALL, 1506 isExternalConnection()); 1507 newProperties = changeBitmask(newProperties, PROPERTY_HAS_CDMA_VOICE_PRIVACY, 1508 mIsCdmaVoicePrivacyEnabled); 1509 newProperties = changeBitmask(newProperties, PROPERTY_ASSISTED_DIALING, 1510 mIsUsingAssistedDialing); 1511 newProperties = changeBitmask(newProperties, PROPERTY_IS_RTT, isRtt()); 1512 newProperties = changeBitmask(newProperties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL, 1513 isNetworkIdentifiedEmergencyCall()); 1514 newProperties = changeBitmask(newProperties, PROPERTY_IS_ADHOC_CONFERENCE, 1515 isAdhocConferenceCall()); 1516 newProperties = changeBitmask(newProperties, PROPERTY_CROSS_SIM, 1517 isCrossSimCall()); 1518 1519 if (getConnectionProperties() != newProperties) { 1520 setTelephonyConnectionProperties(newProperties); 1521 } 1522 } 1523 setTelephonyConnectionProperties(int newProperties)1524 public void setTelephonyConnectionProperties(int newProperties) { 1525 setConnectionProperties(newProperties); 1526 notifyConnectionPropertiesChanged(newProperties); 1527 } 1528 updateAddress()1529 protected final void updateAddress() { 1530 updateConnectionCapabilities(); 1531 updateConnectionProperties(); 1532 if (mOriginalConnection != null) { 1533 Uri address; 1534 if (isShowingOriginalDialString() 1535 && mOriginalConnection.getOrigDialString() != null) { 1536 address = getAddressFromNumber(mOriginalConnection.getOrigDialString()); 1537 } else if (isNeededToFormatIncomingNumberForJp()) { 1538 address = getAddressFromNumber( 1539 formatIncomingNumberForJp(mOriginalConnection.getAddress())); 1540 } else { 1541 address = getAddressFromNumber(mOriginalConnection.getAddress()); 1542 } 1543 int presentation = mOriginalConnection.getNumberPresentation(); 1544 if (!Objects.equals(address, getAddress()) || 1545 presentation != getAddressPresentation()) { 1546 Log.v(this, "updateAddress, address changed"); 1547 if ((getConnectionProperties() & PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0) { 1548 address = null; 1549 } 1550 setAddress(address, presentation); 1551 } 1552 1553 String name = filterCnapName(mOriginalConnection.getCnapName()); 1554 int namePresentation = mOriginalConnection.getCnapNamePresentation(); 1555 if (!Objects.equals(name, getCallerDisplayName()) || 1556 namePresentation != getCallerDisplayNamePresentation()) { 1557 Log.v(this, "updateAddress, caller display name changed"); 1558 setCallerDisplayName(name, namePresentation); 1559 } 1560 1561 TelephonyManager tm = (TelephonyManager) getPhone().getContext() 1562 .getSystemService(Context.TELEPHONY_SERVICE); 1563 if (tm.isEmergencyNumber(mOriginalConnection.getAddress())) { 1564 mTreatAsEmergencyCall = true; 1565 } 1566 1567 // Changing the address of the connection can change whether it is an emergency call or 1568 // not, which can impact whether it can be part of a conference. 1569 refreshConferenceSupported(); 1570 } 1571 } 1572 onRemovedFromCallService()1573 void onRemovedFromCallService() { 1574 // Subclass can override this to do cleanup. 1575 } 1576 registerForCallEvents(Phone phone)1577 public void registerForCallEvents(Phone phone) { 1578 if (mPhoneForEvents == phone) { 1579 Log.i(this, "registerForCallEvents - same phone requested for" 1580 + "registration, ignoring."); 1581 return; 1582 } 1583 Log.i(this, "registerForCallEvents; phone=%s", phone); 1584 // Only one Phone should be registered for events at a time. 1585 unregisterForCallEvents(); 1586 phone.registerForPreciseCallStateChanged(mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null); 1587 phone.registerForHandoverStateChanged(mHandler, MSG_HANDOVER_STATE_CHANGED, null); 1588 phone.registerForRedialConnectionChanged(mHandler, MSG_REDIAL_CONNECTION_CHANGED, null); 1589 phone.registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null); 1590 phone.registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null); 1591 phone.registerForOnHoldTone(mHandler, MSG_ON_HOLD_TONE, null); 1592 phone.registerForInCallVoicePrivacyOn(mHandler, MSG_CDMA_VOICE_PRIVACY_ON, null); 1593 phone.registerForInCallVoicePrivacyOff(mHandler, MSG_CDMA_VOICE_PRIVACY_OFF, null); 1594 mPhoneForEvents = phone; 1595 } 1596 setOriginalConnection(com.android.internal.telephony.Connection originalConnection)1597 void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) { 1598 Log.i(this, "setOriginalConnection: TelephonyConnection, originalConnection: " 1599 + originalConnection); 1600 if (mOriginalConnection != null && originalConnection != null 1601 && !originalConnection.isIncoming() 1602 && originalConnection.getOrigDialString() == null 1603 && isShowingOriginalDialString()) { 1604 Log.i(this, "new original dial string is null, convert to: " 1605 + mOriginalConnection.getOrigDialString()); 1606 originalConnection.restoreDialedNumberAfterConversion( 1607 mOriginalConnection.getOrigDialString()); 1608 } 1609 1610 clearOriginalConnection(); 1611 mOriginalConnectionExtras.clear(); 1612 mOriginalConnection = originalConnection; 1613 mOriginalConnection.setTelecomCallId(getTelecomCallId()); 1614 registerForCallEvents(getPhone()); 1615 1616 mOriginalConnection.addPostDialListener(mPostDialListener); 1617 mOriginalConnection.addListener(mOriginalConnectionListener); 1618 1619 // Set video state and capabilities 1620 setTelephonyVideoState(mOriginalConnection.getVideoState()); 1621 setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities()); 1622 setIsNetworkIdentifiedEmergencyCall(mOriginalConnection.isNetworkIdentifiedEmergencyCall()); 1623 setIsAdhocConferenceCall(mOriginalConnection.isAdhocConference()); 1624 setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip()); 1625 setTelephonyVideoProvider(mOriginalConnection.getVideoProvider()); 1626 setAudioQuality(mOriginalConnection.getAudioQuality()); 1627 setTechnologyTypeExtra(); 1628 1629 setCallRadioTech(mOriginalConnection.getCallRadioTech()); 1630 1631 // Post update of extras to the handler; extras are updated via the handler to ensure thread 1632 // safety. The Extras Bundle is cloned in case the original extras are modified while they 1633 // are being added to mOriginalConnectionExtras in updateExtras. 1634 Bundle connExtras = mOriginalConnection.getConnectionExtras(); 1635 mHandler.obtainMessage(MSG_CONNECTION_EXTRAS_CHANGED, connExtras == null ? null : 1636 new Bundle(connExtras)).sendToTarget(); 1637 1638 TelephonyManager tm = (TelephonyManager) getPhone().getContext() 1639 .getSystemService(Context.TELEPHONY_SERVICE); 1640 if (tm.isEmergencyNumber(mOriginalConnection.getAddress())) { 1641 mTreatAsEmergencyCall = true; 1642 } 1643 // Propagate VERSTAT for IMS calls. 1644 setCallerNumberVerificationStatus(mOriginalConnection.getNumberVerificationStatus()); 1645 1646 if (isImsConnection()) { 1647 mWasImsConnection = true; 1648 } 1649 if (originalConnection instanceof ImsPhoneConnection) { 1650 maybeConfigureDeviceToDeviceCommunication(); 1651 } 1652 mIsMultiParty = mOriginalConnection.isMultiparty(); 1653 1654 Bundle extrasToPut = new Bundle(); 1655 // Also stash the number verification status in a hidden extra key in the connection. 1656 // We do this because a RemoteConnection DOES NOT include a getNumberVerificationStatus 1657 // method and we need to be able to pass the number verification status up to Telecom 1658 // despite the missing pathway in the RemoteConnectionService API surface. 1659 extrasToPut.putInt(Connection.EXTRA_CALLER_NUMBER_VERIFICATION_STATUS, 1660 mOriginalConnection.getNumberVerificationStatus()); 1661 List<String> extrasToRemove = new ArrayList<>(); 1662 if (mOriginalConnection.isActiveCallDisconnectedOnAnswer()) { 1663 extrasToPut.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true); 1664 } else { 1665 extrasToRemove.add(Connection.EXTRA_ANSWERING_DROPS_FG_CALL); 1666 } 1667 1668 if (shouldSetDisableAddCallExtra()) { 1669 extrasToPut.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true); 1670 } else { 1671 extrasToRemove.add(Connection.EXTRA_DISABLE_ADD_CALL); 1672 } 1673 1674 if (mOriginalConnection != null) { 1675 ArrayList<String> forwardedNumber = mOriginalConnection.getForwardedNumber(); 1676 if (forwardedNumber != null) { 1677 extrasToPut.putStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER, 1678 forwardedNumber); 1679 } 1680 } 1681 1682 putTelephonyExtras(extrasToPut); 1683 removeTelephonyExtras(extrasToRemove); 1684 1685 // updateState can set mOriginalConnection to null if its state is DISCONNECTED, so this 1686 // should be executed *after* the above setters have run. 1687 updateState(); 1688 if (mOriginalConnection == null) { 1689 Log.w(this, "original Connection was nulled out as part of setOriginalConnection. " + 1690 originalConnection); 1691 } 1692 1693 fireOnOriginalConnectionConfigured(); 1694 } 1695 1696 /** 1697 * Filters the CNAP name to not include a list of names that are unhelpful to the user for 1698 * Caller ID purposes. 1699 */ filterCnapName(final String cnapName)1700 private String filterCnapName(final String cnapName) { 1701 if (cnapName == null) { 1702 return null; 1703 } 1704 PersistableBundle carrierConfig = getCarrierConfig(); 1705 String[] filteredCnapNames = null; 1706 if (carrierConfig != null) { 1707 filteredCnapNames = carrierConfig.getStringArray( 1708 CarrierConfigManager.KEY_FILTERED_CNAP_NAMES_STRING_ARRAY); 1709 } 1710 if (filteredCnapNames != null) { 1711 long cnapNameMatches = Arrays.asList(filteredCnapNames) 1712 .stream() 1713 .filter(filteredCnapName -> filteredCnapName.equals( 1714 cnapName.toUpperCase(Locale.ROOT))) 1715 .count(); 1716 if (cnapNameMatches > 0) { 1717 Log.i(this, "filterCnapName: Filtered CNAP Name: " + cnapName); 1718 return ""; 1719 } 1720 } 1721 return cnapName; 1722 } 1723 1724 /** 1725 * Sets the EXTRA_CALL_TECHNOLOGY_TYPE extra on the connection to report back to Telecom. 1726 */ setTechnologyTypeExtra()1727 private void setTechnologyTypeExtra() { 1728 if (getPhone() != null) { 1729 Bundle newExtras = getExtras(); 1730 if (newExtras == null) { 1731 newExtras = new Bundle(); 1732 } 1733 newExtras.putInt(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE, getPhone().getPhoneType()); 1734 putTelephonyExtras(newExtras); 1735 } 1736 } 1737 refreshHoldSupported()1738 private void refreshHoldSupported() { 1739 if (mOriginalConnection == null) { 1740 Log.w(this, "refreshHoldSupported org conn is null"); 1741 return; 1742 } 1743 1744 if (!mOriginalConnection.shouldAllowHoldingVideoCall() && canHoldImsCalls() != 1745 ((getConnectionCapabilities() & (CAPABILITY_HOLD | CAPABILITY_SUPPORT_HOLD)) != 0)) { 1746 updateConnectionCapabilities(); 1747 } 1748 } 1749 refreshDisableAddCall()1750 private void refreshDisableAddCall() { 1751 if (shouldSetDisableAddCallExtra()) { 1752 Bundle newExtras = getExtras(); 1753 if (newExtras == null) { 1754 newExtras = new Bundle(); 1755 } 1756 newExtras.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true); 1757 putTelephonyExtras(newExtras); 1758 } else { 1759 removeExtras(Connection.EXTRA_DISABLE_ADD_CALL); 1760 } 1761 } 1762 refreshCodec()1763 private void refreshCodec() { 1764 boolean changed = false; 1765 Bundle newExtras = getExtras(); 1766 if (newExtras == null) { 1767 newExtras = new Bundle(); 1768 } 1769 int newCodecType; 1770 if (isImsConnection()) { 1771 newCodecType = transformCodec(getOriginalConnection().getAudioCodec()); 1772 } else { 1773 // For SRVCC, report AUDIO_CODEC_NONE. 1774 newCodecType = Connection.AUDIO_CODEC_NONE; 1775 } 1776 int oldCodecType = newExtras.getInt(Connection.EXTRA_AUDIO_CODEC, 1777 Connection.AUDIO_CODEC_NONE); 1778 if (newCodecType != oldCodecType) { 1779 newExtras.putInt(Connection.EXTRA_AUDIO_CODEC, newCodecType); 1780 Log.i(this, "refreshCodec: codec changed; old=%d, new=%d", oldCodecType, newCodecType); 1781 changed = true; 1782 } 1783 if (isImsConnection()) { 1784 float newBitrate = getOriginalConnection().getAudioCodecBitrateKbps(); 1785 float oldBitrate = newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, 0.0f); 1786 if (Math.abs(newBitrate - oldBitrate) > THRESHOLD) { 1787 newExtras.putFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, newBitrate); 1788 Log.i(this, "refreshCodec: bitrate changed; old=%f, new=%f", oldBitrate, 1789 newBitrate); 1790 changed = true; 1791 } 1792 1793 float newBandwidth = getOriginalConnection().getAudioCodecBandwidthKhz(); 1794 float oldBandwidth = newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ, 1795 0.0f); 1796 if (Math.abs(newBandwidth - oldBandwidth) > THRESHOLD) { 1797 newExtras.putFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ, newBandwidth); 1798 Log.i(this, "refreshCodec: bandwidth changed; old=%f, new=%f", oldBandwidth, 1799 newBandwidth); 1800 changed = true; 1801 } 1802 } else { 1803 ArrayList<String> toRemove = new ArrayList<>(); 1804 toRemove.add(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS); 1805 toRemove.add(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ); 1806 removeTelephonyExtras(toRemove); 1807 } 1808 1809 if (changed) { 1810 Log.i(this, "refreshCodec: Codec:" 1811 + newExtras.getInt(Connection.EXTRA_AUDIO_CODEC, Connection.AUDIO_CODEC_NONE) 1812 + ", Bitrate:" 1813 + newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, 0.0f) 1814 + ", Bandwidth:" 1815 + newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ, 0.0f)); 1816 putTelephonyExtras(newExtras); 1817 } 1818 } 1819 transformCodec(int codec)1820 private int transformCodec(int codec) { 1821 switch (codec) { 1822 case ImsStreamMediaProfile.AUDIO_QUALITY_NONE: 1823 return Connection.AUDIO_CODEC_NONE; 1824 case ImsStreamMediaProfile.AUDIO_QUALITY_AMR: 1825 return Connection.AUDIO_CODEC_AMR; 1826 case ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB: 1827 return Connection.AUDIO_CODEC_AMR_WB; 1828 case ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K: 1829 return Connection.AUDIO_CODEC_QCELP13K; 1830 case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC: 1831 return Connection.AUDIO_CODEC_EVRC; 1832 case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B: 1833 return Connection.AUDIO_CODEC_EVRC_B; 1834 case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB: 1835 return Connection.AUDIO_CODEC_EVRC_WB; 1836 case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW: 1837 return Connection.AUDIO_CODEC_EVRC_NW; 1838 case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR: 1839 return Connection.AUDIO_CODEC_GSM_EFR; 1840 case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR: 1841 return Connection.AUDIO_CODEC_GSM_FR; 1842 case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR: 1843 return Connection.AUDIO_CODEC_GSM_HR; 1844 case ImsStreamMediaProfile.AUDIO_QUALITY_G711U: 1845 return Connection.AUDIO_CODEC_G711U; 1846 case ImsStreamMediaProfile.AUDIO_QUALITY_G723: 1847 return Connection.AUDIO_CODEC_G723; 1848 case ImsStreamMediaProfile.AUDIO_QUALITY_G711A: 1849 return Connection.AUDIO_CODEC_G711A; 1850 case ImsStreamMediaProfile.AUDIO_QUALITY_G722: 1851 return Connection.AUDIO_CODEC_G722; 1852 case ImsStreamMediaProfile.AUDIO_QUALITY_G711AB: 1853 return Connection.AUDIO_CODEC_G711AB; 1854 case ImsStreamMediaProfile.AUDIO_QUALITY_G729: 1855 return Connection.AUDIO_CODEC_G729; 1856 case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB: 1857 return Connection.AUDIO_CODEC_EVS_NB; 1858 case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB: 1859 return Connection.AUDIO_CODEC_EVS_WB; 1860 case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB: 1861 return Connection.AUDIO_CODEC_EVS_SWB; 1862 case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB: 1863 return Connection.AUDIO_CODEC_EVS_FB; 1864 default: 1865 return Connection.AUDIO_CODEC_NONE; 1866 } 1867 } 1868 shouldSetDisableAddCallExtra()1869 private boolean shouldSetDisableAddCallExtra() { 1870 if (mOriginalConnection == null) { 1871 return false; 1872 } 1873 boolean carrierShouldAllowAddCall = mOriginalConnection.shouldAllowAddCallDuringVideoCall(); 1874 if (carrierShouldAllowAddCall) { 1875 return false; 1876 } 1877 Phone phone = getPhone(); 1878 if (phone == null) { 1879 return false; 1880 } 1881 boolean isCurrentVideoCall = false; 1882 boolean wasVideoCall = false; 1883 boolean isVowifiEnabled = false; 1884 if (phone instanceof ImsPhone) { 1885 ImsPhoneCall foregroundCall = ((ImsPhone) phone).getForegroundCall(); 1886 if (foregroundCall != null) { 1887 ImsCall call = foregroundCall.getImsCall(); 1888 if (call != null) { 1889 isCurrentVideoCall = call.isVideoCall(); 1890 wasVideoCall = call.wasVideoCall(); 1891 } 1892 } 1893 1894 isVowifiEnabled = isWfcEnabled(phone); 1895 } 1896 1897 if (isCurrentVideoCall) { 1898 return true; 1899 } else if (wasVideoCall && isWifi() && !isVowifiEnabled) { 1900 return true; 1901 } 1902 return false; 1903 } 1904 hasHighDefAudioProperty()1905 private boolean hasHighDefAudioProperty() { 1906 if (!mHasHighDefAudio) { 1907 return false; 1908 } 1909 1910 boolean isVideoCall = VideoProfile.isVideo(getVideoState()); 1911 1912 PersistableBundle b = getCarrierConfig(); 1913 boolean canWifiCallsBeHdAudio = 1914 b != null && b.getBoolean(CarrierConfigManager.KEY_WIFI_CALLS_CAN_BE_HD_AUDIO); 1915 boolean canVideoCallsBeHdAudio = 1916 b != null && b.getBoolean(CarrierConfigManager.KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO); 1917 boolean canGsmCdmaCallsBeHdAudio = 1918 b != null && b.getBoolean(CarrierConfigManager.KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO); 1919 boolean shouldDisplayHdAudio = 1920 b != null && b.getBoolean(CarrierConfigManager.KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL); 1921 1922 if (!shouldDisplayHdAudio) { 1923 return false; 1924 } 1925 1926 if (isGsmCdmaConnection() && !canGsmCdmaCallsBeHdAudio) { 1927 return false; 1928 } 1929 1930 if (isVideoCall && !canVideoCallsBeHdAudio) { 1931 return false; 1932 } 1933 1934 if (isWifi() && !canWifiCallsBeHdAudio) { 1935 return false; 1936 } 1937 1938 return true; 1939 } 1940 1941 /** 1942 * @return The address's to which this Connection is currently communicating. 1943 */ getParticipants()1944 public final @Nullable List<Uri> getParticipants() { 1945 return mParticipants; 1946 } 1947 1948 /** 1949 * Sets the value of the {@link #getParticipants()} property. 1950 * 1951 * @param address The participant address's. 1952 */ setParticipants(@ullable List<Uri> address)1953 public final void setParticipants(@Nullable List<Uri> address) { 1954 mParticipants = address; 1955 } 1956 1957 /** 1958 * @return true if connection is adhocConference call else false. 1959 */ isAdhocConferenceCall()1960 public final boolean isAdhocConferenceCall() { 1961 return mIsAdhocConferenceCall; 1962 } 1963 1964 /** 1965 * Sets the value of the {@link #isAdhocConferenceCall()} property. 1966 * 1967 * @param isAdhocConferenceCall represents if the call is adhoc conference call or not. 1968 */ setIsAdhocConferenceCall(boolean isAdhocConferenceCall)1969 public void setIsAdhocConferenceCall(boolean isAdhocConferenceCall) { 1970 mIsAdhocConferenceCall = isAdhocConferenceCall; 1971 updateConnectionProperties(); 1972 } 1973 canHoldImsCalls()1974 private boolean canHoldImsCalls() { 1975 PersistableBundle b = getCarrierConfig(); 1976 // Return true if the CarrierConfig is unavailable 1977 return (!doesDeviceRespectHoldCarrierConfig() || b == null || 1978 b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL)) && 1979 ((mOriginalConnection != null && mOriginalConnection.shouldAllowHoldingVideoCall()) 1980 || !VideoProfile.isVideo(getVideoState())); 1981 } 1982 isConferenceHosted()1983 private boolean isConferenceHosted() { 1984 boolean isHosted = false; 1985 if (getTelephonyConnectionService() != null) { 1986 for (Conference current : getTelephonyConnectionService().getAllConferences()) { 1987 if (current instanceof ImsConference) { 1988 ImsConference other = (ImsConference) current; 1989 if (getState() == current.getState()) { 1990 continue; 1991 } 1992 if (other.isConferenceHost()) { 1993 isHosted = true; 1994 break; 1995 } 1996 } 1997 } 1998 } 1999 return isHosted; 2000 } 2001 isAddParticipantCapable()2002 private boolean isAddParticipantCapable() { 2003 // not add participant capable for non ims phones 2004 if (getPhone() == null || getPhone().getPhoneType() != PhoneConstants.PHONE_TYPE_IMS) { 2005 return false; 2006 } 2007 2008 if (!getCarrierConfig() 2009 .getBoolean(CarrierConfigManager.KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL)) { 2010 return false; 2011 } 2012 2013 boolean isCapable = !mTreatAsEmergencyCall && (mConnectionState == Call.State.ACTIVE || 2014 mConnectionState == Call.State.HOLDING); 2015 2016 // add participant capable if current connection is a host connection or 2017 // if conference is not hosted on the device 2018 isCapable = isCapable && ((mOriginalConnection != null && 2019 mOriginalConnection.isConferenceHost()) || 2020 !isConferenceHosted()); 2021 2022 /** 2023 * For individual IMS calls, if the extra for remote conference support is 2024 * - indicated, then consider the same for add participant capability 2025 * - not indicated, then the add participant capability is same as before. 2026 */ 2027 if (isCapable && (mOriginalConnection != null) && !mIsMultiParty) { 2028 // In case OEMs are still using deprecated value, read it and use it as default value. 2029 boolean isCapableFromDeprecatedExtra = mOriginalConnectionExtras.getBoolean( 2030 ImsCallProfile.EXTRA_CONFERENCE_AVAIL, isCapable); 2031 isCapable = mOriginalConnectionExtras.getBoolean( 2032 ImsCallProfile.EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED, 2033 isCapableFromDeprecatedExtra); 2034 } 2035 return isCapable; 2036 } 2037 2038 /** 2039 * Applies the add participant capabilities to the {@code CallCapabilities} bit-mask. 2040 * 2041 * @param callCapabilities The {@code CallCapabilities} bit-mask. 2042 * @return The capabilities with the add participant capabilities applied. 2043 */ applyAddParticipantCapabilities(int callCapabilities)2044 private int applyAddParticipantCapabilities(int callCapabilities) { 2045 int currentCapabilities = callCapabilities; 2046 if (isAddParticipantCapable()) { 2047 currentCapabilities = changeBitmask(currentCapabilities, 2048 Connection.CAPABILITY_ADD_PARTICIPANT, true); 2049 } else { 2050 currentCapabilities = changeBitmask(currentCapabilities, 2051 Connection.CAPABILITY_ADD_PARTICIPANT, false); 2052 } 2053 return currentCapabilities; 2054 } 2055 2056 @VisibleForTesting getCarrierConfig()2057 public @NonNull PersistableBundle getCarrierConfig() { 2058 Phone phone = getPhone(); 2059 if (phone == null) { 2060 Log.w(this, 2061 "getCarrierConfig: phone is null. Returning CarrierConfigManager" 2062 + ".getDefaultConfig()"); 2063 return CarrierConfigManager.getDefaultConfig(); 2064 } 2065 2066 // potential null returned from .getCarrierConfigForSubId() and method guarantees non-null. 2067 // hence, need for try/finally block 2068 PersistableBundle pb = null; 2069 try { 2070 pb = PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId()); 2071 } catch (Exception e) { 2072 Log.e(this, e, 2073 "getCarrierConfig: caught Exception when calling " 2074 + "PhoneGlobals.getCarrierConfigForSubId(phone.getSubId()). Returning " 2075 + "CarrierConfigManager.getDefaultConfig()"); 2076 } finally { 2077 if (pb == null) { 2078 pb = CarrierConfigManager.getDefaultConfig(); 2079 } 2080 } 2081 return pb; 2082 } 2083 2084 @VisibleForTesting isRttMergeSupported(@onNull PersistableBundle pb)2085 public boolean isRttMergeSupported(@NonNull PersistableBundle pb) { 2086 return pb.getBoolean(CarrierConfigManager.KEY_ALLOW_MERGING_RTT_CALLS_BOOL); 2087 } 2088 canDeflectImsCalls()2089 private boolean canDeflectImsCalls() { 2090 return getCarrierConfig().getBoolean( 2091 CarrierConfigManager.KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL) 2092 && isValidRingingCall(); 2093 } 2094 isCallTransferSupported()2095 private boolean isCallTransferSupported() { 2096 return getCarrierConfig().getBoolean( 2097 CarrierConfigManager.KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL); 2098 } 2099 canTransfer(TelephonyConnection c)2100 private boolean canTransfer(TelephonyConnection c) { 2101 com.android.internal.telephony.Connection connection = c.getOriginalConnection(); 2102 return (connection != null && !connection.isMultiparty() 2103 && (c.getState() == STATE_ACTIVE || c.getState() == STATE_HOLDING)); 2104 } 2105 canTransferToNumber()2106 private boolean canTransferToNumber() { 2107 if (!isCallTransferSupported()) { 2108 return false; 2109 } 2110 return canTransfer(this); 2111 } 2112 canConsultativeTransfer()2113 private boolean canConsultativeTransfer() { 2114 if (!isCallTransferSupported()) { 2115 return false; 2116 } 2117 if (!canTransfer(this)) { 2118 return false; 2119 } 2120 boolean canConsultativeTransfer = false; 2121 if (getTelephonyConnectionService() != null) { 2122 for (Connection current : getTelephonyConnectionService().getAllConnections()) { 2123 if (current != this && current instanceof TelephonyConnection) { 2124 TelephonyConnection other = (TelephonyConnection) current; 2125 if (canTransfer(other)) { 2126 canConsultativeTransfer = true; 2127 break; 2128 } 2129 } 2130 } 2131 } 2132 return canConsultativeTransfer; 2133 } 2134 2135 /** 2136 * Determines if the device will respect the value of the 2137 * {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} configuration option. 2138 * 2139 * @return {@code false} if the device always supports holding IMS calls, {@code true} if it 2140 * will use {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} to determine if 2141 * hold is supported. 2142 */ doesDeviceRespectHoldCarrierConfig()2143 private boolean doesDeviceRespectHoldCarrierConfig() { 2144 Phone phone = getPhone(); 2145 if (phone == null) { 2146 return true; 2147 } 2148 return phone.getContext().getResources().getBoolean( 2149 com.android.internal.R.bool.config_device_respects_hold_carrier_config); 2150 } 2151 2152 /** 2153 * Whether the connection should be treated as an emergency. 2154 * @return {@code true} if the connection should be treated as an emergency call based 2155 * on the number dialed, {@code false} otherwise. 2156 */ shouldTreatAsEmergencyCall()2157 protected boolean shouldTreatAsEmergencyCall() { 2158 return mTreatAsEmergencyCall; 2159 } 2160 2161 /** 2162 * Sets whether to treat this call as an emergency call or not. 2163 * @param shouldTreatAsEmergencyCall 2164 */ 2165 @VisibleForTesting setShouldTreatAsEmergencyCall(boolean shouldTreatAsEmergencyCall)2166 public void setShouldTreatAsEmergencyCall(boolean shouldTreatAsEmergencyCall) { 2167 mTreatAsEmergencyCall = shouldTreatAsEmergencyCall; 2168 } 2169 2170 /** 2171 * Un-sets the underlying radio connection. 2172 */ clearOriginalConnection()2173 void clearOriginalConnection() { 2174 if (mOriginalConnection != null) { 2175 Log.i(this, "clearOriginalConnection; clearing=%s", mOriginalConnection); 2176 unregisterForCallEvents(); 2177 mOriginalConnection.removePostDialListener(mPostDialListener); 2178 mOriginalConnection.removeListener(mOriginalConnectionListener); 2179 mOriginalConnection = null; 2180 } 2181 } 2182 unregisterForCallEvents()2183 public void unregisterForCallEvents() { 2184 if (mPhoneForEvents == null) return; 2185 mPhoneForEvents.unregisterForPreciseCallStateChanged(mHandler); 2186 mPhoneForEvents.unregisterForRingbackTone(mHandler); 2187 mPhoneForEvents.unregisterForHandoverStateChanged(mHandler); 2188 mPhoneForEvents.unregisterForRedialConnectionChanged(mHandler); 2189 mPhoneForEvents.unregisterForDisconnect(mHandler); 2190 mPhoneForEvents.unregisterForSuppServiceNotification(mHandler); 2191 mPhoneForEvents.unregisterForOnHoldTone(mHandler); 2192 mPhoneForEvents.unregisterForInCallVoicePrivacyOn(mHandler); 2193 mPhoneForEvents.unregisterForInCallVoicePrivacyOff(mHandler); 2194 mPhoneForEvents = null; 2195 } 2196 2197 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED) hangup(int telephonyDisconnectCode)2198 public void hangup(int telephonyDisconnectCode) { 2199 if (mOriginalConnection != null) { 2200 mHangupDisconnectCause = telephonyDisconnectCode; 2201 try { 2202 // Hanging up a ringing call requires that we invoke call.hangup() as opposed to 2203 // connection.hangup(). Without this change, the party originating the call 2204 // will not get sent to voicemail if the user opts to reject the call. 2205 if (isValidRingingCall()) { 2206 Call call = getCall(); 2207 if (call != null) { 2208 call.hangup(); 2209 } else { 2210 Log.w(this, "Attempting to hangup a connection without backing call."); 2211 } 2212 } else { 2213 // We still prefer to call connection.hangup() for non-ringing calls 2214 // in order to support hanging-up specific calls within a conference call. 2215 // If we invoked call.hangup() while in a conference, we would end up 2216 // hanging up the entire conference call instead of the specific connection. 2217 mOriginalConnection.hangup(); 2218 } 2219 } catch (CallStateException e) { 2220 Log.e(this, e, "Call to Connection.hangup failed with exception"); 2221 } 2222 } else { 2223 mTelephonyConnectionService.onLocalHangup(this); 2224 if (getState() == STATE_DISCONNECTED) { 2225 Log.i(this, "hangup called on an already disconnected call!"); 2226 close(); 2227 } else { 2228 // There are a few cases where mOriginalConnection has not been set yet. For 2229 // example, when the radio has to be turned on to make an emergency call, 2230 // mOriginalConnection could not be set for many seconds. 2231 setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause( 2232 android.telephony.DisconnectCause.LOCAL, 2233 "Local Disconnect before connection established.")); 2234 close(); 2235 } 2236 } 2237 } 2238 reject(@ndroid.telecom.Call.RejectReason int rejectReason)2239 protected void reject(@android.telecom.Call.RejectReason int rejectReason) { 2240 if (mOriginalConnection != null) { 2241 mHangupDisconnectCause = android.telephony.DisconnectCause.INCOMING_REJECTED; 2242 try { 2243 // Hanging up a ringing call requires that we invoke call.hangup() as opposed to 2244 // connection.hangup(). Without this change, the party originating the call 2245 // will not get sent to voicemail if the user opts to reject the call. 2246 if (isValidRingingCall()) { 2247 Call call = getCall(); 2248 if (call != null) { 2249 call.hangup(rejectReason); 2250 } else { 2251 Log.w(this, "Attempting to hangup a connection without backing call."); 2252 } 2253 } else { 2254 // We still prefer to call connection.hangup() for non-ringing calls 2255 // in order to support hanging-up specific calls within a conference call. 2256 // If we invoked call.hangup() while in a conference, we would end up 2257 // hanging up the entire conference call instead of the specific connection. 2258 mOriginalConnection.hangup(); 2259 } 2260 } catch (CallStateException e) { 2261 Log.e(this, e, "Call to Connection.hangup failed with exception"); 2262 } 2263 } else { 2264 if (getState() == STATE_DISCONNECTED) { 2265 Log.i(this, "hangup called on an already disconnected call!"); 2266 close(); 2267 } else { 2268 // There are a few cases where mOriginalConnection has not been set yet. For 2269 // example, when the radio has to be turned on to make an emergency call, 2270 // mOriginalConnection could not be set for many seconds. 2271 setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause( 2272 android.telephony.DisconnectCause.LOCAL, 2273 "Local Disconnect before connection established.")); 2274 close(); 2275 } 2276 } 2277 } 2278 getOriginalConnection()2279 com.android.internal.telephony.Connection getOriginalConnection() { 2280 return mOriginalConnection; 2281 } 2282 getCall()2283 protected Call getCall() { 2284 if (mOriginalConnection != null) { 2285 return mOriginalConnection.getCall(); 2286 } 2287 return null; 2288 } 2289 getPhone()2290 Phone getPhone() { 2291 Call call = getCall(); 2292 if (call != null) { 2293 return call.getPhone(); 2294 } 2295 return null; 2296 } 2297 hasMultipleTopLevelCalls()2298 private boolean hasMultipleTopLevelCalls() { 2299 int numCalls = 0; 2300 Phone phone = getPhone(); 2301 if (phone != null) { 2302 if (!phone.getRingingCall().isIdle()) { 2303 numCalls++; 2304 } 2305 if (!phone.getForegroundCall().isIdle()) { 2306 numCalls++; 2307 } 2308 if (!phone.getBackgroundCall().isIdle()) { 2309 numCalls++; 2310 } 2311 } 2312 return numCalls > 1; 2313 } 2314 getForegroundConnection()2315 private com.android.internal.telephony.Connection getForegroundConnection() { 2316 if (getPhone() != null) { 2317 return getPhone().getForegroundCall().getEarliestConnection(); 2318 } 2319 return null; 2320 } 2321 2322 /** 2323 * Checks for and returns the list of conference participants 2324 * associated with this connection. 2325 */ getConferenceParticipants()2326 public List<ConferenceParticipant> getConferenceParticipants() { 2327 if (mOriginalConnection == null) { 2328 Log.w(this, "Null mOriginalConnection, cannot get conf participants."); 2329 return null; 2330 } 2331 return mOriginalConnection.getConferenceParticipants(); 2332 } 2333 2334 /** 2335 * Checks to see the original connection corresponds to an active incoming call. Returns false 2336 * if there is no such actual call, or if the associated call is not incoming (See 2337 * {@link Call.State#isRinging}). 2338 */ isValidRingingCall()2339 private boolean isValidRingingCall() { 2340 if (getPhone() == null) { 2341 Log.v(this, "isValidRingingCall, phone is null"); 2342 return false; 2343 } 2344 2345 Call ringingCall = getPhone().getRingingCall(); 2346 if (!ringingCall.getState().isRinging()) { 2347 Log.v(this, "isValidRingingCall, ringing call is not in ringing state"); 2348 return false; 2349 } 2350 2351 if (ringingCall.getEarliestConnection() != mOriginalConnection) { 2352 Log.v(this, "isValidRingingCall, ringing call connection does not match"); 2353 return false; 2354 } 2355 2356 Log.v(this, "isValidRingingCall, returning true"); 2357 return true; 2358 } 2359 2360 // Make sure the extras being passed into this method is a COPY of the original extras Bundle. 2361 // We do not want the extras to be cleared or modified during mOriginalConnectionExtras.putAll 2362 // below. updateExtras(Bundle extras)2363 protected void updateExtras(Bundle extras) { 2364 if (mOriginalConnection != null) { 2365 if (extras != null) { 2366 // Check if extras have changed and need updating. 2367 if (!areBundlesEqual(mOriginalConnectionExtras, extras)) { 2368 if (Log.DEBUG) { 2369 Log.d(TelephonyConnection.this, "Updating extras:"); 2370 for (String key : extras.keySet()) { 2371 Object value = extras.get(key); 2372 if (value instanceof String) { 2373 Log.d(this, "updateExtras Key=" + Rlog.pii(LOG_TAG, key) 2374 + " value=" + Rlog.pii(LOG_TAG, value)); 2375 } 2376 } 2377 } 2378 mOriginalConnectionExtras.clear(); 2379 2380 mOriginalConnectionExtras.putAll(extras); 2381 2382 // Remap any string extras that have a remapping defined. 2383 for (String key : mOriginalConnectionExtras.keySet()) { 2384 if (sExtrasMap.containsKey(key)) { 2385 String newKey = sExtrasMap.get(key); 2386 mOriginalConnectionExtras.putString(newKey, extras.getString(key)); 2387 mOriginalConnectionExtras.remove(key); 2388 } 2389 } 2390 2391 // Ensure extras are propagated to Telecom. 2392 putTelephonyExtras(mOriginalConnectionExtras); 2393 // If extras contain Conference support information, 2394 // then ensure capabilities are updated. 2395 if (mOriginalConnectionExtras.containsKey( 2396 ImsCallProfile.EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED) 2397 || mOriginalConnectionExtras.containsKey( 2398 ImsCallProfile.EXTRA_CONFERENCE_AVAIL)) { 2399 updateConnectionCapabilities(); 2400 } 2401 // If extras contain or contained Cross Sim information, 2402 // then ensure connection properties are updated and propagated to Telecom. 2403 // Also, update the status hints in the case the call has 2404 // has moved from cross sim call back to wifi 2405 mWasCrossSim |= mOriginalConnectionExtras.containsKey( 2406 ImsCallProfile.EXTRA_IS_CROSS_SIM_CALL); 2407 if (mWasCrossSim) { 2408 updateStatusHints(); 2409 updateConnectionProperties(); 2410 } 2411 } else { 2412 Log.d(this, "Extras update not required"); 2413 } 2414 } else { 2415 Log.d(this, "updateExtras extras: " + Rlog.pii(LOG_TAG, extras)); 2416 } 2417 } 2418 } 2419 areBundlesEqual(Bundle extras, Bundle newExtras)2420 private static boolean areBundlesEqual(Bundle extras, Bundle newExtras) { 2421 if (extras == null || newExtras == null) { 2422 return extras == newExtras; 2423 } 2424 2425 if (extras.size() != newExtras.size()) { 2426 return false; 2427 } 2428 2429 for(String key : extras.keySet()) { 2430 if (key != null) { 2431 final Object value = extras.get(key); 2432 final Object newValue = newExtras.get(key); 2433 if (!Objects.equals(value, newValue)) { 2434 return false; 2435 } 2436 } 2437 } 2438 return true; 2439 } 2440 setStateOverride(Call.State state)2441 void setStateOverride(Call.State state) { 2442 mIsStateOverridden = true; 2443 mConnectionOverriddenState = state; 2444 // Need to keep track of the original connection's state before override. 2445 mOriginalConnectionState = mOriginalConnection.getState(); 2446 updateStateInternal(); 2447 } 2448 resetStateOverride()2449 void resetStateOverride() { 2450 mIsStateOverridden = false; 2451 updateStateInternal(); 2452 } 2453 updateStateInternal()2454 void updateStateInternal() { 2455 if (mOriginalConnection == null) { 2456 return; 2457 } 2458 Call.State newState; 2459 // If the state is overridden and the state of the original connection hasn't changed since, 2460 // then we continue in the overridden state, else we go to the original connection's state. 2461 if (mIsStateOverridden && mOriginalConnectionState == mOriginalConnection.getState()) { 2462 newState = mConnectionOverriddenState; 2463 } else { 2464 newState = mOriginalConnection.getState(); 2465 } 2466 int cause = mOriginalConnection.getDisconnectCause(); 2467 Log.v(this, "Update state from %s to %s for %s", mConnectionState, newState, 2468 getTelecomCallId()); 2469 2470 if (mConnectionState != newState) { 2471 mConnectionState = newState; 2472 switch (newState) { 2473 case IDLE: 2474 break; 2475 case ACTIVE: 2476 setActiveInternal(); 2477 break; 2478 case HOLDING: 2479 setTelephonyConnectionOnHold(); 2480 break; 2481 case DIALING: 2482 case ALERTING: 2483 if (mOriginalConnection != null && mOriginalConnection.isPulledCall()) { 2484 setTelephonyConnectionPulling(); 2485 } else { 2486 setTelephonyConnectionDialing(); 2487 } 2488 break; 2489 case INCOMING: 2490 case WAITING: 2491 setTelephonyConnectionRinging(); 2492 break; 2493 case DISCONNECTED: 2494 if (mTelephonyConnectionService != null) { 2495 ImsReasonInfo reasonInfo = null; 2496 if (isImsConnection()) { 2497 ImsPhoneConnection imsPhoneConnection = 2498 (ImsPhoneConnection) mOriginalConnection; 2499 reasonInfo = imsPhoneConnection.getImsReasonInfo(); 2500 if (reasonInfo != null) { 2501 int reasonCode = reasonInfo.getCode(); 2502 int extraCode = reasonInfo.getExtraCode(); 2503 if ((reasonCode == CODE_SIP_ALTERNATE_EMERGENCY_CALL) 2504 || (reasonCode == CODE_LOCAL_CALL_CS_RETRY_REQUIRED 2505 && extraCode == EXTRA_CODE_CALL_RETRY_EMERGENCY)) { 2506 EmergencyNumber numberInfo = 2507 imsPhoneConnection.getEmergencyNumberInfo(); 2508 if (numberInfo != null) { 2509 mEmergencyServiceCategory = 2510 numberInfo.getEmergencyServiceCategoryBitmask(); 2511 mEmergencyUrns = numberInfo.getEmergencyUrns(); 2512 } else { 2513 Log.i(this, "mEmergencyServiceCategory no EmergencyNumber"); 2514 } 2515 2516 if (mEmergencyServiceCategory != null) { 2517 Log.i(this, "mEmergencyServiceCategory=" 2518 + mEmergencyServiceCategory); 2519 } 2520 if (mEmergencyUrns != null) { 2521 Log.i(this, "mEmergencyUrns=" + mEmergencyUrns); 2522 } 2523 } 2524 } 2525 } 2526 2527 if (mTelephonyConnectionService.maybeReselectDomain(this, reasonInfo, 2528 mShowPreciseFailedCause, mHangupDisconnectCause)) { 2529 clearOriginalConnection(); 2530 break; 2531 } 2532 } 2533 2534 if (shouldTreatAsEmergencyCall() 2535 && (cause 2536 == android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE 2537 || cause 2538 == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE)) { 2539 // We can get into a situation where the radio wants us to redial the 2540 // same emergency call on the other available slot. This will not set 2541 // the state to disconnected and will instead tell the 2542 // TelephonyConnectionService to 2543 // create a new originalConnection using the new Slot. 2544 fireOnOriginalConnectionRetryDial(cause 2545 == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE); 2546 } else { 2547 int preciseDisconnectCause = CallFailCause.NOT_VALID; 2548 if (mShowPreciseFailedCause) { 2549 preciseDisconnectCause = 2550 mOriginalConnection.getPreciseDisconnectCause(); 2551 } 2552 int disconnectCause = mOriginalConnection.getDisconnectCause(); 2553 if ((mHangupDisconnectCause != DisconnectCause.NOT_VALID) 2554 && (mHangupDisconnectCause != disconnectCause)) { 2555 Log.i(LOG_TAG, "setDisconnected: override cause: " + disconnectCause 2556 + " -> " + mHangupDisconnectCause); 2557 disconnectCause = mHangupDisconnectCause; 2558 } 2559 ImsReasonInfo imsReasonInfo = null; 2560 if (isImsConnection()) { 2561 ImsPhoneConnection imsPhoneConnection = 2562 (ImsPhoneConnection) mOriginalConnection; 2563 imsReasonInfo = imsPhoneConnection.getImsReasonInfo(); 2564 } 2565 setTelephonyConnectionDisconnected( 2566 DisconnectCauseUtil.toTelecomDisconnectCause( 2567 disconnectCause, 2568 preciseDisconnectCause, 2569 mOriginalConnection.getVendorDisconnectCause(), 2570 getPhone().getPhoneId(), imsReasonInfo, 2571 new FlagsAdapterImpl(), 2572 shouldTreatAsEmergencyCall())); 2573 close(); 2574 } 2575 break; 2576 case DISCONNECTING: 2577 break; 2578 } 2579 2580 if (mCommunicator != null) { 2581 mCommunicator.onStateChanged(getTelecomCallId(), getState()); 2582 } 2583 } 2584 } 2585 updateState()2586 void updateState() { 2587 if (mOriginalConnection == null) { 2588 return; 2589 } 2590 2591 updateStateInternal(); 2592 updateStatusHints(); 2593 updateConnectionCapabilities(); 2594 updateConnectionProperties(); 2595 updateAddress(); 2596 updateMultiparty(); 2597 refreshDisableAddCall(); 2598 refreshCodec(); 2599 } 2600 2601 /** 2602 * Checks for changes to the multiparty bit. If a conference has started, informs listeners. 2603 */ updateMultiparty()2604 private void updateMultiparty() { 2605 if (mOriginalConnection == null) { 2606 return; 2607 } 2608 2609 if (mIsMultiParty != mOriginalConnection.isMultiparty()) { 2610 mIsMultiParty = mOriginalConnection.isMultiparty(); 2611 2612 if (mIsMultiParty) { 2613 notifyConferenceStarted(); 2614 } 2615 } 2616 } 2617 2618 /** 2619 * Handles a failure when merging calls into a conference. 2620 * {@link com.android.internal.telephony.Connection.Listener#onConferenceMergedFailed()} 2621 * listener. 2622 */ handleConferenceMergeFailed()2623 private void handleConferenceMergeFailed(){ 2624 mHandler.obtainMessage(MSG_CONFERENCE_MERGE_FAILED).sendToTarget(); 2625 } 2626 2627 /** 2628 * Handles requests to update the multiparty state received via the 2629 * {@link com.android.internal.telephony.Connection.Listener#onMultipartyStateChanged(boolean)} 2630 * listener. 2631 * <p> 2632 * Note: We post this to the mHandler to ensure that if a conference must be created as a 2633 * result of the multiparty state change, the conference creation happens on the correct 2634 * thread. This ensures that the thread check in 2635 * {@link com.android.internal.telephony.Phone#checkCorrectThread(android.os.Handler)} 2636 * does not fire. 2637 * 2638 * @param isMultiParty {@code true} if this connection is multiparty, {@code false} otherwise. 2639 */ handleMultipartyStateChange(boolean isMultiParty)2640 private void handleMultipartyStateChange(boolean isMultiParty) { 2641 Log.i(this, "Update multiparty state to %s", isMultiParty ? "Y" : "N"); 2642 mHandler.obtainMessage(MSG_MULTIPARTY_STATE_CHANGED, isMultiParty).sendToTarget(); 2643 } 2644 setActiveInternal()2645 private void setActiveInternal() { 2646 if (getState() == STATE_ACTIVE) { 2647 Log.w(this, "Should not be called if this is already ACTIVE"); 2648 return; 2649 } 2650 2651 // When we set a call to active, we need to make sure that there are no other active 2652 // calls. However, the ordering of state updates to connections can be non-deterministic 2653 // since all connections register for state changes on the phone independently. 2654 // To "optimize", we check here to see if there already exists any active calls. If so, 2655 // we issue an update for those calls first to make sure we only have one top-level 2656 // active call. 2657 if (getTelephonyConnectionService() != null) { 2658 for (Connection current : getTelephonyConnectionService().getAllConnections()) { 2659 if (current != this && current instanceof TelephonyConnection) { 2660 TelephonyConnection other = (TelephonyConnection) current; 2661 if (other.getState() == STATE_ACTIVE) { 2662 other.updateState(); 2663 } 2664 } 2665 } 2666 } 2667 setTelephonyConnectionActive(); 2668 } 2669 close()2670 public void close() { 2671 Log.v(this, "close"); 2672 clearOriginalConnection(); 2673 destroy(); 2674 if (mTelephonyConnectionService != null) { 2675 removeTelephonyConnectionListener( 2676 mTelephonyConnectionService.getTelephonyConnectionListener()); 2677 } 2678 notifyDestroyed(); 2679 } 2680 2681 /** 2682 * Determines if the current connection is video capable. 2683 * 2684 * A connection is deemed to be video capable if the original connection capabilities state that 2685 * both local and remote video is supported. 2686 * 2687 * @return {@code true} if the connection is video capable, {@code false} otherwise. 2688 */ isVideoCapable()2689 private boolean isVideoCapable() { 2690 return (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL) 2691 == Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL 2692 && (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL) 2693 == Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL; 2694 } 2695 2696 /** 2697 * Determines if the current connection is an external connection. 2698 * 2699 * A connection is deemed to be external if the original connection capabilities state that it 2700 * is. 2701 * 2702 * @return {@code true} if the connection is external, {@code false} otherwise. 2703 */ isExternalConnection()2704 private boolean isExternalConnection() { 2705 return (mOriginalConnectionCapabilities 2706 & Capability.IS_EXTERNAL_CONNECTION) == Capability.IS_EXTERNAL_CONNECTION; 2707 } 2708 2709 /** 2710 * Determines if the current connection has RTT enabled. 2711 */ isRtt()2712 private boolean isRtt() { 2713 return mOriginalConnection != null 2714 && mOriginalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS 2715 && mOriginalConnection instanceof ImsPhoneConnection 2716 && ((ImsPhoneConnection) mOriginalConnection).isRttEnabledForCall(); 2717 } 2718 2719 /** 2720 * Determines if the current connection is cross sim calling 2721 */ isCrossSimCall()2722 private boolean isCrossSimCall() { 2723 return mOriginalConnection != null 2724 && mOriginalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS 2725 && mOriginalConnection instanceof ImsPhoneConnection 2726 && ((ImsPhoneConnection) mOriginalConnection).isCrossSimCall(); 2727 } 2728 2729 /** 2730 * Determines if the current connection is pullable. 2731 * 2732 * A connection is deemed to be pullable if the original connection capabilities state that it 2733 * is. 2734 * 2735 * @return {@code true} if the connection is pullable, {@code false} otherwise. 2736 */ isPullable()2737 private boolean isPullable() { 2738 return (mOriginalConnectionCapabilities & Capability.IS_EXTERNAL_CONNECTION) 2739 == Capability.IS_EXTERNAL_CONNECTION 2740 && (mOriginalConnectionCapabilities & Capability.IS_PULLABLE) 2741 == Capability.IS_PULLABLE; 2742 } 2743 2744 /** 2745 * Sets whether or not CDMA enhanced call privacy is enabled for this connection. 2746 */ setCdmaVoicePrivacy(boolean isEnabled)2747 private void setCdmaVoicePrivacy(boolean isEnabled) { 2748 if(mIsCdmaVoicePrivacyEnabled != isEnabled) { 2749 mIsCdmaVoicePrivacyEnabled = isEnabled; 2750 updateConnectionProperties(); 2751 } 2752 } 2753 2754 /** 2755 * Applies capabilities specific to conferences termination to the 2756 * {@code ConnectionCapabilities} bit-mask. 2757 * 2758 * @param capabilities The {@code ConnectionCapabilities} bit-mask. 2759 * @return The capabilities with the IMS conference capabilities applied. 2760 */ applyConferenceTerminationCapabilities(int capabilities)2761 private int applyConferenceTerminationCapabilities(int capabilities) { 2762 int currentCapabilities = capabilities; 2763 2764 // An IMS call cannot be individually disconnected or separated from its parent conference. 2765 // If the call was IMS, even if it hands over to GMS, these capabilities are not supported. 2766 if (!mWasImsConnection) { 2767 currentCapabilities |= CAPABILITY_DISCONNECT_FROM_CONFERENCE; 2768 currentCapabilities |= CAPABILITY_SEPARATE_FROM_CONFERENCE; 2769 } 2770 2771 return currentCapabilities; 2772 } 2773 2774 /** 2775 * Stores the new original connection capabilities, and applies them to the current connection, 2776 * notifying any listeners as necessary. 2777 * 2778 * @param connectionCapabilities The original connection capabilties. 2779 */ setOriginalConnectionCapabilities(int connectionCapabilities)2780 public void setOriginalConnectionCapabilities(int connectionCapabilities) { 2781 mOriginalConnectionCapabilities = connectionCapabilities; 2782 updateConnectionCapabilities(); 2783 updateConnectionProperties(); 2784 } 2785 2786 /** 2787 * Called to apply the capabilities present in the {@link #mOriginalConnection} to this 2788 * {@link Connection}. Provides a mapping between the capabilities present in the original 2789 * connection (see {@link com.android.internal.telephony.Connection.Capability}) and those in 2790 * this {@link Connection}. 2791 * 2792 * @param capabilities The capabilities bitmask from the {@link Connection}. 2793 * @return the capabilities bitmask with the original connection capabilities remapped and 2794 * applied. 2795 */ applyOriginalConnectionCapabilities(int capabilities)2796 public int applyOriginalConnectionCapabilities(int capabilities) { 2797 // We only support downgrading to audio if both the remote and local side support 2798 // downgrading to audio. 2799 int supportsDowngrade = Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL 2800 | Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE; 2801 boolean supportsDowngradeToAudio = 2802 (mOriginalConnectionCapabilities & supportsDowngrade) == supportsDowngrade; 2803 capabilities = changeBitmask(capabilities, 2804 CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO, !supportsDowngradeToAudio); 2805 2806 capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL, 2807 (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL) 2808 == Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL); 2809 2810 boolean isLocalVideoSupported = (mOriginalConnectionCapabilities 2811 & Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL) 2812 == Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL && !mIsTtyEnabled; 2813 capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL, 2814 isLocalVideoSupported); 2815 2816 capabilities = changeBitmask(capabilities, CAPABILITY_REMOTE_PARTY_SUPPORTS_RTT, 2817 (mOriginalConnectionCapabilities & Capability.SUPPORTS_RTT_REMOTE) 2818 == Capability.SUPPORTS_RTT_REMOTE); 2819 2820 return capabilities; 2821 } 2822 2823 /** 2824 * Whether the call is using wifi. 2825 */ isWifi()2826 boolean isWifi() { 2827 return getCallRadioTech() == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; 2828 } 2829 2830 /** 2831 * Sets whether this call has been identified by the network as an emergency call. 2832 * @param isNetworkIdentifiedEmergencyCall {@code true} if the network has identified this call 2833 * as an emergency call, {@code false} otherwise. 2834 */ setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall)2835 public void setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall) { 2836 Log.d(this, "setIsNetworkIdentifiedEmergencyCall; callId=%s, " 2837 + "isNetworkIdentifiedEmergencyCall=%b", getTelecomCallId(), 2838 isNetworkIdentifiedEmergencyCall); 2839 mIsNetworkIdentifiedEmergencyCall = isNetworkIdentifiedEmergencyCall; 2840 updateConnectionProperties(); 2841 } 2842 2843 /** 2844 * @return {@code true} if the network has identified this call as an emergency call, 2845 * {@code false} otherwise. 2846 */ isNetworkIdentifiedEmergencyCall()2847 public boolean isNetworkIdentifiedEmergencyCall() { 2848 return mIsNetworkIdentifiedEmergencyCall; 2849 } 2850 2851 /** 2852 * @return {@code true} if this is an outgoing call, {@code false} otherwise. 2853 */ isOutgoingCall()2854 public boolean isOutgoingCall() { 2855 return getCallDirection() == android.telecom.Call.Details.DIRECTION_OUTGOING; 2856 } 2857 2858 /** 2859 * Sets the current call audio quality. Used during rebuild of the properties 2860 * to set or unset the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property. 2861 * 2862 * @param audioQuality The audio quality. 2863 */ setAudioQuality(int audioQuality)2864 public void setAudioQuality(int audioQuality) { 2865 mHasHighDefAudio = audioQuality == 2866 com.android.internal.telephony.Connection.AUDIO_QUALITY_HIGH_DEFINITION; 2867 updateConnectionProperties(); 2868 } 2869 resetStateForConference()2870 void resetStateForConference() { 2871 if (getState() == Connection.STATE_HOLDING) { 2872 resetStateOverride(); 2873 } 2874 } 2875 setHoldingForConference()2876 boolean setHoldingForConference() { 2877 if (getState() == Connection.STATE_ACTIVE) { 2878 setStateOverride(Call.State.HOLDING); 2879 return true; 2880 } 2881 return false; 2882 } 2883 setRttTextStream(RttTextStream s)2884 public void setRttTextStream(RttTextStream s) { 2885 mRttTextStream = s; 2886 } 2887 getRttTextStream()2888 public RttTextStream getRttTextStream() { 2889 return mRttTextStream; 2890 } 2891 2892 /** 2893 * For video calls, sets whether this connection supports pausing the outgoing video for the 2894 * call using the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState. 2895 * 2896 * @param isVideoPauseSupported {@code true} if pause state supported, {@code false} otherwise. 2897 */ setVideoPauseSupported(boolean isVideoPauseSupported)2898 public void setVideoPauseSupported(boolean isVideoPauseSupported) { 2899 mIsVideoPauseSupported = isVideoPauseSupported; 2900 } 2901 2902 /** 2903 * @return {@code true} if this connection supports pausing the outgoing video using the 2904 * {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState. 2905 */ getVideoPauseSupported()2906 public boolean getVideoPauseSupported() { 2907 return mIsVideoPauseSupported; 2908 } 2909 2910 /** 2911 * Sets whether this connection supports conference calling. 2912 * @param isConferenceSupported {@code true} if conference calling is supported by this 2913 * connection, {@code false} otherwise. 2914 */ setConferenceSupported(boolean isConferenceSupported)2915 public void setConferenceSupported(boolean isConferenceSupported) { 2916 mIsConferenceSupported = isConferenceSupported; 2917 } 2918 2919 /** 2920 * @return {@code true} if this connection supports merging calls into a conference. 2921 */ isConferenceSupported()2922 public boolean isConferenceSupported() { 2923 return mIsConferenceSupported; 2924 } 2925 2926 /** 2927 * Sets whether managing conference call is supported after this connection being a part of a 2928 * Ims conference. 2929 * 2930 * @param isManageImsConferenceCallSupported {@code true} if manage conference calling is 2931 * supported after this connection being a part of a IMS conference, 2932 * {@code false} otherwise. 2933 */ setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported)2934 public void setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported) { 2935 mIsManageImsConferenceCallSupported = isManageImsConferenceCallSupported; 2936 } 2937 2938 /** 2939 * @return {@code true} if manage conference calling is supported after this connection being a 2940 * part of a IMS conference. 2941 */ isManageImsConferenceCallSupported()2942 public boolean isManageImsConferenceCallSupported() { 2943 return mIsManageImsConferenceCallSupported; 2944 } 2945 2946 /** 2947 * Sets whether this connection supports showing precise call disconnect cause. 2948 * @param showPreciseFailedCause {@code true} if showing precise call 2949 * disconnect cause is supported by this connection, {@code false} otherwise. 2950 */ setShowPreciseFailedCause(boolean showPreciseFailedCause)2951 public void setShowPreciseFailedCause(boolean showPreciseFailedCause) { 2952 mShowPreciseFailedCause = showPreciseFailedCause; 2953 } 2954 2955 /** 2956 * Sets whether TTY is enabled or not. 2957 * @param isTtyEnabled 2958 */ setTtyEnabled(boolean isTtyEnabled)2959 public void setTtyEnabled(boolean isTtyEnabled) { 2960 mIsTtyEnabled = isTtyEnabled; 2961 updateConnectionCapabilities(); 2962 } 2963 2964 /** 2965 * Whether the original connection is an IMS connection. 2966 * @return {@code True} if the original connection is an IMS connection, {@code false} 2967 * otherwise. 2968 */ isImsConnection()2969 protected boolean isImsConnection() { 2970 com.android.internal.telephony.Connection originalConnection = getOriginalConnection(); 2971 2972 return originalConnection != null 2973 && originalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS 2974 && originalConnection instanceof ImsPhoneConnection; 2975 } 2976 2977 /** 2978 * Whether the original connection is an GSM/CDMA connection. 2979 * @return {@code True} if the original connection is an GSM/CDMA connection, {@code false} 2980 * otherwise. 2981 */ isGsmCdmaConnection()2982 protected boolean isGsmCdmaConnection() { 2983 Phone phone = getPhone(); 2984 if (phone != null) { 2985 switch (phone.getPhoneType()) { 2986 case PhoneConstants.PHONE_TYPE_GSM: 2987 case PhoneConstants.PHONE_TYPE_CDMA: 2988 return true; 2989 default: 2990 return false; 2991 } 2992 } 2993 return false; 2994 } 2995 2996 /** 2997 * Whether the original connection was ever an IMS connection, either before or now. 2998 * @return {@code True} if the original connection was ever an IMS connection, {@code false} 2999 * otherwise. 3000 */ wasImsConnection()3001 public boolean wasImsConnection() { 3002 return mWasImsConnection; 3003 } 3004 getIsUsingAssistedDialing()3005 boolean getIsUsingAssistedDialing() { 3006 return mIsUsingAssistedDialing; 3007 } 3008 setIsUsingAssistedDialing(Boolean isUsingAssistedDialing)3009 void setIsUsingAssistedDialing(Boolean isUsingAssistedDialing) { 3010 mIsUsingAssistedDialing = isUsingAssistedDialing; 3011 updateConnectionProperties(); 3012 } 3013 getAddressFromNumber(String number)3014 private static Uri getAddressFromNumber(String number) { 3015 // Address can be null for blocked calls. 3016 if (number == null) { 3017 number = ""; 3018 } 3019 return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null); 3020 } 3021 3022 /** 3023 * Changes a capabilities bit-mask to add or remove a capability. 3024 * 3025 * @param bitmask The bit-mask. 3026 * @param bitfield The bit-field to change. 3027 * @param enabled Whether the bit-field should be set or removed. 3028 * @return The bit-mask with the bit-field changed. 3029 */ changeBitmask(int bitmask, int bitfield, boolean enabled)3030 private int changeBitmask(int bitmask, int bitfield, boolean enabled) { 3031 if (enabled) { 3032 return bitmask | bitfield; 3033 } else { 3034 return bitmask & ~bitfield; 3035 } 3036 } 3037 updateStatusHints()3038 private void updateStatusHints() { 3039 if (isWifi() && !isCrossSimCall() && getPhone() != null) { 3040 int labelId = isValidRingingCall() 3041 ? R.string.status_hint_label_incoming_wifi_call 3042 : R.string.status_hint_label_wifi_call; 3043 3044 Context context = getPhone().getContext(); 3045 setTelephonyStatusHints(new StatusHints( 3046 getResourceString(labelId), 3047 Icon.createWithResource( 3048 context, R.drawable.ic_signal_wifi_4_bar_24dp), 3049 null /* extras */)); 3050 } else { 3051 setTelephonyStatusHints(null); 3052 } 3053 } 3054 3055 /** 3056 * Register a listener for {@link TelephonyConnection} specific triggers. 3057 * @param l The instance of the listener to add 3058 * @return The connection being listened to 3059 */ addTelephonyConnectionListener(TelephonyConnectionListener l)3060 public final TelephonyConnection addTelephonyConnectionListener(TelephonyConnectionListener l) { 3061 mTelephonyListeners.add(l); 3062 // If we already have an original connection, let's call back immediately. 3063 // This would be the case for incoming calls. 3064 if (mOriginalConnection != null) { 3065 fireOnOriginalConnectionConfigured(); 3066 } 3067 return this; 3068 } 3069 3070 /** 3071 * Remove a listener for {@link TelephonyConnection} specific triggers. 3072 * @param l The instance of the listener to remove 3073 * @return The connection being listened to 3074 */ removeTelephonyConnectionListener( TelephonyConnectionListener l)3075 public final TelephonyConnection removeTelephonyConnectionListener( 3076 TelephonyConnectionListener l) { 3077 if (l != null) { 3078 mTelephonyListeners.remove(l); 3079 } 3080 return this; 3081 } 3082 3083 @Override setHoldable(boolean isHoldable)3084 public void setHoldable(boolean isHoldable) { 3085 mIsHoldable = isHoldable; 3086 updateConnectionCapabilities(); 3087 } 3088 3089 @Override isChildHoldable()3090 public boolean isChildHoldable() { 3091 return getConference() != null; 3092 } 3093 isHoldable()3094 public boolean isHoldable() { 3095 return mIsHoldable; 3096 } 3097 3098 /** 3099 * Fire a callback to the various listeners for when the original connection is 3100 * set in this {@link TelephonyConnection} 3101 */ fireOnOriginalConnectionConfigured()3102 private final void fireOnOriginalConnectionConfigured() { 3103 for (TelephonyConnectionListener l : mTelephonyListeners) { 3104 l.onOriginalConnectionConfigured(this); 3105 } 3106 } 3107 fireOnOriginalConnectionRetryDial(boolean isPermanentFailure)3108 private final void fireOnOriginalConnectionRetryDial(boolean isPermanentFailure) { 3109 for (TelephonyConnectionListener l : mTelephonyListeners) { 3110 l.onOriginalConnectionRetry(this, isPermanentFailure); 3111 } 3112 } 3113 3114 /** 3115 * Handles exiting ECM mode. 3116 */ handleExitedEcmMode()3117 protected void handleExitedEcmMode() { 3118 updateConnectionProperties(); 3119 } 3120 3121 /** 3122 * Determines whether the connection supports conference calling. A connection supports 3123 * conference calling if it: 3124 * 1. Is not an emergency call. 3125 * 2. Carrier supports conference calls. 3126 * 3. If call is a video call, carrier supports video conference calls. 3127 * 4. If call is a wifi call and VoWIFI is disabled and carrier supports merging these calls. 3128 */ 3129 @VisibleForTesting refreshConferenceSupported()3130 void refreshConferenceSupported() { 3131 boolean isVideoCall = VideoProfile.isVideo(getVideoState()); 3132 Phone phone = getPhone(); 3133 if (phone == null) { 3134 Log.w(this, "refreshConferenceSupported = false; phone is null"); 3135 if (isConferenceSupported()) { 3136 setConferenceSupported(false); 3137 notifyConferenceSupportedChanged(false); 3138 } 3139 return; 3140 } 3141 3142 boolean isIms = phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS; 3143 boolean isVoWifiEnabled = false; 3144 if (isIms) { 3145 isVoWifiEnabled = isWfcEnabled(phone); 3146 } 3147 PhoneAccountHandle phoneAccountHandle = isIms ? PhoneUtils 3148 .makePstnPhoneAccountHandle(phone.getDefaultPhone()) 3149 : PhoneUtils.makePstnPhoneAccountHandle(phone); 3150 TelecomAccountRegistry telecomAccountRegistry = getTelecomAccountRegistry( 3151 getPhone().getContext()); 3152 boolean isConferencingSupported = telecomAccountRegistry 3153 .isMergeCallSupported(phoneAccountHandle); 3154 boolean isImsConferencingSupported = telecomAccountRegistry 3155 .isMergeImsCallSupported(phoneAccountHandle); 3156 mIsCarrierVideoConferencingSupported = telecomAccountRegistry 3157 .isVideoConferencingSupported(phoneAccountHandle); 3158 boolean isMergeOfWifiCallsAllowedWhenVoWifiOff = telecomAccountRegistry 3159 .isMergeOfWifiCallsAllowedWhenVoWifiOff(phoneAccountHandle); 3160 ImsCall imsCall = isImsConnection() 3161 ? ((ImsPhoneConnection) getOriginalConnection()).getImsCall() 3162 : null; 3163 CarrierConfigManager configManager = (CarrierConfigManager) phone.getContext() 3164 .getSystemService(Context.CARRIER_CONFIG_SERVICE); 3165 boolean downGradedVideoCall = false; 3166 if (configManager != null) { 3167 PersistableBundle config = configManager.getConfigForSubId(phone.getSubId()); 3168 if (config != null) { 3169 downGradedVideoCall = config.getBoolean( 3170 CarrierConfigManager.KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL); 3171 } 3172 } 3173 3174 Log.v(this, "refreshConferenceSupported : isConfSupp=%b, isImsConfSupp=%b, " + 3175 "isVidConfSupp=%b, isMergeOfWifiAllowed=%b, " + 3176 "isWifi=%b, isVoWifiEnabled=%b", 3177 isConferencingSupported, isImsConferencingSupported, 3178 mIsCarrierVideoConferencingSupported, isMergeOfWifiCallsAllowedWhenVoWifiOff, 3179 isWifi(), isVoWifiEnabled); 3180 boolean isConferenceSupported = true; 3181 if (mTreatAsEmergencyCall) { 3182 isConferenceSupported = false; 3183 Log.d(this, "refreshConferenceSupported = false; emergency call"); 3184 } else if (isRtt() && !isRttMergeSupported(getCarrierConfig())) { 3185 isConferenceSupported = false; 3186 Log.d(this, "refreshConferenceSupported = false; rtt call"); 3187 } else if (!isConferencingSupported || isIms && !isImsConferencingSupported) { 3188 isConferenceSupported = false; 3189 Log.d(this, "refreshConferenceSupported = false; carrier doesn't support conf."); 3190 } else if (isVideoCall && !mIsCarrierVideoConferencingSupported) { 3191 isConferenceSupported = false; 3192 Log.d(this, "refreshConferenceSupported = false; video conf not supported."); 3193 } else if ((imsCall != null) && (imsCall.wasVideoCall() && downGradedVideoCall) 3194 && !mIsCarrierVideoConferencingSupported) { 3195 isConferenceSupported = false; 3196 Log.d(this, 3197 "refreshConferenceSupported = false;" 3198 + " video conf not supported for downgraded audio call."); 3199 } else if (!isMergeOfWifiCallsAllowedWhenVoWifiOff && isWifi() && !isVoWifiEnabled) { 3200 isConferenceSupported = false; 3201 Log.d(this, 3202 "refreshConferenceSupported = false; can't merge wifi calls when voWifi off."); 3203 } else { 3204 Log.d(this, "refreshConferenceSupported = true."); 3205 } 3206 3207 if (isConferenceSupported != isConferenceSupported()) { 3208 setConferenceSupported(isConferenceSupported); 3209 notifyConferenceSupportedChanged(isConferenceSupported); 3210 } 3211 } 3212 3213 @VisibleForTesting isWfcEnabled(Phone phone)3214 boolean isWfcEnabled(Phone phone) { 3215 return ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId()); 3216 } 3217 3218 /** 3219 * Provides a mapping from extras keys which may be found in the 3220 * {@link com.android.internal.telephony.Connection} to their equivalents defined in 3221 * {@link android.telecom.Connection}. 3222 * 3223 * @return Map containing key mappings. 3224 */ createExtrasMap()3225 private static Map<String, String> createExtrasMap() { 3226 Map<String, String> result = new HashMap<String, String>(); 3227 result.put(ImsCallProfile.EXTRA_CHILD_NUMBER, 3228 android.telecom.Connection.EXTRA_CHILD_ADDRESS); 3229 result.put(ImsCallProfile.EXTRA_DISPLAY_TEXT, 3230 android.telecom.Connection.EXTRA_CALL_SUBJECT); 3231 result.put(ImsCallProfile.EXTRA_ADDITIONAL_SIP_INVITE_FIELDS, 3232 android.telecom.Connection.EXTRA_SIP_INVITE); 3233 return Collections.unmodifiableMap(result); 3234 } 3235 isShowingOriginalDialString()3236 private boolean isShowingOriginalDialString() { 3237 boolean showOrigDialString = false; 3238 Phone phone = getPhone(); 3239 if (phone != null && (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) 3240 && !mOriginalConnection.isIncoming()) { 3241 showOrigDialString = getCarrierConfig().getBoolean(CarrierConfigManager 3242 .KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL); 3243 Log.d(this, "showOrigDialString: " + showOrigDialString); 3244 } 3245 return showOrigDialString; 3246 } 3247 3248 /** 3249 * Creates a string representation of this {@link TelephonyConnection}. Primarily intended for 3250 * use in log statements. 3251 * 3252 * @return String representation of the connection. 3253 */ 3254 @Override toString()3255 public String toString() { 3256 StringBuilder sb = new StringBuilder(); 3257 sb.append("[TelephonyConnection objId:"); 3258 sb.append(System.identityHashCode(this)); 3259 sb.append(" telecomCallID:"); 3260 sb.append(getTelecomCallId()); 3261 sb.append(" type:"); 3262 if (isImsConnection()) { 3263 sb.append("ims"); 3264 } else if (this instanceof com.android.services.telephony.GsmConnection) { 3265 sb.append("gsm"); 3266 } else if (this instanceof CdmaConnection) { 3267 sb.append("cdma"); 3268 } 3269 sb.append(" state:"); 3270 sb.append(Connection.stateToString(getState())); 3271 sb.append(" capabilities:"); 3272 sb.append(capabilitiesToString(getConnectionCapabilities())); 3273 sb.append(" properties:"); 3274 sb.append(propertiesToString(getConnectionProperties())); 3275 sb.append(" address:"); 3276 sb.append(Rlog.pii(LOG_TAG, getAddress())); 3277 sb.append(" originalConnection:"); 3278 sb.append(mOriginalConnection); 3279 sb.append(" partOfConf:"); 3280 if (getConference() == null) { 3281 sb.append("N"); 3282 } else { 3283 sb.append("Y"); 3284 } 3285 sb.append(" confSupported:"); 3286 sb.append(mIsConferenceSupported ? "Y" : "N"); 3287 sb.append(" isAdhocConf:"); 3288 sb.append(isAdhocConferenceCall() ? "Y" : "N"); 3289 sb.append("]"); 3290 return sb.toString(); 3291 } 3292 setTelephonyConnectionService(TelephonyConnectionService connectionService)3293 public final void setTelephonyConnectionService(TelephonyConnectionService connectionService) { 3294 mTelephonyConnectionService = connectionService; 3295 } 3296 getTelephonyConnectionService()3297 public final TelephonyConnectionService getTelephonyConnectionService() { 3298 return mTelephonyConnectionService; 3299 } 3300 3301 /** 3302 * Set this {@link TelephonyConnection} to an active state. 3303 * <p> 3304 * Note: This should be used instead of {@link #setActive()} to ensure listeners are notified. 3305 */ setTelephonyConnectionActive()3306 public void setTelephonyConnectionActive() { 3307 setActive(); 3308 notifyStateChanged(getState()); 3309 } 3310 3311 /** 3312 * Set this {@link TelephonyConnection} to a ringing state. 3313 * <p> 3314 * Note: This should be used instead of {@link #setRinging()} to ensure listeners are notified. 3315 */ setTelephonyConnectionRinging()3316 public void setTelephonyConnectionRinging() { 3317 setRinging(); 3318 notifyStateChanged(getState()); 3319 } 3320 3321 /** 3322 * Set this {@link TelephonyConnection} to an initializing state. 3323 * <p> 3324 * Note: This should be used instead of {@link #setInitializing()} to ensure listeners are 3325 * notified. 3326 */ setTelephonyConnectionInitializing()3327 public void setTelephonyConnectionInitializing() { 3328 setInitializing(); 3329 notifyStateChanged(getState()); 3330 } 3331 3332 /** 3333 * Set this {@link TelephonyConnection} to a dialing state. 3334 * <p> 3335 * Note: This should be used instead of {@link #setDialing()} to ensure listeners are notified. 3336 */ setTelephonyConnectionDialing()3337 public void setTelephonyConnectionDialing() { 3338 setDialing(); 3339 notifyStateChanged(getState()); 3340 } 3341 3342 /** 3343 * Set this {@link TelephonyConnection} to a pulling state. 3344 * <p> 3345 * Note: This should be used instead of {@link #setPulling()} to ensure listeners are notified. 3346 */ setTelephonyConnectionPulling()3347 public void setTelephonyConnectionPulling() { 3348 setPulling(); 3349 notifyStateChanged(getState()); 3350 } 3351 3352 /** 3353 * Set this {@link TelephonyConnection} to a held state. 3354 * <p> 3355 * Note: This should be used instead of {@link #setOnHold()} to ensure listeners are notified. 3356 */ setTelephonyConnectionOnHold()3357 public void setTelephonyConnectionOnHold() { 3358 setOnHold(); 3359 notifyStateChanged(getState()); 3360 } 3361 3362 /** 3363 * Set this {@link TelephonyConnection} to a disconnected state. 3364 * <p> 3365 * Note: This should be used instead of 3366 * {@link #setDisconnected(android.telecom.DisconnectCause)} to ensure listeners are notified. 3367 */ setTelephonyConnectionDisconnected(@onNull android.telecom.DisconnectCause disconnectCause)3368 public void setTelephonyConnectionDisconnected(@NonNull 3369 android.telecom.DisconnectCause disconnectCause) { 3370 setDisconnected(disconnectCause); 3371 notifyDisconnected(disconnectCause); 3372 notifyStateChanged(getState()); 3373 } 3374 3375 /** 3376 * Sends a connection event for this {@link TelephonyConnection}. 3377 * <p> 3378 * Note: This should be used instead of {@link #sendConnectionEvent(String, Bundle)} to ensure 3379 * listeners are notified. 3380 */ sendTelephonyConnectionEvent(@onNull String event, @Nullable Bundle extras)3381 public void sendTelephonyConnectionEvent(@NonNull String event, @Nullable Bundle extras) { 3382 sendConnectionEvent(event, extras); 3383 notifyTelephonyConnectionEvent(event, extras); 3384 } 3385 3386 /** 3387 * Sets the extras associated with this {@link TelephonyConnection}. 3388 * <p> 3389 * Note: This should be used instead of {@link #putExtras(Bundle)} to ensure listeners are 3390 * notified. 3391 */ putTelephonyExtras(@onNull Bundle extras)3392 public void putTelephonyExtras(@NonNull Bundle extras) { 3393 putExtras(extras); 3394 notifyPutExtras(extras); 3395 } 3396 3397 /** 3398 * Removes the specified extras associated with this {@link TelephonyConnection}. 3399 * <p> 3400 * Note: This should be used instead of {@link #removeExtras(String...)} to ensure listeners are 3401 * notified. 3402 */ removeTelephonyExtras(@onNull List<String> keys)3403 public void removeTelephonyExtras(@NonNull List<String> keys) { 3404 removeExtras(keys); 3405 notifyRemoveExtras(keys); 3406 } 3407 3408 /** 3409 * Sets the video state associated with this {@link TelephonyConnection}. 3410 * <p> 3411 * Note: This should be used instead of {@link #setVideoState(int)} to ensure listeners are 3412 * notified. 3413 * @param videoState The new video state. Valid values: 3414 * {@link VideoProfile#STATE_AUDIO_ONLY}, 3415 * {@link VideoProfile#STATE_BIDIRECTIONAL}, 3416 * {@link VideoProfile#STATE_TX_ENABLED}, 3417 * {@link VideoProfile#STATE_RX_ENABLED}. 3418 */ setTelephonyVideoState(int videoState)3419 public void setTelephonyVideoState(int videoState) { 3420 setVideoState(videoState); 3421 notifyVideoStateChanged(videoState); 3422 } 3423 3424 /** 3425 * Sets the video provider associated with this {@link TelephonyConnection}. 3426 * <p> 3427 * Note: This should be used instead of {@link #setVideoProvider(VideoProvider)} to ensure 3428 * listeners are notified. 3429 */ setTelephonyVideoProvider(@ullable VideoProvider videoProvider)3430 public void setTelephonyVideoProvider(@Nullable VideoProvider videoProvider) { 3431 setVideoProvider(videoProvider); 3432 notifyVideoProviderChanged(videoProvider); 3433 } 3434 3435 /** 3436 * Sets the status hints associated with this {@link TelephonyConnection}. 3437 * <p> 3438 * Note: This should be used instead of {@link #setStatusHints(StatusHints)} to ensure listeners 3439 * are notified. 3440 */ setTelephonyStatusHints(@ullable StatusHints statusHints)3441 public void setTelephonyStatusHints(@Nullable StatusHints statusHints) { 3442 setStatusHints(statusHints); 3443 notifyStatusHintsChanged(statusHints); 3444 } 3445 3446 /** 3447 * Sets RIL voice radio technology used for current connection. 3448 * <p> 3449 * This property is set by the Telephony {@link ConnectionService}. 3450 * 3451 * @param vrat the RIL Voice Radio Technology used for current connection, 3452 * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}. 3453 */ setCallRadioTech(@ilRadioTechnology int vrat)3454 public final void setCallRadioTech(@RilRadioTechnology int vrat) { 3455 Bundle extras = getExtras(); 3456 if (extras == null) { 3457 extras = new Bundle(); 3458 } 3459 extras.putInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE, 3460 ServiceState.rilRadioTechnologyToNetworkType(vrat)); 3461 putExtras(extras); 3462 // Propagates the call radio technology to its parent {@link android.telecom.Conference} 3463 // This action only covers non-IMS CS conference calls. 3464 // For IMS PS call conference call, it can be updated via its host connection 3465 // {@link #Listener.onExtrasChanged} event. 3466 if (getConference() != null) { 3467 Bundle newExtras = new Bundle(); 3468 newExtras.putInt( 3469 TelecomManager.EXTRA_CALL_NETWORK_TYPE, 3470 ServiceState.rilRadioTechnologyToNetworkType(vrat)); 3471 getConference().putExtras(newExtras); 3472 } 3473 } 3474 3475 /** 3476 * Returns RIL voice radio technology used for current connection. 3477 * <p> 3478 * Used by the Telephony {@link ConnectionService}. 3479 * 3480 * @return the RIL voice radio technology used for current connection, 3481 * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}. 3482 */ getCallRadioTech()3483 public final @RilRadioTechnology int getCallRadioTech() { 3484 int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; 3485 Bundle extras = getExtras(); 3486 if (extras != null) { 3487 voiceNetworkType = extras.getInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE, 3488 TelephonyManager.NETWORK_TYPE_UNKNOWN); 3489 } 3490 return ServiceState.networkTypeToRilRadioTechnology(voiceNetworkType); 3491 } 3492 3493 /** 3494 * Notifies {@link TelephonyConnectionListener}s of a change to conference participant data 3495 * received via the {@link ImsConference} (i.e. conference event package). 3496 * 3497 * @param conferenceParticipants The participants. 3498 */ updateConferenceParticipants( @onNull List<ConferenceParticipant> conferenceParticipants)3499 private void updateConferenceParticipants( 3500 @NonNull List<ConferenceParticipant> conferenceParticipants) { 3501 for (TelephonyConnectionListener l : mTelephonyListeners) { 3502 l.onConferenceParticipantsChanged(this, conferenceParticipants); 3503 } 3504 } 3505 3506 /** 3507 * Where device to device communication is available and this is an IMS call, configures the 3508 * D2D communication infrastructure for operation. 3509 */ maybeConfigureDeviceToDeviceCommunication()3510 private void maybeConfigureDeviceToDeviceCommunication() { 3511 if (!getPhone().getContext().getResources().getBoolean( 3512 R.bool.config_use_device_to_device_communication)) { 3513 Log.i(this, "maybeConfigureDeviceToDeviceCommunication: not using D2D."); 3514 notifyD2DAvailabilityChanged(false); 3515 return; 3516 } 3517 if (!isImsConnection()) { 3518 Log.i(this, "maybeConfigureDeviceToDeviceCommunication: not an IMS connection."); 3519 if (mCommunicator != null) { 3520 mCommunicator = null; 3521 } 3522 notifyD2DAvailabilityChanged(false); 3523 return; 3524 } 3525 if (mTreatAsEmergencyCall || mIsNetworkIdentifiedEmergencyCall) { 3526 Log.i(this, "maybeConfigureDeviceToDeviceCommunication: emergency call; no D2D"); 3527 notifyD2DAvailabilityChanged(false); 3528 return; 3529 } 3530 3531 ArrayList<TransportProtocol> supportedTransports = new ArrayList<>(2); 3532 3533 if (supportsD2DUsingRtp()) { 3534 Log.i(this, "maybeConfigureDeviceToDeviceCommunication: carrier supports RTP."); 3535 // Implement abstracted out RTP functionality the RTP transport depends on. 3536 RtpAdapter rtpAdapter = new RtpAdapter() { 3537 @Override 3538 public Set<RtpHeaderExtensionType> getAcceptedRtpHeaderExtensions() { 3539 ImsPhoneConnection originalConnection = 3540 (ImsPhoneConnection) mOriginalConnection; 3541 return originalConnection.getAcceptedRtpHeaderExtensions(); 3542 } 3543 3544 @Override 3545 public void sendRtpHeaderExtensions( 3546 @NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) { 3547 Log.i(TelephonyConnection.this, "sendRtpHeaderExtensions: sending: %s", 3548 rtpHeaderExtensions.stream() 3549 .map(r -> r.toString()) 3550 .collect(Collectors.joining(","))); 3551 ImsPhoneConnection originalConnection = 3552 (ImsPhoneConnection) mOriginalConnection; 3553 originalConnection.sendRtpHeaderExtensions(rtpHeaderExtensions); 3554 } 3555 }; 3556 mRtpTransport = new RtpTransport(rtpAdapter, null /* TODO: not needed yet */, mHandler, 3557 supportsSdpNegotiationOfRtpHeaderExtensions()); 3558 supportedTransports.add(mRtpTransport); 3559 } 3560 if (supportsD2DUsingDtmf()) { 3561 Log.i(this, "maybeConfigureDeviceToDeviceCommunication: carrier supports DTMF."); 3562 DtmfAdapter dtmfAdapter = digit -> { 3563 Log.i(TelephonyConnection.this, "sendDtmf: send digit %c", digit); 3564 ImsPhoneConnection originalConnection = 3565 (ImsPhoneConnection) mOriginalConnection; 3566 Message dtmfComplete = mHandler.obtainMessage(MSG_DTMF_DONE); 3567 dtmfComplete.replyTo = mHandlerMessenger; 3568 originalConnection.getImsCall().sendDtmf(digit, dtmfComplete); 3569 }; 3570 ContentResolver cr = getPhone().getContext().getContentResolver(); 3571 mDtmfTransport = new DtmfTransport(dtmfAdapter, new Timeouts.Adapter(cr), 3572 Executors.newSingleThreadScheduledExecutor()); 3573 supportedTransports.add(mDtmfTransport); 3574 } 3575 if (supportedTransports.size() > 0) { 3576 mCommunicator = new Communicator(supportedTransports, this); 3577 mD2DCallStateAdapter = new D2DCallStateAdapter(mCommunicator); 3578 addTelephonyConnectionListener(mD2DCallStateAdapter); 3579 } else { 3580 Log.i(this, "maybeConfigureDeviceToDeviceCommunication: no transports; disabled."); 3581 notifyD2DAvailabilityChanged(false); 3582 } 3583 } 3584 3585 /** 3586 * Notifies upper layers of the availability of D2D communication. 3587 * @param isAvailable {@code true} if D2D is available, {@code false} otherwise. 3588 */ notifyD2DAvailabilityChanged(boolean isAvailable)3589 private void notifyD2DAvailabilityChanged(boolean isAvailable) { 3590 Bundle extras = new Bundle(); 3591 extras.putBoolean(Connection.EXTRA_IS_DEVICE_TO_DEVICE_COMMUNICATION_AVAILABLE, 3592 isAvailable); 3593 putTelephonyExtras(extras); 3594 } 3595 3596 /** 3597 * @return The D2D communication class, or {@code null} if not set up. 3598 */ getCommunicator()3599 public @Nullable Communicator getCommunicator() { 3600 return mCommunicator; 3601 } 3602 3603 /** 3604 * Called by {@link Communicator} associated with this {@link TelephonyConnection} when there 3605 * are incoming device-to-device messages received. 3606 * @param messages the incoming messages. 3607 */ 3608 @Override onMessagesReceived(@onNull Set<Communicator.Message> messages)3609 public void onMessagesReceived(@NonNull Set<Communicator.Message> messages) { 3610 Log.i(this, "onMessagesReceived: got d2d messages: %s", messages); 3611 // Send connection events up to Telecom so that we can relay the messages to a valid 3612 // CallDiagnosticService which is bound. 3613 for (Communicator.Message msg : messages) { 3614 Integer dcMsgType = MessageTypeAndValueHelper.MSG_TYPE_TO_DC_MSG_TYPE.getValue( 3615 msg.getType()); 3616 if (dcMsgType == null) { 3617 // Invalid msg type, skip. 3618 continue; 3619 } 3620 3621 Integer dcMsgValue; 3622 switch (msg.getType()) { 3623 case CallDiagnostics.MESSAGE_CALL_AUDIO_CODEC: 3624 dcMsgValue = MessageTypeAndValueHelper.CODEC_TO_DC_CODEC.getValue( 3625 msg.getValue()); 3626 break; 3627 case CallDiagnostics.MESSAGE_CALL_NETWORK_TYPE: 3628 dcMsgValue = MessageTypeAndValueHelper.RAT_TYPE_TO_DC_NETWORK_TYPE.getValue( 3629 msg.getValue()); 3630 break; 3631 case CallDiagnostics.MESSAGE_DEVICE_BATTERY_STATE: 3632 dcMsgValue = MessageTypeAndValueHelper.BATTERY_STATE_TO_DC_BATTERY_STATE 3633 .getValue(msg.getValue()); 3634 break; 3635 case CallDiagnostics.MESSAGE_DEVICE_NETWORK_COVERAGE: 3636 dcMsgValue = MessageTypeAndValueHelper.COVERAGE_TO_DC_COVERAGE 3637 .getValue(msg.getValue()); 3638 break; 3639 default: 3640 Log.w(this, "onMessagesReceived: msg=%d - invalid msg", msg.getValue()); 3641 continue; 3642 } 3643 if (dcMsgValue == null) { 3644 Log.w(this, "onMessagesReceived: msg=%d/%d - invalid msg value", msg.getType(), 3645 msg.getValue()); 3646 continue; 3647 } 3648 Bundle extras = new Bundle(); 3649 extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE, dcMsgType); 3650 extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE, dcMsgValue); 3651 sendConnectionEvent(Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE, extras); 3652 } 3653 } 3654 3655 /** 3656 * Handles report from {@link Communicator} when the availability of D2D changes. 3657 * @param isAvailable {@code true} if D2D is available, {@code false} if unavailable. 3658 */ 3659 @Override onD2DAvailabilitychanged(boolean isAvailable)3660 public void onD2DAvailabilitychanged(boolean isAvailable) { 3661 notifyD2DAvailabilityChanged(isAvailable); 3662 } 3663 3664 /** 3665 * Called by a {@link ConnectionService} to notify Telecom that a {@link Conference#onMerge()} 3666 * operation has started. 3667 */ notifyConferenceStarted()3668 protected void notifyConferenceStarted() { 3669 for (TelephonyConnectionListener l : mTelephonyListeners) { 3670 l.onConferenceStarted(); 3671 } 3672 } 3673 3674 /** 3675 * Notifies {@link TelephonyConnectionListener}s when a change has occurred to the Connection 3676 * which impacts its ability to be a part of a conference call. 3677 * @param isConferenceSupported {@code true} if the connection supports being part of a 3678 * conference call, {@code false} otherwise. 3679 */ notifyConferenceSupportedChanged(boolean isConferenceSupported)3680 private void notifyConferenceSupportedChanged(boolean isConferenceSupported) { 3681 for (TelephonyConnectionListener l : mTelephonyListeners) { 3682 l.onConferenceSupportedChanged(this, isConferenceSupported); 3683 } 3684 } 3685 3686 /** 3687 * Notifies {@link TelephonyConnectionListener}s of changes to the connection capabilities. 3688 * @param newCapabilities the new capabilities. 3689 */ notifyConnectionCapabilitiesChanged(int newCapabilities)3690 private void notifyConnectionCapabilitiesChanged(int newCapabilities) { 3691 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3692 listener.onConnectionCapabilitiesChanged(this, newCapabilities); 3693 } 3694 } 3695 3696 /** 3697 * Notifies {@link TelephonyConnectionListener}s of changes to the connection properties. 3698 * @param newProperties the new properties. 3699 */ notifyConnectionPropertiesChanged(int newProperties)3700 private void notifyConnectionPropertiesChanged(int newProperties) { 3701 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3702 listener.onConnectionPropertiesChanged(this, newProperties); 3703 } 3704 } 3705 3706 /** 3707 * Notifies {@link TelephonyConnectionListener}s when a connection is destroyed. 3708 */ notifyDestroyed()3709 private void notifyDestroyed() { 3710 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3711 listener.onDestroyed(this); 3712 } 3713 } 3714 3715 /** 3716 * Notifies {@link TelephonyConnectionListener}s when a connection disconnects. 3717 * @param cause The disconnect cause. 3718 */ notifyDisconnected(android.telecom.DisconnectCause cause)3719 private void notifyDisconnected(android.telecom.DisconnectCause cause) { 3720 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3721 listener.onDisconnected(this, cause); 3722 } 3723 } 3724 3725 /** 3726 * Notifies {@link TelephonyConnectionListener}s of connection state changes. 3727 * @param newState The new state. 3728 */ notifyStateChanged(int newState)3729 private void notifyStateChanged(int newState) { 3730 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3731 listener.onStateChanged(this, newState); 3732 } 3733 } 3734 3735 /** 3736 * Notifies {@link TelephonyConnectionListener}s of telephony connection events. 3737 * @param event The event. 3738 * @param extras Any extras. 3739 */ notifyTelephonyConnectionEvent(String event, Bundle extras)3740 private void notifyTelephonyConnectionEvent(String event, Bundle extras) { 3741 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3742 listener.onConnectionEvent(this, event, extras); 3743 } 3744 } 3745 3746 /** 3747 * Notifies {@link TelephonyConnectionListener}s when extras are added to the connection. 3748 * @param extras The new extras. 3749 */ notifyPutExtras(Bundle extras)3750 private void notifyPutExtras(Bundle extras) { 3751 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3752 listener.onExtrasChanged(this, extras); 3753 } 3754 } 3755 3756 /** 3757 * Notifies {@link TelephonyConnectionListener}s when extra keys are removed from a connection. 3758 * @param keys The removed keys. 3759 */ notifyRemoveExtras(List<String> keys)3760 private void notifyRemoveExtras(List<String> keys) { 3761 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3762 listener.onExtrasRemoved(this, keys); 3763 } 3764 } 3765 3766 /** 3767 * Notifies {@link TelephonyConnectionListener}s of a change to the video state of a connection. 3768 * @param videoState The new video state. Valid values: 3769 * {@link VideoProfile#STATE_AUDIO_ONLY}, 3770 * {@link VideoProfile#STATE_BIDIRECTIONAL}, 3771 * {@link VideoProfile#STATE_TX_ENABLED}, 3772 * {@link VideoProfile#STATE_RX_ENABLED}. 3773 */ notifyVideoStateChanged(int videoState)3774 private void notifyVideoStateChanged(int videoState) { 3775 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3776 listener.onVideoStateChanged(this, videoState); 3777 } 3778 } 3779 3780 /** 3781 * Notifies {@link TelephonyConnectionListener}s of a whether to play Ringback Tone or not. 3782 * @param ringback Whether the ringback tone is to be played 3783 */ notifyRingbackRequested(boolean ringback)3784 private void notifyRingbackRequested(boolean ringback) { 3785 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3786 listener.onRingbackRequested(this, ringback); 3787 } 3788 } 3789 3790 /** 3791 * Notifies {@link TelephonyConnectionListener}s of changes to the video provider for a 3792 * connection. 3793 * @param videoProvider The new video provider. 3794 */ notifyVideoProviderChanged(VideoProvider videoProvider)3795 private void notifyVideoProviderChanged(VideoProvider videoProvider) { 3796 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3797 listener.onVideoProviderChanged(this, videoProvider); 3798 } 3799 } 3800 3801 /** 3802 * Notifies {@link TelephonyConnectionListener}s of changes to the status hints for a 3803 * connection. 3804 * @param statusHints The new status hints. 3805 */ notifyStatusHintsChanged(StatusHints statusHints)3806 private void notifyStatusHintsChanged(StatusHints statusHints) { 3807 for (TelephonyConnectionListener listener : mTelephonyListeners) { 3808 listener.onStatusHintsChanged(this, statusHints); 3809 } 3810 } 3811 3812 /** 3813 * Whether the incoming call number should be formatted to national number for Japan. 3814 * @return {@code true} should be convert to the national format, {@code false} otherwise. 3815 */ isNeededToFormatIncomingNumberForJp()3816 private boolean isNeededToFormatIncomingNumberForJp() { 3817 if (mOriginalConnection.isIncoming() 3818 && !TextUtils.isEmpty(mOriginalConnection.getAddress()) 3819 && mOriginalConnection.getAddress().startsWith(JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN)) { 3820 return getCarrierConfig().getBoolean( 3821 CarrierConfigManager.KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL); 3822 } 3823 return false; 3824 } 3825 3826 /** 3827 * Format the incoming call number to national number for Japan. 3828 * @param number 3829 * @return the formatted phone number (e.g, "+819012345678" -> "09012345678") 3830 */ formatIncomingNumberForJp(String number)3831 private String formatIncomingNumberForJp(String number) { 3832 return PhoneNumberUtils.stripSeparators( 3833 PhoneNumberUtils.formatNumber(number, JAPAN_ISO_COUNTRY_CODE)); 3834 } 3835 getTelecomAccountRegistry(Context context)3836 public TelecomAccountRegistry getTelecomAccountRegistry(Context context) { 3837 return TelecomAccountRegistry.getInstance(context); 3838 } 3839 3840 /** 3841 * @return {@code true} if the carrier supports D2D using RTP header extensions, {@code false} 3842 * otherwise. 3843 */ supportsD2DUsingRtp()3844 private boolean supportsD2DUsingRtp() { 3845 return getCarrierConfig().getBoolean( 3846 CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL); 3847 } 3848 3849 /** 3850 * @return {@code true} if the carrier supports D2D using DTMF digits, {@code false} otherwise. 3851 */ supportsD2DUsingDtmf()3852 private boolean supportsD2DUsingDtmf() { 3853 return getCarrierConfig().getBoolean( 3854 CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL); 3855 } 3856 3857 /** 3858 * @return {@code true} if the carrier supports using SDP negotiation for the RTP header 3859 * extensions used in D2D comms, {@code false} otherwise. 3860 */ supportsSdpNegotiationOfRtpHeaderExtensions()3861 private boolean supportsSdpNegotiationOfRtpHeaderExtensions() { 3862 return getCarrierConfig().getBoolean( 3863 CarrierConfigManager 3864 .KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL); 3865 } 3866 3867 /** 3868 * Handles a device to device message which a {@link CallDiagnostics} wishes to send. 3869 * @param extras the call event extras bundle. 3870 */ handleOutgoingDeviceToDeviceMessage(Bundle extras)3871 private void handleOutgoingDeviceToDeviceMessage(Bundle extras) { 3872 int messageType = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE); 3873 int messageValue = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE); 3874 3875 Integer internalMessageValue; 3876 switch (messageType) { 3877 case CallDiagnostics.MESSAGE_CALL_AUDIO_CODEC: 3878 internalMessageValue = MessageTypeAndValueHelper.CODEC_TO_DC_CODEC.getKey( 3879 messageValue); 3880 break; 3881 case CallDiagnostics.MESSAGE_CALL_NETWORK_TYPE: 3882 internalMessageValue = MessageTypeAndValueHelper.RAT_TYPE_TO_DC_NETWORK_TYPE.getKey( 3883 messageValue); 3884 break; 3885 case CallDiagnostics.MESSAGE_DEVICE_BATTERY_STATE: 3886 internalMessageValue = MessageTypeAndValueHelper.BATTERY_STATE_TO_DC_BATTERY_STATE 3887 .getKey(messageValue); 3888 break; 3889 case CallDiagnostics.MESSAGE_DEVICE_NETWORK_COVERAGE: 3890 internalMessageValue = MessageTypeAndValueHelper.COVERAGE_TO_DC_COVERAGE 3891 .getKey(messageValue); 3892 break; 3893 default: 3894 Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d - invalid msg", 3895 messageType); 3896 return; 3897 } 3898 Integer internalMessageType = MessageTypeAndValueHelper.MSG_TYPE_TO_DC_MSG_TYPE.getKey( 3899 messageType); 3900 if (internalMessageValue == null) { 3901 Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d/%d - invalid value", 3902 messageType, messageValue); 3903 return; 3904 } 3905 3906 if (mCommunicator != null) { 3907 Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d/%d - sending", 3908 internalMessageType, internalMessageValue); 3909 Set<Communicator.Message> set = new ArraySet<>(); 3910 set.add(new Communicator.Message(internalMessageType, internalMessageValue)); 3911 mCommunicator.sendMessages(set); 3912 } 3913 } 3914 3915 /** 3916 * Returns the current telephony connection listeners for test purposes. 3917 * @return list of telephony connection listeners. 3918 */ 3919 @VisibleForTesting getTelephonyConnectionListeners()3920 public List<TelephonyConnectionListener> getTelephonyConnectionListeners() { 3921 return new ArrayList<>(mTelephonyListeners); 3922 } 3923 3924 /** 3925 * @return An {@link Integer} instance of the emergency service category. 3926 */ getEmergencyServiceCategory()3927 public @Nullable Integer getEmergencyServiceCategory() { 3928 return mEmergencyServiceCategory; 3929 } 3930 3931 /** 3932 * Sets the emergency service category. 3933 * 3934 * @param eccCategory The emergency service category. 3935 */ 3936 @VisibleForTesting setEmergencyServiceCategory(int eccCategory)3937 public void setEmergencyServiceCategory(int eccCategory) { 3938 mEmergencyServiceCategory = eccCategory; 3939 } 3940 3941 /** 3942 * @return a {@link List} of {@link String}s that are the emrgency URNs. 3943 */ getEmergencyUrns()3944 public @Nullable List<String> getEmergencyUrns() { 3945 return mEmergencyUrns; 3946 } 3947 3948 /** 3949 * Set the emergency URNs. 3950 * 3951 * @param emergencyUrns The emergency URNs. 3952 */ 3953 @VisibleForTesting setEmergencyUrns(@ullable List<String> emergencyUrns)3954 public void setEmergencyUrns(@Nullable List<String> emergencyUrns) { 3955 mEmergencyUrns = emergencyUrns; 3956 } 3957 } 3958