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