TelephonyConnectionService.java revision e7939d87e0758327ebed3f433326aef75b887331
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 17package com.android.services.telephony; 18 19import android.content.ActivityNotFoundException; 20import android.content.ComponentName; 21import android.content.Context; 22import android.content.Intent; 23import android.net.Uri; 24import android.telecom.Connection; 25import android.telecom.ConnectionRequest; 26import android.telecom.ConnectionService; 27import android.telecom.PhoneAccount; 28import android.telecom.PhoneAccountHandle; 29import android.telephony.CarrierConfigManager; 30import android.telephony.DisconnectCause; 31import android.telephony.PhoneNumberUtils; 32import android.telephony.ServiceState; 33import android.telephony.SubscriptionManager; 34import android.telephony.TelephonyManager; 35import android.text.TextUtils; 36 37import com.android.internal.telephony.Call; 38import com.android.internal.telephony.CallStateException; 39import com.android.internal.telephony.IccCard; 40import com.android.internal.telephony.IccCardConstants; 41import com.android.internal.telephony.Phone; 42import com.android.internal.telephony.PhoneConstants; 43import com.android.internal.telephony.PhoneFactory; 44import com.android.internal.telephony.SubscriptionController; 45import com.android.phone.MMIDialogActivity; 46import com.android.phone.PhoneUtils; 47import com.android.phone.R; 48 49import java.util.ArrayList; 50import java.util.List; 51import java.util.regex.Pattern; 52 53/** 54 * Service for making GSM and CDMA connections. 55 */ 56public class TelephonyConnectionService extends ConnectionService { 57 58 // If configured, reject attempts to dial numbers matching this pattern. 59 private static final Pattern CDMA_ACTIVATION_CODE_REGEX_PATTERN = 60 Pattern.compile("\\*228[0-9]{0,2}"); 61 62 private final TelephonyConferenceController mTelephonyConferenceController = 63 new TelephonyConferenceController(this); 64 private final CdmaConferenceController mCdmaConferenceController = 65 new CdmaConferenceController(this); 66 private final ImsConferenceController mImsConferenceController = 67 new ImsConferenceController(this); 68 69 private ComponentName mExpectedComponentName = null; 70 private EmergencyCallHelper mEmergencyCallHelper; 71 private EmergencyTonePlayer mEmergencyTonePlayer; 72 73 /** 74 * A listener to actionable events specific to the TelephonyConnection. 75 */ 76 private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener = 77 new TelephonyConnection.TelephonyConnectionListener() { 78 @Override 79 public void onOriginalConnectionConfigured(TelephonyConnection c) { 80 addConnectionToConferenceController(c); 81 } 82 }; 83 84 @Override 85 public void onCreate() { 86 super.onCreate(); 87 mExpectedComponentName = new ComponentName(this, this.getClass()); 88 mEmergencyTonePlayer = new EmergencyTonePlayer(this); 89 TelecomAccountRegistry.getInstance(this).setTelephonyConnectionService(this); 90 } 91 92 @Override 93 public Connection onCreateOutgoingConnection( 94 PhoneAccountHandle connectionManagerPhoneAccount, 95 final ConnectionRequest request) { 96 Log.i(this, "onCreateOutgoingConnection, request: " + request); 97 98 Uri handle = request.getAddress(); 99 if (handle == null) { 100 Log.d(this, "onCreateOutgoingConnection, handle is null"); 101 return Connection.createFailedConnection( 102 DisconnectCauseUtil.toTelecomDisconnectCause( 103 android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED, 104 "No phone number supplied")); 105 } 106 107 String scheme = handle.getScheme(); 108 final String number; 109 if (PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) { 110 // TODO: We don't check for SecurityException here (requires 111 // CALL_PRIVILEGED permission). 112 final Phone phone = getPhoneForAccount(request.getAccountHandle(), false); 113 if (phone == null) { 114 Log.d(this, "onCreateOutgoingConnection, phone is null"); 115 return Connection.createFailedConnection( 116 DisconnectCauseUtil.toTelecomDisconnectCause( 117 android.telephony.DisconnectCause.OUT_OF_SERVICE, 118 "Phone is null")); 119 } 120 number = phone.getVoiceMailNumber(); 121 if (TextUtils.isEmpty(number)) { 122 Log.d(this, "onCreateOutgoingConnection, no voicemail number set."); 123 return Connection.createFailedConnection( 124 DisconnectCauseUtil.toTelecomDisconnectCause( 125 android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING, 126 "Voicemail scheme provided but no voicemail number set.")); 127 } 128 129 // Convert voicemail: to tel: 130 handle = Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null); 131 } else { 132 if (!PhoneAccount.SCHEME_TEL.equals(scheme)) { 133 Log.d(this, "onCreateOutgoingConnection, Handle %s is not type tel", scheme); 134 return Connection.createFailedConnection( 135 DisconnectCauseUtil.toTelecomDisconnectCause( 136 android.telephony.DisconnectCause.INVALID_NUMBER, 137 "Handle scheme is not type tel")); 138 } 139 140 number = handle.getSchemeSpecificPart(); 141 if (TextUtils.isEmpty(number)) { 142 Log.d(this, "onCreateOutgoingConnection, unable to parse number"); 143 return Connection.createFailedConnection( 144 DisconnectCauseUtil.toTelecomDisconnectCause( 145 android.telephony.DisconnectCause.INVALID_NUMBER, 146 "Unable to parse number")); 147 } 148 149 final Phone phone = getPhoneForAccount(request.getAccountHandle(), false); 150 if (phone != null && CDMA_ACTIVATION_CODE_REGEX_PATTERN.matcher(number).matches()) { 151 // Obtain the configuration for the outgoing phone's SIM. If the outgoing number 152 // matches the *228 regex pattern, fail the call. This number is used for OTASP, and 153 // when dialed could lock LTE SIMs to 3G if not prohibited.. 154 boolean disableActivation = false; 155 CarrierConfigManager cfgManager = (CarrierConfigManager) 156 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 157 if (cfgManager != null) { 158 disableActivation = cfgManager.getConfigForSubId(phone.getSubId()) 159 .getBoolean(CarrierConfigManager.KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL); 160 } 161 162 if (disableActivation) { 163 return Connection.createFailedConnection( 164 DisconnectCauseUtil.toTelecomDisconnectCause( 165 android.telephony.DisconnectCause 166 .CDMA_ALREADY_ACTIVATED, 167 "Tried to dial *228")); 168 } 169 } 170 } 171 172 boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(this, number); 173 174 // Get the right phone object from the account data passed in. 175 final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber); 176 if (phone == null) { 177 final Context context = getApplicationContext(); 178 if (context.getResources().getBoolean(R.bool.config_checkSimStateBeforeOutgoingCall)) { 179 // Check SIM card state before the outgoing call. 180 // Start the SIM unlock activity if PIN_REQUIRED. 181 final Phone defaultPhone = PhoneFactory.getDefaultPhone(); 182 final IccCard icc = defaultPhone.getIccCard(); 183 IccCardConstants.State simState = IccCardConstants.State.UNKNOWN; 184 if (icc != null) { 185 simState = icc.getState(); 186 } 187 if (simState == IccCardConstants.State.PIN_REQUIRED) { 188 final String simUnlockUiPackage = context.getResources().getString( 189 R.string.config_simUnlockUiPackage); 190 final String simUnlockUiClass = context.getResources().getString( 191 R.string.config_simUnlockUiClass); 192 if (simUnlockUiPackage != null && simUnlockUiClass != null) { 193 Intent simUnlockIntent = new Intent().setComponent(new ComponentName( 194 simUnlockUiPackage, simUnlockUiClass)); 195 simUnlockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 196 try { 197 context.startActivity(simUnlockIntent); 198 } catch (ActivityNotFoundException exception) { 199 Log.e(this, exception, "Unable to find SIM unlock UI activity."); 200 } 201 } 202 return Connection.createFailedConnection( 203 DisconnectCauseUtil.toTelecomDisconnectCause( 204 android.telephony.DisconnectCause.OUT_OF_SERVICE, 205 "SIM_STATE_PIN_REQUIRED")); 206 } 207 } 208 209 Log.d(this, "onCreateOutgoingConnection, phone is null"); 210 return Connection.createFailedConnection( 211 DisconnectCauseUtil.toTelecomDisconnectCause( 212 android.telephony.DisconnectCause.OUT_OF_SERVICE, "Phone is null")); 213 } 214 215 // Check both voice & data RAT to enable normal CS call, 216 // when voice RAT is OOS but Data RAT is present. 217 int state = phone.getServiceState().getState(); 218 if (state == ServiceState.STATE_OUT_OF_SERVICE) { 219 if (phone.getServiceState().getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE) { 220 state = phone.getServiceState().getDataRegState(); 221 } 222 } 223 boolean useEmergencyCallHelper = false; 224 225 // If we're dialing a non-emergency number and the phone is in ECM mode, reject the call if 226 // carrier configuration specifies that we cannot make non-emergency calls in ECM mode. 227 if (!isEmergencyNumber && phone.isInEcm()) { 228 boolean allowNonEmergencyCalls = true; 229 CarrierConfigManager cfgManager = (CarrierConfigManager) 230 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 231 if (cfgManager != null) { 232 allowNonEmergencyCalls = cfgManager.getConfigForSubId(phone.getSubId()) 233 .getBoolean(CarrierConfigManager.KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL); 234 } 235 236 if (!allowNonEmergencyCalls) { 237 return Connection.createFailedConnection( 238 DisconnectCauseUtil.toTelecomDisconnectCause( 239 DisconnectCause.CDMA_NOT_EMERGENCY, 240 "Cannot make non-emergency call in ECM mode." 241 )); 242 } 243 } 244 245 if (isEmergencyNumber) { 246 if (!phone.isRadioOn()) { 247 useEmergencyCallHelper = true; 248 } 249 } else { 250 switch (state) { 251 case ServiceState.STATE_IN_SERVICE: 252 case ServiceState.STATE_EMERGENCY_ONLY: 253 break; 254 case ServiceState.STATE_OUT_OF_SERVICE: 255 if (phone.isUtEnabled() && number.endsWith("#")) { 256 Log.d(this, "onCreateOutgoingConnection dial for UT"); 257 break; 258 } else { 259 return Connection.createFailedConnection( 260 DisconnectCauseUtil.toTelecomDisconnectCause( 261 android.telephony.DisconnectCause.OUT_OF_SERVICE, 262 "ServiceState.STATE_OUT_OF_SERVICE")); 263 } 264 case ServiceState.STATE_POWER_OFF: 265 return Connection.createFailedConnection( 266 DisconnectCauseUtil.toTelecomDisconnectCause( 267 android.telephony.DisconnectCause.POWER_OFF, 268 "ServiceState.STATE_POWER_OFF")); 269 default: 270 Log.d(this, "onCreateOutgoingConnection, unknown service state: %d", state); 271 return Connection.createFailedConnection( 272 DisconnectCauseUtil.toTelecomDisconnectCause( 273 android.telephony.DisconnectCause.OUTGOING_FAILURE, 274 "Unknown service state " + state)); 275 } 276 } 277 278 final TelephonyConnection connection = 279 createConnectionFor(phone, null, true /* isOutgoing */, request.getAccountHandle(), 280 request.getTelecomCallId()); 281 if (connection == null) { 282 return Connection.createFailedConnection( 283 DisconnectCauseUtil.toTelecomDisconnectCause( 284 android.telephony.DisconnectCause.OUTGOING_FAILURE, 285 "Invalid phone type")); 286 } 287 connection.setAddress(handle, PhoneConstants.PRESENTATION_ALLOWED); 288 connection.setInitializing(); 289 connection.setVideoState(request.getVideoState()); 290 291 if (useEmergencyCallHelper) { 292 if (mEmergencyCallHelper == null) { 293 mEmergencyCallHelper = new EmergencyCallHelper(this); 294 } 295 mEmergencyCallHelper.startTurnOnRadioSequence(phone, 296 new EmergencyCallHelper.Callback() { 297 @Override 298 public void onComplete(boolean isRadioReady) { 299 if (connection.getState() == Connection.STATE_DISCONNECTED) { 300 // If the connection has already been disconnected, do nothing. 301 } else if (isRadioReady) { 302 connection.setInitialized(); 303 placeOutgoingConnection(connection, phone, request); 304 } else { 305 Log.d(this, "onCreateOutgoingConnection, failed to turn on radio"); 306 connection.setDisconnected( 307 DisconnectCauseUtil.toTelecomDisconnectCause( 308 android.telephony.DisconnectCause.POWER_OFF, 309 "Failed to turn on radio.")); 310 connection.destroy(); 311 } 312 } 313 }); 314 315 } else { 316 placeOutgoingConnection(connection, phone, request); 317 } 318 319 return connection; 320 } 321 322 @Override 323 public Connection onCreateIncomingConnection( 324 PhoneAccountHandle connectionManagerPhoneAccount, 325 ConnectionRequest request) { 326 Log.i(this, "onCreateIncomingConnection, request: " + request); 327 328 Phone phone = getPhoneForAccount(request.getAccountHandle(), false); 329 if (phone == null) { 330 return Connection.createFailedConnection( 331 DisconnectCauseUtil.toTelecomDisconnectCause( 332 android.telephony.DisconnectCause.ERROR_UNSPECIFIED, 333 "Phone is null")); 334 } 335 336 Call call = phone.getRingingCall(); 337 if (!call.getState().isRinging()) { 338 Log.i(this, "onCreateIncomingConnection, no ringing call"); 339 return Connection.createFailedConnection( 340 DisconnectCauseUtil.toTelecomDisconnectCause( 341 android.telephony.DisconnectCause.INCOMING_MISSED, 342 "Found no ringing call")); 343 } 344 345 com.android.internal.telephony.Connection originalConnection = 346 call.getState() == Call.State.WAITING ? 347 call.getLatestConnection() : call.getEarliestConnection(); 348 if (isOriginalConnectionKnown(originalConnection)) { 349 Log.i(this, "onCreateIncomingConnection, original connection already registered"); 350 return Connection.createCanceledConnection(); 351 } 352 353 Connection connection = 354 createConnectionFor(phone, originalConnection, false /* isOutgoing */, 355 request.getAccountHandle(), request.getTelecomCallId()); 356 if (connection == null) { 357 return Connection.createCanceledConnection(); 358 } else { 359 return connection; 360 } 361 } 362 363 @Override 364 public void triggerConferenceRecalculate() { 365 if (mTelephonyConferenceController.shouldRecalculate()) { 366 mTelephonyConferenceController.recalculate(); 367 } 368 } 369 370 @Override 371 public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount, 372 ConnectionRequest request) { 373 Log.i(this, "onCreateUnknownConnection, request: " + request); 374 375 Phone phone = getPhoneForAccount(request.getAccountHandle(), false); 376 if (phone == null) { 377 return Connection.createFailedConnection( 378 DisconnectCauseUtil.toTelecomDisconnectCause( 379 android.telephony.DisconnectCause.ERROR_UNSPECIFIED, 380 "Phone is null")); 381 } 382 383 final List<com.android.internal.telephony.Connection> allConnections = new ArrayList<>(); 384 final Call ringingCall = phone.getRingingCall(); 385 if (ringingCall.hasConnections()) { 386 allConnections.addAll(ringingCall.getConnections()); 387 } 388 final Call foregroundCall = phone.getForegroundCall(); 389 if ((foregroundCall.getState() != Call.State.DISCONNECTED) 390 && (foregroundCall.hasConnections())) { 391 allConnections.addAll(foregroundCall.getConnections()); 392 } 393 if (phone.getImsPhone() != null) { 394 final Call imsFgCall = phone.getImsPhone().getForegroundCall(); 395 if ((imsFgCall.getState() != Call.State.DISCONNECTED) && imsFgCall.hasConnections()) { 396 allConnections.addAll(imsFgCall.getConnections()); 397 } 398 } 399 final Call backgroundCall = phone.getBackgroundCall(); 400 if (backgroundCall.hasConnections()) { 401 allConnections.addAll(phone.getBackgroundCall().getConnections()); 402 } 403 404 com.android.internal.telephony.Connection unknownConnection = null; 405 for (com.android.internal.telephony.Connection telephonyConnection : allConnections) { 406 if (!isOriginalConnectionKnown(telephonyConnection)) { 407 unknownConnection = telephonyConnection; 408 Log.d(this, "onCreateUnknownConnection: conn = " + unknownConnection); 409 break; 410 } 411 } 412 413 if (unknownConnection == null) { 414 Log.i(this, "onCreateUnknownConnection, did not find previously unknown connection."); 415 return Connection.createCanceledConnection(); 416 } 417 418 TelephonyConnection connection = 419 createConnectionFor(phone, unknownConnection, 420 !unknownConnection.isIncoming() /* isOutgoing */, 421 request.getAccountHandle(), request.getTelecomCallId()); 422 423 if (connection == null) { 424 return Connection.createCanceledConnection(); 425 } else { 426 connection.updateState(); 427 return connection; 428 } 429 } 430 431 @Override 432 public void onConference(Connection connection1, Connection connection2) { 433 if (connection1 instanceof TelephonyConnection && 434 connection2 instanceof TelephonyConnection) { 435 ((TelephonyConnection) connection1).performConference( 436 (TelephonyConnection) connection2); 437 } 438 439 } 440 441 private void placeOutgoingConnection( 442 TelephonyConnection connection, Phone phone, ConnectionRequest request) { 443 String number = connection.getAddress().getSchemeSpecificPart(); 444 445 com.android.internal.telephony.Connection originalConnection; 446 try { 447 originalConnection = 448 phone.dial(number, null, request.getVideoState(), request.getExtras()); 449 } catch (CallStateException e) { 450 Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e); 451 int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE; 452 if (e.getError() == CallStateException.ERROR_DISCONNECTED) { 453 cause = android.telephony.DisconnectCause.OUT_OF_SERVICE; 454 } 455 connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause( 456 cause, e.getMessage())); 457 return; 458 } 459 460 if (originalConnection == null) { 461 int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE; 462 // On GSM phones, null connection means that we dialed an MMI code 463 if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) { 464 Log.d(this, "dialed MMI code"); 465 telephonyDisconnectCause = android.telephony.DisconnectCause.DIALED_MMI; 466 final Intent intent = new Intent(this, MMIDialogActivity.class); 467 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 468 Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 469 startActivity(intent); 470 } 471 Log.d(this, "placeOutgoingConnection, phone.dial returned null"); 472 connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause( 473 telephonyDisconnectCause, "Connection is null")); 474 } else { 475 connection.setOriginalConnection(originalConnection); 476 } 477 } 478 479 private TelephonyConnection createConnectionFor( 480 Phone phone, 481 com.android.internal.telephony.Connection originalConnection, 482 boolean isOutgoing, 483 PhoneAccountHandle phoneAccountHandle, 484 String telecomCallId) { 485 TelephonyConnection returnConnection = null; 486 int phoneType = phone.getPhoneType(); 487 if (phoneType == TelephonyManager.PHONE_TYPE_GSM) { 488 returnConnection = new GsmConnection(originalConnection, telecomCallId); 489 } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) { 490 boolean allowMute = allowMute(phone); 491 returnConnection = new CdmaConnection( 492 originalConnection, mEmergencyTonePlayer, allowMute, isOutgoing, telecomCallId); 493 } 494 if (returnConnection != null) { 495 // Listen to Telephony specific callbacks from the connection 496 returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener); 497 returnConnection.setVideoPauseSupported( 498 TelecomAccountRegistry.getInstance(this).isVideoPauseSupported( 499 phoneAccountHandle)); 500 returnConnection.setConferenceSupported( 501 TelecomAccountRegistry.getInstance(this).isMergeCallSupported( 502 phoneAccountHandle)); 503 } 504 return returnConnection; 505 } 506 507 private boolean isOriginalConnectionKnown( 508 com.android.internal.telephony.Connection originalConnection) { 509 for (Connection connection : getAllConnections()) { 510 if (connection instanceof TelephonyConnection) { 511 TelephonyConnection telephonyConnection = (TelephonyConnection) connection; 512 if (telephonyConnection.getOriginalConnection() == originalConnection) { 513 return true; 514 } 515 } 516 } 517 return false; 518 } 519 520 private Phone getPhoneForAccount(PhoneAccountHandle accountHandle, boolean isEmergency) { 521 if (isEmergency) { 522 return PhoneFactory.getDefaultPhone(); 523 } 524 525 int subId = PhoneUtils.getSubIdForPhoneAccountHandle(accountHandle); 526 if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 527 int phoneId = SubscriptionController.getInstance().getPhoneId(subId); 528 return PhoneFactory.getPhone(phoneId); 529 } 530 531 return null; 532 } 533 534 private Phone getFirstPhoneForEmergencyCall() { 535 Phone selectPhone = null; 536 for (int i = 0; i < TelephonyManager.getDefault().getSimCount(); i++) { 537 int[] subIds = SubscriptionController.getInstance().getSubIdUsingSlotId(i); 538 if (subIds.length == 0) 539 continue; 540 541 int phoneId = SubscriptionController.getInstance().getPhoneId(subIds[0]); 542 Phone phone = PhoneFactory.getPhone(phoneId); 543 if (phone == null) 544 continue; 545 546 if (ServiceState.STATE_IN_SERVICE == phone.getServiceState().getState()) { 547 // the slot is radio on & state is in service 548 Log.d(this, "pickBestPhoneForEmergencyCall, radio on & in service, slotId:" + i); 549 return phone; 550 } else if (ServiceState.STATE_POWER_OFF != phone.getServiceState().getState()) { 551 // the slot is radio on & with SIM card inserted. 552 if (TelephonyManager.getDefault().hasIccCard(i)) { 553 Log.d(this, "pickBestPhoneForEmergencyCall," + 554 "radio on and SIM card inserted, slotId:" + i); 555 selectPhone = phone; 556 } else if (selectPhone == null) { 557 Log.d(this, "pickBestPhoneForEmergencyCall, radio on, slotId:" + i); 558 selectPhone = phone; 559 } 560 } 561 } 562 563 if (selectPhone == null) { 564 Log.d(this, "pickBestPhoneForEmergencyCall, return default phone"); 565 selectPhone = PhoneFactory.getDefaultPhone(); 566 } 567 568 return selectPhone; 569 } 570 571 /** 572 * Determines if the connection should allow mute. 573 * 574 * @param phone The current phone. 575 * @return {@code True} if the connection should allow mute. 576 */ 577 private boolean allowMute(Phone phone) { 578 // For CDMA phones, check if we are in Emergency Callback Mode (ECM). Mute is disallowed 579 // in ECM mode. 580 if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { 581 if (phone.isInEcm()) { 582 return false; 583 } 584 } 585 586 return true; 587 } 588 589 @Override 590 public void removeConnection(Connection connection) { 591 super.removeConnection(connection); 592 if (connection instanceof TelephonyConnection) { 593 TelephonyConnection telephonyConnection = (TelephonyConnection) connection; 594 telephonyConnection.removeTelephonyConnectionListener(mTelephonyConnectionListener); 595 } 596 } 597 598 /** 599 * When a {@link TelephonyConnection} has its underlying original connection configured, 600 * we need to add it to the correct conference controller. 601 * 602 * @param connection The connection to be added to the controller 603 */ 604 public void addConnectionToConferenceController(TelephonyConnection connection) { 605 // TODO: Do we need to handle the case of the original connection changing 606 // and triggering this callback multiple times for the same connection? 607 // If that is the case, we might want to remove this connection from all 608 // conference controllers first before re-adding it. 609 if (connection.isImsConnection()) { 610 Log.d(this, "Adding IMS connection to conference controller: " + connection); 611 mImsConferenceController.add(connection); 612 } else { 613 int phoneType = connection.getCall().getPhone().getPhoneType(); 614 if (phoneType == TelephonyManager.PHONE_TYPE_GSM) { 615 Log.d(this, "Adding GSM connection to conference controller: " + connection); 616 mTelephonyConferenceController.add(connection); 617 } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA && 618 connection instanceof CdmaConnection) { 619 Log.d(this, "Adding CDMA connection to conference controller: " + connection); 620 mCdmaConferenceController.add((CdmaConnection)connection); 621 } 622 Log.d(this, "Removing connection from IMS conference controller: " + connection); 623 mImsConferenceController.remove(connection); 624 } 625 } 626} 627