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