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