ImsPhoneCallTracker.java revision cf91ae7acc62eba22d9e652e5de5fb90a89e2ac8
1/*
2 * Copyright (C) 2013 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.internal.telephony.imsphone;
18
19import java.io.FileDescriptor;
20import java.io.PrintWriter;
21import java.util.ArrayList;
22import java.util.List;
23
24import android.app.PendingIntent;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.SharedPreferences;
30import android.os.AsyncResult;
31import android.os.Handler;
32import android.os.Message;
33import android.os.Registrant;
34import android.os.RegistrantList;
35import android.os.RemoteException;
36import android.os.SystemProperties;
37import android.preference.PreferenceManager;
38import android.telecomm.VideoProfile;
39import android.telephony.DisconnectCause;
40import android.telephony.PhoneNumberUtils;
41import android.telephony.Rlog;
42import android.telephony.ServiceState;
43
44import com.android.ims.ImsCall;
45import com.android.ims.ImsCallProfile;
46import com.android.ims.ImsConfig;
47import com.android.ims.ImsConnectionStateListener;
48import com.android.ims.ImsEcbm;
49import com.android.ims.ImsException;
50import com.android.ims.ImsManager;
51import com.android.ims.ImsReasonInfo;
52import com.android.ims.ImsServiceClass;
53import com.android.ims.ImsUtInterface;
54import com.android.ims.internal.IImsVideoCallProvider;
55import com.android.ims.internal.ImsVideoCallProviderWrapper;
56import com.android.internal.telephony.Call;
57import com.android.internal.telephony.CallStateException;
58import com.android.internal.telephony.CallTracker;
59import com.android.internal.telephony.CommandException;
60import com.android.internal.telephony.CommandsInterface;
61import com.android.internal.telephony.Connection;
62import com.android.internal.telephony.Phone;
63import com.android.internal.telephony.PhoneBase;
64import com.android.internal.telephony.PhoneConstants;
65import com.android.internal.telephony.TelephonyProperties;
66
67/**
68 * {@hide}
69 */
70public final class ImsPhoneCallTracker extends CallTracker {
71    static final String LOG_TAG = "ImsPhoneCallTracker";
72
73    private static final boolean DBG = true;
74
75    private boolean mIsVolteEnabled = false;
76    private boolean mIsVtEnabled = false;
77
78    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
79        @Override
80        public void onReceive(Context context, Intent intent) {
81            if (intent.getAction().equals(ImsManager.ACTION_IMS_INCOMING_CALL)) {
82                if (DBG) log("onReceive : incoming call intent");
83
84                if (mImsManager == null) return;
85
86                if (mServiceId < 0) return;
87
88                try {
89                    // Network initiated USSD will be treated by mImsUssdListener
90                    boolean isUssd = intent.getBooleanExtra(ImsManager.EXTRA_USSD, false);
91                    if (isUssd) {
92                        if (DBG) log("onReceive : USSD");
93                        mUssdSession = mImsManager.takeCall(mServiceId, intent, mImsUssdListener);
94                        if (mUssdSession != null) {
95                            mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE);
96                        }
97                        return;
98                    }
99
100                    // Normal MT call
101                    ImsCall imsCall = mImsManager.takeCall(mServiceId, intent, mImsCallListener);
102
103                    ImsPhoneConnection conn = new ImsPhoneConnection(mPhone.getContext(), imsCall,
104                            ImsPhoneCallTracker.this, mRingingCall);
105                    addConnection(conn);
106
107                    IImsVideoCallProvider imsVideoCallProvider =
108                            imsCall.getCallSession().getVideoCallProvider();
109                    if (imsVideoCallProvider != null) {
110                        ImsVideoCallProviderWrapper imsVideoCallProviderWrapper =
111                                new ImsVideoCallProviderWrapper(imsVideoCallProvider);
112                        conn.setVideoProvider(imsVideoCallProviderWrapper);
113                    }
114
115                    if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE) ||
116                            (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) {
117                        conn.update(imsCall, ImsPhoneCall.State.WAITING);
118                    }
119
120                    mPhone.notifyNewRingingConnection(conn);
121                    mPhone.notifyIncomingRing();
122
123                    updatePhoneState();
124                    mPhone.notifyPreciseCallStateChanged();
125                } catch (ImsException e) {
126                    loge("onReceive : exception " + e);
127                } catch (RemoteException e) {
128                }
129            }
130        }
131    };
132
133    //***** Constants
134
135    static final int MAX_CONNECTIONS = 7;
136    static final int MAX_CONNECTIONS_PER_CALL = 5;
137
138    private static final int EVENT_HANGUP_PENDINGMO = 18;
139    private static final int EVENT_RESUME_BACKGROUND = 19;
140    private static final int EVENT_DIAL_PENDINGMO = 20;
141
142    private static final int TIMEOUT_HANGUP_PENDINGMO = 500;
143
144    //***** Instance Variables
145    private ArrayList<ImsPhoneConnection> mConnections = new ArrayList<ImsPhoneConnection>();
146    private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
147    private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
148
149    ImsPhoneCall mRingingCall = new ImsPhoneCall(this);
150    ImsPhoneCall mForegroundCall = new ImsPhoneCall(this);
151    ImsPhoneCall mBackgroundCall = new ImsPhoneCall(this);
152    ImsPhoneCall mHandoverCall = new ImsPhoneCall(this);
153
154    private ImsPhoneConnection mPendingMO;
155    private int mClirMode = CommandsInterface.CLIR_DEFAULT;
156    private Object mSyncHold = new Object();
157
158    private ImsCall mUssdSession = null;
159    private Message mPendingUssd = null;
160
161    ImsPhone mPhone;
162
163    private boolean mDesiredMute = false;    // false = mute off
164    private boolean mOnHoldToneStarted = false;
165
166    PhoneConstants.State mState = PhoneConstants.State.IDLE;
167
168    private ImsManager mImsManager;
169    private int mServiceId = -1;
170
171    private Call.SrvccState mSrvccState = Call.SrvccState.NONE;
172
173    private boolean mIsInEmergencyCall = false;
174
175    private int pendingCallClirMode;
176    private int pendingCallVideoState;
177    private boolean pendingCallInEcm = false;
178
179    //***** Events
180
181
182    //***** Constructors
183
184    ImsPhoneCallTracker(ImsPhone phone) {
185        this.mPhone = phone;
186
187        IntentFilter intentfilter = new IntentFilter();
188        intentfilter.addAction(ImsManager.ACTION_IMS_INCOMING_CALL);
189        mPhone.getContext().registerReceiver(mReceiver, intentfilter);
190
191        Thread t = new Thread() {
192            public void run() {
193                getImsService();
194            }
195        };
196        t.start();
197    }
198
199    private PendingIntent createIncomingCallPendingIntent() {
200        Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
201        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
202        return PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
203                PendingIntent.FLAG_UPDATE_CURRENT);
204    }
205
206    private void getImsService() {
207        if (DBG) log("getImsService");
208        mImsManager = ImsManager.getInstance(mPhone.getContext(), mPhone.getSubId());
209        try {
210            mServiceId = mImsManager.open(ImsServiceClass.MMTEL,
211                    createIncomingCallPendingIntent(),
212                    mImsConnectionStateListener);
213
214            // Get the ECBM interface and set IMSPhone's listener object for notifications
215            getEcbmInterface().setEcbmStateListener(mPhone.mImsEcbmStateListener);
216            if (mPhone.isInEcm()) {
217                // Call exit ECBM which will invoke onECBMExited
218                mPhone.exitEmergencyCallbackMode();
219            }
220        } catch (ImsException e) {
221            loge("getImsService: " + e);
222            //Leave mImsManager as null, then CallStateException will be thrown when dialing
223            mImsManager = null;
224        }
225    }
226
227    public void dispose() {
228        if (DBG) log("dispose");
229        mRingingCall.dispose();
230        mBackgroundCall.dispose();
231        mForegroundCall.dispose();
232        mHandoverCall.dispose();
233
234        clearDisconnected();
235        mPhone.getContext().unregisterReceiver(mReceiver);
236    }
237
238    @Override
239    protected void finalize() {
240        log("ImsPhoneCallTracker finalized");
241    }
242
243    //***** Instance Methods
244
245    //***** Public Methods
246    @Override
247    public void registerForVoiceCallStarted(Handler h, int what, Object obj) {
248        Registrant r = new Registrant(h, what, obj);
249        mVoiceCallStartedRegistrants.add(r);
250    }
251
252    @Override
253    public void unregisterForVoiceCallStarted(Handler h) {
254        mVoiceCallStartedRegistrants.remove(h);
255    }
256
257    @Override
258    public void registerForVoiceCallEnded(Handler h, int what, Object obj) {
259        Registrant r = new Registrant(h, what, obj);
260        mVoiceCallEndedRegistrants.add(r);
261    }
262
263    @Override
264    public void unregisterForVoiceCallEnded(Handler h) {
265        mVoiceCallEndedRegistrants.remove(h);
266    }
267
268    Connection
269    dial(String dialString, int videoState) throws CallStateException {
270        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
271        int oirMode = sp.getInt(PhoneBase.CLIR_KEY, CommandsInterface.CLIR_DEFAULT);
272        return dial(dialString, oirMode, videoState);
273    }
274
275    /**
276     * oirMode is one of the CLIR_ constants
277     */
278    synchronized Connection
279    dial(String dialString, int clirMode, int videoState) throws CallStateException {
280        boolean isPhoneInEcmMode = SystemProperties.getBoolean(
281                TelephonyProperties.PROPERTY_INECM_MODE, false);
282        boolean isEmergencyNumber = PhoneNumberUtils.isEmergencyNumber(dialString);
283
284        if (DBG) log("dial clirMode=" + clirMode);
285
286        // note that this triggers call state changed notif
287        clearDisconnected();
288
289        if (mImsManager == null) {
290            throw new CallStateException("service not available");
291        }
292
293        if (!canDial()) {
294            throw new CallStateException("cannot dial in current state");
295        }
296
297        if (isPhoneInEcmMode && isEmergencyNumber) {
298            handleEcmTimer(ImsPhone.CANCEL_ECM_TIMER);
299        }
300
301        boolean holdBeforeDial = false;
302
303        // The new call must be assigned to the foreground call.
304        // That call must be idle, so place anything that's
305        // there on hold
306        if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) {
307            if (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE) {
308                //we should have failed in !canDial() above before we get here
309                throw new CallStateException("cannot dial in current state");
310            }
311            // foreground call is empty for the newly dialed connection
312            holdBeforeDial = true;
313            switchWaitingOrHoldingAndActive();
314        }
315
316        ImsPhoneCall.State fgState = ImsPhoneCall.State.IDLE;
317        ImsPhoneCall.State bgState = ImsPhoneCall.State.IDLE;
318
319        mClirMode = clirMode;
320
321        synchronized (mSyncHold) {
322            if (holdBeforeDial) {
323                fgState = mForegroundCall.getState();
324                bgState = mBackgroundCall.getState();
325
326                //holding foreground call failed
327                if (fgState == ImsPhoneCall.State.ACTIVE) {
328                    throw new CallStateException("cannot dial in current state");
329                }
330
331                //holding foreground call succeeded
332                if (bgState == ImsPhoneCall.State.HOLDING) {
333                    holdBeforeDial = false;
334                }
335            }
336
337            mPendingMO = new ImsPhoneConnection(mPhone.getContext(),
338                    checkForTestEmergencyNumber(dialString), this, mForegroundCall);
339        }
340        addConnection(mPendingMO);
341
342        if (!holdBeforeDial) {
343            if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) {
344                dialInternal(mPendingMO, clirMode, videoState);
345            } else {
346                try {
347                    getEcbmInterface().exitEmergencyCallbackMode();
348                } catch (ImsException e) {
349                    e.printStackTrace();
350                    throw new CallStateException("service not available");
351                }
352                mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null);
353                pendingCallClirMode = clirMode;
354                pendingCallVideoState = videoState;
355                pendingCallInEcm = true;
356            }
357        }
358
359        updatePhoneState();
360        mPhone.notifyPreciseCallStateChanged();
361
362        return mPendingMO;
363    }
364
365    private void handleEcmTimer(int action) {
366        mPhone.handleTimerInEmergencyCallbackMode(action);
367        switch (action) {
368            case ImsPhone.CANCEL_ECM_TIMER:
369                break;
370            case ImsPhone.RESTART_ECM_TIMER:
371                break;
372            default:
373                log("handleEcmTimer, unsupported action " + action);
374        }
375    }
376
377    private void dialInternal(ImsPhoneConnection conn, int clirMode, int videoState) {
378        if (conn == null) {
379            return;
380        }
381
382        if (conn.getAddress()== null || conn.getAddress().length() == 0
383                || conn.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) {
384            // Phone number is invalid
385            conn.setDisconnectCause(DisconnectCause.INVALID_NUMBER);
386            sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
387            return;
388        }
389
390        // Always unmute when initiating a new call
391        setMute(false);
392        int serviceType = PhoneNumberUtils.isEmergencyNumber(conn.getAddress()) ?
393                ImsCallProfile.SERVICE_TYPE_EMERGENCY : ImsCallProfile.SERVICE_TYPE_NORMAL;
394        int callType = ImsCallProfile.getCallTypeFromVideoState(videoState);
395        //TODO(vt): Is this sufficient?  At what point do we know the video state of the call?
396        conn.setVideoState(videoState);
397
398        try {
399            String[] callees = new String[] { conn.getAddress() };
400            ImsCallProfile profile = mImsManager.createCallProfile(mServiceId,
401                    serviceType, callType);
402            profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode);
403
404            ImsCall imsCall = mImsManager.makeCall(mServiceId, profile,
405                    callees, mImsCallListener);
406            conn.setImsCall(imsCall);
407
408            IImsVideoCallProvider imsVideoCallProvider =
409                    imsCall.getCallSession().getVideoCallProvider();
410            if (imsVideoCallProvider != null) {
411                ImsVideoCallProviderWrapper imsVideoCallProviderWrapper =
412                        new ImsVideoCallProviderWrapper(imsVideoCallProvider);
413                conn.setVideoProvider(imsVideoCallProviderWrapper);
414            }
415        } catch (ImsException e) {
416            loge("dialInternal : " + e);
417            conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
418            sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
419        } catch (RemoteException e) {
420        }
421    }
422
423    /**
424     * Accepts a call with the specified video state.  The video state is the video state that the
425     * user has agreed upon in the InCall UI.
426     *
427     * @param videoState The video State
428     * @throws CallStateException
429     */
430    void acceptCall (int videoState) throws CallStateException {
431        if (DBG) log("acceptCall");
432
433        if (mForegroundCall.getState().isAlive()
434                && mBackgroundCall.getState().isAlive()) {
435            throw new CallStateException("cannot accept call");
436        }
437
438        if ((mRingingCall.getState() == ImsPhoneCall.State.WAITING)
439                && mForegroundCall.getState().isAlive()) {
440            setMute(false);
441            switchWaitingOrHoldingAndActive();
442        } else if (mRingingCall.getState().isRinging()) {
443            if (DBG) log("acceptCall: incoming...");
444            // Always unmute when answering a new call
445            setMute(false);
446            try {
447                ImsCall imsCall = mRingingCall.getImsCall();
448                if (imsCall != null) {
449                    imsCall.accept(ImsCallProfile.getCallTypeFromVideoState(videoState));
450                } else {
451                    throw new CallStateException("no valid ims call");
452                }
453            } catch (ImsException e) {
454                throw new CallStateException("cannot accept call");
455            }
456        } else {
457            throw new CallStateException("phone not ringing");
458        }
459    }
460
461    void
462    rejectCall () throws CallStateException {
463        if (DBG) log("rejectCall");
464
465        if (mRingingCall.getState().isRinging()) {
466            hangup(mRingingCall);
467        } else {
468            throw new CallStateException("phone not ringing");
469        }
470    }
471
472    void
473    switchWaitingOrHoldingAndActive() throws CallStateException {
474        if (DBG) log("switchWaitingOrHoldingAndActive");
475
476        if (mRingingCall.getState() == ImsPhoneCall.State.INCOMING) {
477            throw new CallStateException("cannot be in the incoming state");
478        }
479
480        if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) {
481            ImsCall imsCall = mForegroundCall.getImsCall();
482            if (imsCall == null) {
483                throw new CallStateException("no ims call");
484            }
485
486            mForegroundCall.switchWith(mBackgroundCall);
487
488            try {
489                imsCall.hold();
490            } catch (ImsException e) {
491                mForegroundCall.switchWith(mBackgroundCall);
492                throw new CallStateException(e.getMessage());
493            }
494        } else if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) {
495            resumeWaitingOrHolding();
496        }
497    }
498
499    void
500    conference() {
501        if (DBG) log("conference");
502
503        ImsCall fgImsCall = mForegroundCall.getImsCall();
504        if (fgImsCall == null) {
505            log("conference no foreground ims call");
506            return;
507        }
508
509        ImsCall bgImsCall = mBackgroundCall.getImsCall();
510        if (bgImsCall == null) {
511            log("conference no background ims call");
512            return;
513        }
514
515        try {
516            fgImsCall.merge(bgImsCall);
517        } catch (ImsException e) {
518            log("conference " + e.getMessage());
519        }
520    }
521
522    void
523    explicitCallTransfer() {
524        //TODO : implement
525    }
526
527    void
528    clearDisconnected() {
529        if (DBG) log("clearDisconnected");
530
531        internalClearDisconnected();
532
533        updatePhoneState();
534        mPhone.notifyPreciseCallStateChanged();
535    }
536
537    boolean
538    canConference() {
539        return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE
540            && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING
541            && !mBackgroundCall.isFull()
542            && !mForegroundCall.isFull();
543    }
544
545    boolean
546    canDial() {
547        boolean ret;
548        int serviceState = mPhone.getServiceState().getState();
549        String disableCall = SystemProperties.get(
550                TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
551
552        ret = (serviceState != ServiceState.STATE_POWER_OFF)
553            && mPendingMO == null
554            && !mRingingCall.isRinging()
555            && !disableCall.equals("true")
556            && (!mForegroundCall.getState().isAlive()
557                    || !mBackgroundCall.getState().isAlive());
558
559        return ret;
560    }
561
562    boolean
563    canTransfer() {
564        return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE
565            && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING;
566    }
567
568    //***** Private Instance Methods
569
570    private void
571    internalClearDisconnected() {
572        mRingingCall.clearDisconnected();
573        mForegroundCall.clearDisconnected();
574        mBackgroundCall.clearDisconnected();
575        mHandoverCall.clearDisconnected();
576    }
577
578    private void
579    updatePhoneState() {
580        PhoneConstants.State oldState = mState;
581
582        if (mRingingCall.isRinging()) {
583            mState = PhoneConstants.State.RINGING;
584        } else if (mPendingMO != null ||
585                !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
586            mState = PhoneConstants.State.OFFHOOK;
587        } else {
588            mState = PhoneConstants.State.IDLE;
589        }
590
591        if (mState == PhoneConstants.State.IDLE && oldState != mState) {
592            mVoiceCallEndedRegistrants.notifyRegistrants(
593                    new AsyncResult(null, null, null));
594        } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
595            mVoiceCallStartedRegistrants.notifyRegistrants (
596                    new AsyncResult(null, null, null));
597        }
598
599        if (DBG) log("updatePhoneState oldState=" + oldState + ", newState=" + mState);
600
601        if (mState != oldState) {
602            mPhone.notifyPhoneStateChanged();
603        }
604    }
605
606    private void
607    handleRadioNotAvailable() {
608        // handlePollCalls will clear out its
609        // call list when it gets the CommandException
610        // error result from this
611        pollCallsWhenSafe();
612    }
613
614    private void
615    dumpState() {
616        List l;
617
618        log("Phone State:" + mState);
619
620        log("Ringing call: " + mRingingCall.toString());
621
622        l = mRingingCall.getConnections();
623        for (int i = 0, s = l.size(); i < s; i++) {
624            log(l.get(i).toString());
625        }
626
627        log("Foreground call: " + mForegroundCall.toString());
628
629        l = mForegroundCall.getConnections();
630        for (int i = 0, s = l.size(); i < s; i++) {
631            log(l.get(i).toString());
632        }
633
634        log("Background call: " + mBackgroundCall.toString());
635
636        l = mBackgroundCall.getConnections();
637        for (int i = 0, s = l.size(); i < s; i++) {
638            log(l.get(i).toString());
639        }
640
641    }
642
643    //***** Called from ImsPhone
644
645    /*package*/ void
646    setMute(boolean mute) {
647        mDesiredMute = mute;
648        mForegroundCall.setMute(mute);
649    }
650
651    /*package*/ boolean
652    getMute() {
653        return mDesiredMute;
654    }
655
656    /*package*/ void
657    sendDtmf(char c) {
658        if (DBG) log("sendDtmf");
659
660        ImsCall imscall = mForegroundCall.getImsCall();
661        if (imscall != null) {
662            imscall.sendDtmf(convertDtmf(c));
663        }
664    }
665
666    private int convertDtmf(char c) {
667        int code = c - '0';
668        if ((code < 0) || (code > 9)) {
669            switch (c) {
670                case '*': return 10;
671                case '#': return 11;
672                case 'A': return 12;
673                case 'B': return 13;
674                case 'C': return 14;
675                case 'D': return 15;
676                default:
677                    throw new IllegalArgumentException(
678                            "invalid DTMF char: " + (int) c);
679            }
680        }
681        return code;
682    }
683
684    //***** Called from ImsPhoneConnection
685
686    /*package*/ void
687    hangup (ImsPhoneConnection conn) throws CallStateException {
688        if (DBG) log("hangup connection");
689
690        if (conn.getOwner() != this) {
691            throw new CallStateException ("ImsPhoneConnection " + conn
692                    + "does not belong to ImsPhoneCallTracker " + this);
693        }
694
695        hangup(conn.getCall());
696    }
697
698    //***** Called from ImsPhoneCall
699
700    /* package */ void
701    hangup (ImsPhoneCall call) throws CallStateException {
702        if (DBG) log("hangup call");
703
704        if (call.getConnections().size() == 0) {
705            throw new CallStateException("no connections");
706        }
707
708        ImsCall imsCall = call.getImsCall();
709        boolean rejectCall = false;
710
711        if (call == mRingingCall) {
712            if (Phone.DEBUG_PHONE) log("(ringing) hangup incoming");
713            rejectCall = true;
714        } else if (call == mForegroundCall) {
715            if (call.isDialingOrAlerting()) {
716                if (Phone.DEBUG_PHONE) {
717                    log("(foregnd) hangup dialing or alerting...");
718                }
719            } else {
720                if (Phone.DEBUG_PHONE) {
721                    log("(foregnd) hangup foreground");
722                }
723                //held call will be resumed by onCallTerminated
724            }
725        } else if (call == mBackgroundCall) {
726            if (Phone.DEBUG_PHONE) {
727                log("(backgnd) hangup waiting or background");
728            }
729        } else {
730            throw new RuntimeException ("ImsPhoneCall " + call +
731                    "does not belong to ImsPhoneCallTracker " + this);
732        }
733
734        call.onHangupLocal();
735
736        try {
737            if (imsCall != null) {
738                if (rejectCall) imsCall.reject(ImsReasonInfo.CODE_USER_DECLINE);
739                else imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
740            } else if (mPendingMO != null && call == mForegroundCall) {
741                // is holding a foreground call
742                mPendingMO.update(null, ImsPhoneCall.State.DISCONNECTED);
743                mPendingMO.onDisconnect();
744                removeConnection(mPendingMO);
745                mPendingMO = null;
746                updatePhoneState();
747                removeMessages(EVENT_DIAL_PENDINGMO);
748            }
749        } catch (ImsException e) {
750            throw new CallStateException(e.getMessage());
751        }
752
753        mPhone.notifyPreciseCallStateChanged();
754    }
755
756    /* package */
757    void resumeWaitingOrHolding() throws CallStateException {
758        if (DBG) log("resumeWaitingOrHolding");
759
760        try {
761            if (mForegroundCall.getState().isAlive()) {
762                //resume foreground call after holding background call
763                //they were switched before holding
764                ImsCall imsCall = mForegroundCall.getImsCall();
765                if (imsCall != null) imsCall.resume();
766            } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING) {
767                //accept waiting call after holding background call
768                ImsCall imsCall = mRingingCall.getImsCall();
769                if (imsCall != null) imsCall.accept(ImsCallProfile.CALL_TYPE_VOICE);
770            } else {
771                //Just resume background call.
772                //To distinguish resuming call with swapping calls
773                //we do not switch calls.here
774                //ImsPhoneConnection.update will chnage the parent when completed
775                ImsCall imsCall = mBackgroundCall.getImsCall();
776                if (imsCall != null) imsCall.resume();
777            }
778        } catch (ImsException e) {
779            throw new CallStateException(e.getMessage());
780        }
781    }
782
783    /* package */
784    void sendUSSD (String ussdString, Message response) {
785        if (DBG) log("sendUSSD");
786
787        try {
788            if (mUssdSession != null) {
789                mUssdSession.sendUssd(ussdString);
790                AsyncResult.forMessage(response, null, null);
791                response.sendToTarget();
792                return;
793            }
794
795            String[] callees = new String[] { ussdString };
796            ImsCallProfile profile = mImsManager.createCallProfile(mServiceId,
797                    ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.CALL_TYPE_VOICE);
798            profile.setCallExtraInt(ImsCallProfile.EXTRA_DIALSTRING,
799                    ImsCallProfile.DIALSTRING_USSD);
800
801            mUssdSession = mImsManager.makeCall(mServiceId, profile,
802                    callees, mImsUssdListener);
803        } catch (ImsException e) {
804            loge("sendUSSD : " + e);
805            mPhone.sendErrorResponse(response, e);
806        }
807    }
808
809    /* package */
810    void cancelUSSD() {
811        if (mUssdSession == null) return;
812
813        try {
814            mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
815        } catch (ImsException e) {
816        }
817
818    }
819
820    private synchronized ImsPhoneConnection findConnection(ImsCall imsCall) {
821        for (ImsPhoneConnection conn : mConnections) {
822            if (conn.getImsCall() == imsCall) {
823                return conn;
824            }
825        }
826        return null;
827    }
828
829    private synchronized void removeConnection(ImsPhoneConnection conn) {
830        mConnections.remove(conn);
831    }
832
833    private synchronized void addConnection(ImsPhoneConnection conn) {
834        mConnections.add(conn);
835    }
836
837    private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) {
838        if (DBG) log("processCallStateChange state=" + state + " cause=" + cause);
839
840        if (imsCall == null) return;
841
842        boolean changed = false;
843        ImsPhoneConnection conn = findConnection(imsCall);
844
845        if (conn == null) {
846            // TODO : what should be done?
847            return;
848        }
849
850        changed = conn.update(imsCall, state);
851
852        if (state == ImsPhoneCall.State.DISCONNECTED) {
853            changed = conn.onDisconnect(cause) || changed;
854            removeConnection(conn);
855        }
856
857        if (changed) {
858            if (conn.getCall() == mHandoverCall) return;
859            updatePhoneState();
860            mPhone.notifyPreciseCallStateChanged();
861        }
862    }
863
864    private int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) {
865        int cause = DisconnectCause.ERROR_UNSPECIFIED;
866
867        //int type = reasonInfo.getReasonType();
868        int code = reasonInfo.getCode();
869        switch (code) {
870            case ImsReasonInfo.CODE_SIP_BAD_ADDRESS:
871            case ImsReasonInfo.CODE_SIP_NOT_REACHABLE:
872                return DisconnectCause.NUMBER_UNREACHABLE;
873
874            case ImsReasonInfo.CODE_SIP_BUSY:
875                return DisconnectCause.BUSY;
876
877            case ImsReasonInfo.CODE_USER_TERMINATED:
878                return DisconnectCause.LOCAL;
879
880            case ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE:
881                return DisconnectCause.NORMAL;
882
883            case ImsReasonInfo.CODE_SIP_REDIRECTED:
884            case ImsReasonInfo.CODE_SIP_BAD_REQUEST:
885            case ImsReasonInfo.CODE_SIP_FORBIDDEN:
886            case ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE:
887            case ImsReasonInfo.CODE_SIP_USER_REJECTED:
888            case ImsReasonInfo.CODE_SIP_GLOBAL_ERROR:
889                return DisconnectCause.SERVER_ERROR;
890
891            case ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE:
892            case ImsReasonInfo.CODE_SIP_NOT_FOUND:
893            case ImsReasonInfo.CODE_SIP_SERVER_ERROR:
894                return DisconnectCause.SERVER_UNREACHABLE;
895
896            case ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING:
897            case ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED:
898            case ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN:
899            case ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE:
900            case ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED:
901            case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE:
902            case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE:
903            case ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING:
904                return DisconnectCause.OUT_OF_SERVICE;
905
906            case ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT:
907            case ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING:
908            case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER:
909            case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE:
910                return DisconnectCause.TIMED_OUT;
911
912            case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY:
913            case ImsReasonInfo.CODE_LOCAL_POWER_OFF:
914                return DisconnectCause.POWER_OFF;
915
916            default:
917        }
918
919        return cause;
920    }
921
922    /**
923     * Listen to the IMS call state change
924     */
925    private ImsCall.Listener mImsCallListener = new ImsCall.Listener() {
926        @Override
927        public void onCallProgressing(ImsCall imsCall) {
928            if (DBG) log("onCallProgressing");
929
930            mPendingMO = null;
931            processCallStateChange(imsCall, ImsPhoneCall.State.ALERTING,
932                    DisconnectCause.NOT_DISCONNECTED);
933        }
934
935        @Override
936        public void onCallStarted(ImsCall imsCall) {
937            if (DBG) log("onCallStarted");
938
939            mPendingMO = null;
940            processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE,
941                    DisconnectCause.NOT_DISCONNECTED);
942        }
943
944        /**
945         * onCallStartFailed will be invoked when:
946         * case 1) Dialing fails
947         * case 2) Ringing call is disconnected by local or remote user
948         */
949        @Override
950        public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
951            if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode());
952
953            if (mPendingMO != null) {
954                // To initiate dialing circuit-switched call
955                if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
956                        && mBackgroundCall.getState() == ImsPhoneCall.State.IDLE
957                        && mRingingCall.getState() == ImsPhoneCall.State.IDLE) {
958                    mForegroundCall.detach(mPendingMO);
959                    removeConnection(mPendingMO);
960                    mPendingMO.finalize();
961                    mPendingMO = null;
962                    mPhone.initiateSilentRedial();
963                    return;
964                }
965                mPendingMO = null;
966            }
967            onCallTerminated(imsCall, reasonInfo);
968        }
969
970        @Override
971        public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
972            if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode());
973
974            ImsPhoneCall.State oldState = mForegroundCall.getState();
975
976            processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED,
977                    getDisconnectCauseFromReasonInfo(reasonInfo));
978
979            if (reasonInfo.getCode() == ImsReasonInfo.CODE_USER_TERMINATED) {
980                if ((oldState == ImsPhoneCall.State.DISCONNECTING)
981                        && (mForegroundCall.getState() == ImsPhoneCall.State.DISCONNECTED)
982                        && (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING)) {
983                    sendEmptyMessage(EVENT_RESUME_BACKGROUND);
984                }
985            }
986        }
987
988        @Override
989        public void onCallHeld(ImsCall imsCall) {
990            if (DBG) log("onCallHeld");
991
992            synchronized (mSyncHold) {
993                ImsPhoneCall.State oldState = mBackgroundCall.getState();
994                processCallStateChange(imsCall, ImsPhoneCall.State.HOLDING,
995                        DisconnectCause.NOT_DISCONNECTED);
996
997                if (oldState == ImsPhoneCall.State.ACTIVE) {
998                    if ((mForegroundCall.getState() == ImsPhoneCall.State.HOLDING)
999                            || (mRingingCall.getState() == ImsPhoneCall.State.WAITING)) {
1000                        sendEmptyMessage(EVENT_RESUME_BACKGROUND);
1001                    } else {
1002                        //when multiple connections belong to background call,
1003                        //only the first callback reaches here
1004                        //otherwise the oldState is already HOLDING
1005                        if (mPendingMO != null) {
1006                            sendEmptyMessage(EVENT_DIAL_PENDINGMO);
1007                        }
1008                    }
1009                }
1010            }
1011        }
1012
1013        @Override
1014        public void onCallHoldFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
1015            if (DBG) log("onCallHoldFailed reasonCode=" + reasonInfo.getCode());
1016
1017            synchronized (mSyncHold) {
1018                ImsPhoneCall.State bgState = mBackgroundCall.getState();
1019                if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED) {
1020                    // disconnected while processing hold
1021                    if (mPendingMO != null) {
1022                        sendEmptyMessage(EVENT_DIAL_PENDINGMO);
1023                    }
1024                } else if (bgState == ImsPhoneCall.State.ACTIVE) {
1025                    mForegroundCall.switchWith(mBackgroundCall);
1026
1027                    if (mPendingMO != null) {
1028                        mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
1029                        sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
1030                    }
1031                }
1032            }
1033        }
1034
1035        @Override
1036        public void onCallResumed(ImsCall imsCall) {
1037            if (DBG) log("onCallResumed");
1038
1039            processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE,
1040                    DisconnectCause.NOT_DISCONNECTED);
1041        }
1042
1043        @Override
1044        public void onCallResumeFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
1045            // TODO : What should be done?
1046        }
1047
1048        @Override
1049        public void onCallResumeReceived(ImsCall imsCall) {
1050            if (DBG) log("onCallResumeReceived");
1051
1052            if (mOnHoldToneStarted) {
1053                mPhone.stopOnHoldTone();
1054                mOnHoldToneStarted = false;
1055            }
1056        }
1057
1058        @Override
1059        public void onCallHoldReceived(ImsCall imsCall) {
1060            if (DBG) log("onCallHoldReceived");
1061
1062            ImsPhoneConnection conn = findConnection(imsCall);
1063            if (conn != null && conn.getState() == ImsPhoneCall.State.ACTIVE) {
1064                if (!mOnHoldToneStarted && ImsPhoneCall.isLocalTone(imsCall)) {
1065                    mPhone.startOnHoldTone();
1066                    mOnHoldToneStarted = true;
1067                }
1068            }
1069        }
1070
1071        @Override
1072        public void onCallMerged(ImsCall call, ImsCall newCall) {
1073            if (DBG) log("onCallMerged");
1074
1075            mForegroundCall.merge(mBackgroundCall, mForegroundCall.getState());
1076            updatePhoneState();
1077            mPhone.notifyPreciseCallStateChanged();
1078        }
1079
1080        @Override
1081        public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) {
1082            if (DBG) log("onCallMergeFailed reasonCode=" + reasonInfo.getCode());
1083            mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE);
1084        }
1085    };
1086
1087    /**
1088     * Listen to the IMS call state change
1089     */
1090    private ImsCall.Listener mImsUssdListener = new ImsCall.Listener() {
1091        @Override
1092        public void onCallStarted(ImsCall imsCall) {
1093            if (DBG) log("mImsUssdListener onCallStarted");
1094
1095            if (imsCall == mUssdSession) {
1096                if (mPendingUssd != null) {
1097                    AsyncResult.forMessage(mPendingUssd);
1098                    mPendingUssd.sendToTarget();
1099                    mPendingUssd = null;
1100                }
1101            }
1102        }
1103
1104        @Override
1105        public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
1106            if (DBG) log("mImsUssdListener onCallStartFailed reasonCode=" + reasonInfo.getCode());
1107
1108            onCallTerminated(imsCall, reasonInfo);
1109        }
1110
1111        @Override
1112        public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
1113            if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode());
1114
1115            if (imsCall == mUssdSession) {
1116                mUssdSession = null;
1117                if (mPendingUssd != null) {
1118                    CommandException ex =
1119                            new CommandException(CommandException.Error.GENERIC_FAILURE);
1120                    AsyncResult.forMessage(mPendingUssd, null, ex);
1121                    mPendingUssd.sendToTarget();
1122                    mPendingUssd = null;
1123                }
1124            }
1125            imsCall.close();
1126        }
1127
1128        @Override
1129        public void onCallUssdMessageReceived(ImsCall call,
1130                int mode, String ussdMessage) {
1131            if (DBG) log("mImsUssdListener onCallUssdMessageReceived mode=" + mode);
1132
1133            int ussdMode = -1;
1134
1135            switch(mode) {
1136                case ImsCall.USSD_MODE_REQUEST:
1137                    ussdMode = CommandsInterface.USSD_MODE_REQUEST;
1138                    break;
1139
1140                case ImsCall.USSD_MODE_NOTIFY:
1141                    ussdMode = CommandsInterface.USSD_MODE_NOTIFY;
1142                    break;
1143            }
1144
1145            mPhone.onIncomingUSSD(ussdMode, ussdMessage);
1146        }
1147    };
1148
1149    /**
1150     * Listen to the IMS service state change
1151     *
1152     */
1153    private ImsConnectionStateListener mImsConnectionStateListener =
1154        new ImsConnectionStateListener() {
1155        @Override
1156        public void onImsConnected() {
1157            if (DBG) log("onImsConnected");
1158            mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
1159        }
1160
1161        @Override
1162        public void onImsDisconnected() {
1163            if (DBG) log("onImsDisconnected");
1164            mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
1165        }
1166
1167        @Override
1168        public void onImsResumed() {
1169            if (DBG) log("onImsResumed");
1170            mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
1171        }
1172
1173        @Override
1174        public void onImsSuspended() {
1175            if (DBG) log("onImsSuspended");
1176            mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
1177        }
1178
1179        @Override
1180        public void onFeatureCapabilityChanged(int serviceClass,
1181                int[] enabledFeatures, int[] disabledFeatures) {
1182            if (serviceClass == ImsServiceClass.MMTEL) {
1183                if (enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] ==
1184                        ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE) {
1185                    mIsVolteEnabled = true;
1186                }
1187                if (enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] ==
1188                        ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE) {
1189                    mIsVtEnabled = true;
1190                }
1191                if (disabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] ==
1192                        ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE) {
1193                    mIsVolteEnabled = false;
1194                }
1195                if (disabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] ==
1196                        ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE) {
1197                    mIsVtEnabled = false;
1198                }
1199            }
1200            if (DBG) log("onFeatureCapabilityChanged, mIsVolteEnabled = " +  mIsVolteEnabled
1201                    + " mIsVtEnabled = " + mIsVtEnabled);
1202        }
1203    };
1204
1205    /* package */
1206    ImsUtInterface getUtInterface() throws ImsException {
1207        if (mImsManager == null) {
1208            throw new ImsException("no ims manager", ImsReasonInfo.CODE_UNSPECIFIED);
1209        }
1210
1211        ImsUtInterface ut = mImsManager.getSupplementaryServiceConfiguration(mServiceId);
1212        return ut;
1213    }
1214
1215    /* package */
1216    void notifySrvccState(Call.SrvccState state) {
1217        if (DBG) log("notifySrvccState state=" + state);
1218
1219        mSrvccState = state;
1220
1221        if (mSrvccState == Call.SrvccState.COMPLETED) {
1222            if (mForegroundCall.getConnections().size() > 0) {
1223                mHandoverCall.switchWith(mForegroundCall);
1224            } else if (mBackgroundCall.getConnections().size() > 0) {
1225                mHandoverCall.switchWith(mBackgroundCall);
1226            }
1227        }
1228    }
1229
1230    //****** Overridden from Handler
1231
1232    @Override
1233    public void
1234    handleMessage (Message msg) {
1235        AsyncResult ar;
1236        if (DBG) log("handleMessage what=" + msg.what);
1237
1238        switch (msg.what) {
1239            case EVENT_HANGUP_PENDINGMO:
1240                if (mPendingMO != null) {
1241                    mPendingMO.onDisconnect();
1242                    removeConnection(mPendingMO);
1243                    mPendingMO = null;
1244                }
1245
1246                updatePhoneState();
1247                mPhone.notifyPreciseCallStateChanged();
1248                break;
1249            case EVENT_RESUME_BACKGROUND:
1250                try {
1251                    resumeWaitingOrHolding();
1252                } catch (CallStateException e) {
1253                    if (Phone.DEBUG_PHONE) {
1254                        loge("handleMessage EVENT_RESUME_BACKGROUND exception=" + e);
1255                    }
1256                }
1257                break;
1258            case EVENT_DIAL_PENDINGMO:
1259                dialInternal(mPendingMO, mClirMode, VideoProfile.VideoState.AUDIO_ONLY);
1260                break;
1261
1262            case EVENT_EXIT_ECM_RESPONSE_CDMA:
1263                // no matter the result, we still do the same here
1264                if (pendingCallInEcm) {
1265                    dialInternal(mPendingMO, pendingCallClirMode, pendingCallVideoState);
1266                    pendingCallInEcm = false;
1267                }
1268                mPhone.unsetOnEcbModeExitResponse(this);
1269                break;
1270        }
1271    }
1272
1273    @Override
1274    protected void log(String msg) {
1275        Rlog.d(LOG_TAG, "[ImsPhoneCallTracker] " + msg);
1276    }
1277
1278    protected void loge(String msg) {
1279        Rlog.e(LOG_TAG, "[ImsPhoneCallTracker] " + msg);
1280    }
1281
1282    @Override
1283    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1284        pw.println("ImsPhoneCallTracker extends:");
1285        super.dump(fd, pw, args);
1286        pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants);
1287        pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants);
1288        pw.println(" mRingingCall=" + mRingingCall);
1289        pw.println(" mForegroundCall=" + mForegroundCall);
1290        pw.println(" mBackgroundCall=" + mBackgroundCall);
1291        pw.println(" mHandoverCall=" + mHandoverCall);
1292        pw.println(" mPendingMO=" + mPendingMO);
1293        //pw.println(" mHangupPendingMO=" + mHangupPendingMO);
1294        pw.println(" mPhone=" + mPhone);
1295        pw.println(" mDesiredMute=" + mDesiredMute);
1296        pw.println(" mState=" + mState);
1297    }
1298
1299    @Override
1300    protected void handlePollCalls(AsyncResult ar) {
1301    }
1302
1303    /* package */
1304    ImsEcbm getEcbmInterface() throws ImsException {
1305        if (mImsManager == null) {
1306            throw new ImsException("no ims manager", ImsReasonInfo.CODE_UNSPECIFIED);
1307        }
1308
1309        ImsEcbm ecbm = mImsManager.getEcbmInterface(mServiceId);
1310        return ecbm;
1311    }
1312
1313    public boolean isInEmergencyCall() {
1314        return mIsInEmergencyCall;
1315    }
1316
1317    public boolean isVolteEnabled() {
1318        return mIsVolteEnabled;
1319    }
1320
1321    public boolean isVtEnabled() {
1322        return mIsVtEnabled;
1323    }
1324}
1325