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