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