Home | History | Annotate | Download | only in imsphone
      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 
     17 package com.android.internal.telephony.imsphone;
     18 
     19 import java.io.FileDescriptor;
     20 import java.io.PrintWriter;
     21 import java.util.ArrayList;
     22 import java.util.List;
     23 
     24 import android.app.PendingIntent;
     25 import android.content.BroadcastReceiver;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.SharedPreferences;
     30 import android.os.AsyncResult;
     31 import android.os.Handler;
     32 import android.os.Message;
     33 import android.os.Registrant;
     34 import android.os.RegistrantList;
     35 import android.os.RemoteException;
     36 import android.os.SystemProperties;
     37 import android.preference.PreferenceManager;
     38 import android.telecom.VideoProfile;
     39 import android.telephony.DisconnectCause;
     40 import android.telephony.PhoneNumberUtils;
     41 import android.telephony.Rlog;
     42 import android.telephony.ServiceState;
     43 
     44 import com.android.ims.ImsCall;
     45 import com.android.ims.ImsCallProfile;
     46 import com.android.ims.ImsConfig;
     47 import com.android.ims.ImsConnectionStateListener;
     48 import com.android.ims.ImsEcbm;
     49 import com.android.ims.ImsException;
     50 import com.android.ims.ImsManager;
     51 import com.android.ims.ImsReasonInfo;
     52 import com.android.ims.ImsServiceClass;
     53 import com.android.ims.ImsUtInterface;
     54 import com.android.ims.internal.IImsVideoCallProvider;
     55 import com.android.ims.internal.ImsVideoCallProviderWrapper;
     56 import com.android.internal.telephony.Call;
     57 import com.android.internal.telephony.CallStateException;
     58 import com.android.internal.telephony.CallTracker;
     59 import com.android.internal.telephony.CommandException;
     60 import com.android.internal.telephony.CommandsInterface;
     61 import com.android.internal.telephony.Connection;
     62 import com.android.internal.telephony.Phone;
     63 import com.android.internal.telephony.PhoneBase;
     64 import com.android.internal.telephony.PhoneConstants;
     65 import com.android.internal.telephony.TelephonyProperties;
     66 
     67 /**
     68  * {@hide}
     69  */
     70 public 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(c);
    663         }
    664     }
    665 
    666     //***** Called from ImsPhoneConnection
    667 
    668     /*package*/ void
    669     hangup (ImsPhoneConnection conn) throws CallStateException {
    670         if (DBG) log("hangup connection");
    671 
    672         if (conn.getOwner() != this) {
    673             throw new CallStateException ("ImsPhoneConnection " + conn
    674                     + "does not belong to ImsPhoneCallTracker " + this);
    675         }
    676 
    677         hangup(conn.getCall());
    678     }
    679 
    680     //***** Called from ImsPhoneCall
    681 
    682     /* package */ void
    683     hangup (ImsPhoneCall call) throws CallStateException {
    684         if (DBG) log("hangup call");
    685 
    686         if (call.getConnections().size() == 0) {
    687             throw new CallStateException("no connections");
    688         }
    689 
    690         ImsCall imsCall = call.getImsCall();
    691         boolean rejectCall = false;
    692 
    693         if (call == mRingingCall) {
    694             if (Phone.DEBUG_PHONE) log("(ringing) hangup incoming");
    695             rejectCall = true;
    696         } else if (call == mForegroundCall) {
    697             if (call.isDialingOrAlerting()) {
    698                 if (Phone.DEBUG_PHONE) {
    699                     log("(foregnd) hangup dialing or alerting...");
    700                 }
    701             } else {
    702                 if (Phone.DEBUG_PHONE) {
    703                     log("(foregnd) hangup foreground");
    704                 }
    705                 //held call will be resumed by onCallTerminated
    706             }
    707         } else if (call == mBackgroundCall) {
    708             if (Phone.DEBUG_PHONE) {
    709                 log("(backgnd) hangup waiting or background");
    710             }
    711         } else {
    712             throw new CallStateException ("ImsPhoneCall " + call +
    713                     "does not belong to ImsPhoneCallTracker " + this);
    714         }
    715 
    716         call.onHangupLocal();
    717 
    718         try {
    719             if (imsCall != null) {
    720                 if (rejectCall) imsCall.reject(ImsReasonInfo.CODE_USER_DECLINE);
    721                 else imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
    722             } else if (mPendingMO != null && call == mForegroundCall) {
    723                 // is holding a foreground call
    724                 mPendingMO.update(null, ImsPhoneCall.State.DISCONNECTED);
    725                 mPendingMO.onDisconnect();
    726                 removeConnection(mPendingMO);
    727                 mPendingMO = null;
    728                 updatePhoneState();
    729                 removeMessages(EVENT_DIAL_PENDINGMO);
    730             }
    731         } catch (ImsException e) {
    732             throw new CallStateException(e.getMessage());
    733         }
    734 
    735         mPhone.notifyPreciseCallStateChanged();
    736     }
    737 
    738     /* package */
    739     void resumeWaitingOrHolding() throws CallStateException {
    740         if (DBG) log("resumeWaitingOrHolding");
    741 
    742         try {
    743             if (mForegroundCall.getState().isAlive()) {
    744                 //resume foreground call after holding background call
    745                 //they were switched before holding
    746                 ImsCall imsCall = mForegroundCall.getImsCall();
    747                 if (imsCall != null) imsCall.resume();
    748             } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING) {
    749                 //accept waiting call after holding background call
    750                 ImsCall imsCall = mRingingCall.getImsCall();
    751                 if (imsCall != null) imsCall.accept(ImsCallProfile.CALL_TYPE_VOICE);
    752             } else {
    753                 //Just resume background call.
    754                 //To distinguish resuming call with swapping calls
    755                 //we do not switch calls.here
    756                 //ImsPhoneConnection.update will chnage the parent when completed
    757                 ImsCall imsCall = mBackgroundCall.getImsCall();
    758                 if (imsCall != null) imsCall.resume();
    759             }
    760         } catch (ImsException e) {
    761             throw new CallStateException(e.getMessage());
    762         }
    763     }
    764 
    765     /* package */
    766     void sendUSSD (String ussdString, Message response) {
    767         if (DBG) log("sendUSSD");
    768 
    769         try {
    770             if (mUssdSession != null) {
    771                 mUssdSession.sendUssd(ussdString);
    772                 AsyncResult.forMessage(response, null, null);
    773                 response.sendToTarget();
    774                 return;
    775             }
    776 
    777             String[] callees = new String[] { ussdString };
    778             ImsCallProfile profile = mImsManager.createCallProfile(mServiceId,
    779                     ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.CALL_TYPE_VOICE);
    780             profile.setCallExtraInt(ImsCallProfile.EXTRA_DIALSTRING,
    781                     ImsCallProfile.DIALSTRING_USSD);
    782 
    783             mUssdSession = mImsManager.makeCall(mServiceId, profile,
    784                     callees, mImsUssdListener);
    785         } catch (ImsException e) {
    786             loge("sendUSSD : " + e);
    787             mPhone.sendErrorResponse(response, e);
    788         }
    789     }
    790 
    791     /* package */
    792     void cancelUSSD() {
    793         if (mUssdSession == null) return;
    794 
    795         try {
    796             mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
    797         } catch (ImsException e) {
    798         }
    799 
    800     }
    801 
    802     private synchronized ImsPhoneConnection findConnection(ImsCall imsCall) {
    803         for (ImsPhoneConnection conn : mConnections) {
    804             if (conn.getImsCall() == imsCall) {
    805                 return conn;
    806             }
    807         }
    808         return null;
    809     }
    810 
    811     private synchronized void removeConnection(ImsPhoneConnection conn) {
    812         mConnections.remove(conn);
    813     }
    814 
    815     private synchronized void addConnection(ImsPhoneConnection conn) {
    816         mConnections.add(conn);
    817     }
    818 
    819     private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) {
    820         if (DBG) log("processCallStateChange state=" + state + " cause=" + cause);
    821 
    822         if (imsCall == null) return;
    823 
    824         boolean changed = false;
    825         ImsPhoneConnection conn = findConnection(imsCall);
    826 
    827         if (conn == null) {
    828             // TODO : what should be done?
    829             return;
    830         }
    831 
    832         changed = conn.update(imsCall, state);
    833 
    834         if (state == ImsPhoneCall.State.DISCONNECTED) {
    835             changed = conn.onDisconnect(cause) || changed;
    836             removeConnection(conn);
    837         }
    838 
    839         if (changed) {
    840             if (conn.getCall() == mHandoverCall) return;
    841             updatePhoneState();
    842             mPhone.notifyPreciseCallStateChanged();
    843         }
    844     }
    845 
    846     private int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) {
    847         int cause = DisconnectCause.ERROR_UNSPECIFIED;
    848 
    849         //int type = reasonInfo.getReasonType();
    850         int code = reasonInfo.getCode();
    851         switch (code) {
    852             case ImsReasonInfo.CODE_SIP_BAD_ADDRESS:
    853             case ImsReasonInfo.CODE_SIP_NOT_REACHABLE:
    854                 return DisconnectCause.NUMBER_UNREACHABLE;
    855 
    856             case ImsReasonInfo.CODE_SIP_BUSY:
    857                 return DisconnectCause.BUSY;
    858 
    859             case ImsReasonInfo.CODE_USER_TERMINATED:
    860                 return DisconnectCause.LOCAL;
    861 
    862             case ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE:
    863                 return DisconnectCause.NORMAL;
    864 
    865             case ImsReasonInfo.CODE_SIP_REDIRECTED:
    866             case ImsReasonInfo.CODE_SIP_BAD_REQUEST:
    867             case ImsReasonInfo.CODE_SIP_FORBIDDEN:
    868             case ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE:
    869             case ImsReasonInfo.CODE_SIP_USER_REJECTED:
    870             case ImsReasonInfo.CODE_SIP_GLOBAL_ERROR:
    871                 return DisconnectCause.SERVER_ERROR;
    872 
    873             case ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE:
    874             case ImsReasonInfo.CODE_SIP_NOT_FOUND:
    875             case ImsReasonInfo.CODE_SIP_SERVER_ERROR:
    876                 return DisconnectCause.SERVER_UNREACHABLE;
    877 
    878             case ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING:
    879             case ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED:
    880             case ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN:
    881             case ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE:
    882             case ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED:
    883             case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE:
    884             case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE:
    885             case ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING:
    886                 return DisconnectCause.OUT_OF_SERVICE;
    887 
    888             case ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT:
    889             case ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING:
    890             case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER:
    891             case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE:
    892                 return DisconnectCause.TIMED_OUT;
    893 
    894             case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY:
    895             case ImsReasonInfo.CODE_LOCAL_POWER_OFF:
    896                 return DisconnectCause.POWER_OFF;
    897 
    898             default:
    899         }
    900 
    901         return cause;
    902     }
    903 
    904     /**
    905      * Listen to the IMS call state change
    906      */
    907     private ImsCall.Listener mImsCallListener = new ImsCall.Listener() {
    908         @Override
    909         public void onCallProgressing(ImsCall imsCall) {
    910             if (DBG) log("onCallProgressing");
    911 
    912             mPendingMO = null;
    913             processCallStateChange(imsCall, ImsPhoneCall.State.ALERTING,
    914                     DisconnectCause.NOT_DISCONNECTED);
    915         }
    916 
    917         @Override
    918         public void onCallStarted(ImsCall imsCall) {
    919             if (DBG) log("onCallStarted");
    920 
    921             mPendingMO = null;
    922             processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE,
    923                     DisconnectCause.NOT_DISCONNECTED);
    924         }
    925 
    926         /**
    927          * onCallStartFailed will be invoked when:
    928          * case 1) Dialing fails
    929          * case 2) Ringing call is disconnected by local or remote user
    930          */
    931         @Override
    932         public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
    933             if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode());
    934 
    935             if (mPendingMO != null) {
    936                 // To initiate dialing circuit-switched call
    937                 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
    938                         && mBackgroundCall.getState() == ImsPhoneCall.State.IDLE
    939                         && mRingingCall.getState() == ImsPhoneCall.State.IDLE) {
    940                     mForegroundCall.detach(mPendingMO);
    941                     removeConnection(mPendingMO);
    942                     mPendingMO.finalize();
    943                     mPendingMO = null;
    944                     mPhone.initiateSilentRedial();
    945                     return;
    946                 }
    947                 mPendingMO = null;
    948             }
    949         }
    950 
    951         @Override
    952         public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
    953             if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode());
    954 
    955             ImsPhoneCall.State oldState = mForegroundCall.getState();
    956             int cause = getDisconnectCauseFromReasonInfo(reasonInfo);
    957             ImsPhoneConnection conn = findConnection(imsCall);
    958             if (DBG) log("cause = " + cause + " conn = " + conn);
    959 
    960             if (conn != null && conn.isIncoming() && conn.getConnectTime() == 0) {
    961                 // Missed or rejected call
    962                 if (cause == DisconnectCause.LOCAL) {
    963                     cause = DisconnectCause.INCOMING_REJECTED;
    964                 } else {
    965                     cause = DisconnectCause.INCOMING_MISSED;
    966                 }
    967             }
    968             processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause);
    969 
    970             if (reasonInfo.getCode() == ImsReasonInfo.CODE_USER_TERMINATED) {
    971                 if ((oldState == ImsPhoneCall.State.DISCONNECTING)
    972                         && (mForegroundCall.getState() == ImsPhoneCall.State.DISCONNECTED)
    973                         && (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING)) {
    974                     sendEmptyMessage(EVENT_RESUME_BACKGROUND);
    975                 }
    976             }
    977         }
    978 
    979         @Override
    980         public void onCallHeld(ImsCall imsCall) {
    981             if (DBG) log("onCallHeld");
    982 
    983             synchronized (mSyncHold) {
    984                 ImsPhoneCall.State oldState = mBackgroundCall.getState();
    985                 processCallStateChange(imsCall, ImsPhoneCall.State.HOLDING,
    986                         DisconnectCause.NOT_DISCONNECTED);
    987 
    988                 if (oldState == ImsPhoneCall.State.ACTIVE) {
    989                     if ((mForegroundCall.getState() == ImsPhoneCall.State.HOLDING)
    990                             || (mRingingCall.getState() == ImsPhoneCall.State.WAITING)) {
    991                         sendEmptyMessage(EVENT_RESUME_BACKGROUND);
    992                     } else {
    993                         //when multiple connections belong to background call,
    994                         //only the first callback reaches here
    995                         //otherwise the oldState is already HOLDING
    996                         if (mPendingMO != null) {
    997                             sendEmptyMessage(EVENT_DIAL_PENDINGMO);
    998                         }
    999                     }
   1000                 }
   1001             }
   1002         }
   1003 
   1004         @Override
   1005         public void onCallHoldFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
   1006             if (DBG) log("onCallHoldFailed reasonCode=" + reasonInfo.getCode());
   1007 
   1008             synchronized (mSyncHold) {
   1009                 ImsPhoneCall.State bgState = mBackgroundCall.getState();
   1010                 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED) {
   1011                     // disconnected while processing hold
   1012                     if (mPendingMO != null) {
   1013                         sendEmptyMessage(EVENT_DIAL_PENDINGMO);
   1014                     }
   1015                 } else if (bgState == ImsPhoneCall.State.ACTIVE) {
   1016                     mForegroundCall.switchWith(mBackgroundCall);
   1017 
   1018                     if (mPendingMO != null) {
   1019                         mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
   1020                         sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
   1021                     }
   1022                 }
   1023             }
   1024         }
   1025 
   1026         @Override
   1027         public void onCallResumed(ImsCall imsCall) {
   1028             if (DBG) log("onCallResumed");
   1029 
   1030             processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE,
   1031                     DisconnectCause.NOT_DISCONNECTED);
   1032         }
   1033 
   1034         @Override
   1035         public void onCallResumeFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
   1036             // TODO : What should be done?
   1037         }
   1038 
   1039         @Override
   1040         public void onCallResumeReceived(ImsCall imsCall) {
   1041             if (DBG) log("onCallResumeReceived");
   1042 
   1043             if (mOnHoldToneStarted) {
   1044                 mPhone.stopOnHoldTone();
   1045                 mOnHoldToneStarted = false;
   1046             }
   1047         }
   1048 
   1049         @Override
   1050         public void onCallHoldReceived(ImsCall imsCall) {
   1051             if (DBG) log("onCallHoldReceived");
   1052 
   1053             ImsPhoneConnection conn = findConnection(imsCall);
   1054             if (conn != null && conn.getState() == ImsPhoneCall.State.ACTIVE) {
   1055                 if (!mOnHoldToneStarted && ImsPhoneCall.isLocalTone(imsCall)) {
   1056                     mPhone.startOnHoldTone();
   1057                     mOnHoldToneStarted = true;
   1058                 }
   1059             }
   1060         }
   1061 
   1062         @Override
   1063         public void onCallMerged(ImsCall call, ImsCall newCall) {
   1064             if (DBG) log("onCallMerged");
   1065 
   1066             mForegroundCall.merge(mBackgroundCall, mForegroundCall.getState());
   1067             updatePhoneState();
   1068             mPhone.notifyPreciseCallStateChanged();
   1069         }
   1070 
   1071         @Override
   1072         public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) {
   1073             if (DBG) log("onCallMergeFailed reasonCode=" + reasonInfo.getCode());
   1074             mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE);
   1075         }
   1076     };
   1077 
   1078     /**
   1079      * Listen to the IMS call state change
   1080      */
   1081     private ImsCall.Listener mImsUssdListener = new ImsCall.Listener() {
   1082         @Override
   1083         public void onCallStarted(ImsCall imsCall) {
   1084             if (DBG) log("mImsUssdListener onCallStarted");
   1085 
   1086             if (imsCall == mUssdSession) {
   1087                 if (mPendingUssd != null) {
   1088                     AsyncResult.forMessage(mPendingUssd);
   1089                     mPendingUssd.sendToTarget();
   1090                     mPendingUssd = null;
   1091                 }
   1092             }
   1093         }
   1094 
   1095         @Override
   1096         public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
   1097             if (DBG) log("mImsUssdListener onCallStartFailed reasonCode=" + reasonInfo.getCode());
   1098 
   1099             onCallTerminated(imsCall, reasonInfo);
   1100         }
   1101 
   1102         @Override
   1103         public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
   1104             if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode());
   1105 
   1106             if (imsCall == mUssdSession) {
   1107                 mUssdSession = null;
   1108                 if (mPendingUssd != null) {
   1109                     CommandException ex =
   1110                             new CommandException(CommandException.Error.GENERIC_FAILURE);
   1111                     AsyncResult.forMessage(mPendingUssd, null, ex);
   1112                     mPendingUssd.sendToTarget();
   1113                     mPendingUssd = null;
   1114                 }
   1115             }
   1116             imsCall.close();
   1117         }
   1118 
   1119         @Override
   1120         public void onCallUssdMessageReceived(ImsCall call,
   1121                 int mode, String ussdMessage) {
   1122             if (DBG) log("mImsUssdListener onCallUssdMessageReceived mode=" + mode);
   1123 
   1124             int ussdMode = -1;
   1125 
   1126             switch(mode) {
   1127                 case ImsCall.USSD_MODE_REQUEST:
   1128                     ussdMode = CommandsInterface.USSD_MODE_REQUEST;
   1129                     break;
   1130 
   1131                 case ImsCall.USSD_MODE_NOTIFY:
   1132                     ussdMode = CommandsInterface.USSD_MODE_NOTIFY;
   1133                     break;
   1134             }
   1135 
   1136             mPhone.onIncomingUSSD(ussdMode, ussdMessage);
   1137         }
   1138     };
   1139 
   1140     /**
   1141      * Listen to the IMS service state change
   1142      *
   1143      */
   1144     private ImsConnectionStateListener mImsConnectionStateListener =
   1145         new ImsConnectionStateListener() {
   1146         @Override
   1147         public void onImsConnected() {
   1148             if (DBG) log("onImsConnected");
   1149             mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
   1150         }
   1151 
   1152         @Override
   1153         public void onImsDisconnected() {
   1154             if (DBG) log("onImsDisconnected");
   1155             mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
   1156         }
   1157 
   1158         @Override
   1159         public void onImsResumed() {
   1160             if (DBG) log("onImsResumed");
   1161             mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
   1162         }
   1163 
   1164         @Override
   1165         public void onImsSuspended() {
   1166             if (DBG) log("onImsSuspended");
   1167             mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
   1168         }
   1169 
   1170         @Override
   1171         public void onFeatureCapabilityChanged(int serviceClass,
   1172                 int[] enabledFeatures, int[] disabledFeatures) {
   1173             if (serviceClass == ImsServiceClass.MMTEL) {
   1174                 if (enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] ==
   1175                         ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE) {
   1176                     mIsVolteEnabled = true;
   1177                 }
   1178                 if (enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] ==
   1179                         ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE) {
   1180                     mIsVtEnabled = true;
   1181                 }
   1182                 if (disabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] ==
   1183                         ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE) {
   1184                     mIsVolteEnabled = false;
   1185                 }
   1186                 if (disabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] ==
   1187                         ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE) {
   1188                     mIsVtEnabled = false;
   1189                 }
   1190             }
   1191             if (DBG) log("onFeatureCapabilityChanged, mIsVolteEnabled = " +  mIsVolteEnabled
   1192                     + " mIsVtEnabled = " + mIsVtEnabled);
   1193         }
   1194     };
   1195 
   1196     /* package */
   1197     ImsUtInterface getUtInterface() throws ImsException {
   1198         if (mImsManager == null) {
   1199             throw new ImsException("no ims manager", ImsReasonInfo.CODE_UNSPECIFIED);
   1200         }
   1201 
   1202         ImsUtInterface ut = mImsManager.getSupplementaryServiceConfiguration(mServiceId);
   1203         return ut;
   1204     }
   1205 
   1206     /* package */
   1207     void notifySrvccState(Call.SrvccState state) {
   1208         if (DBG) log("notifySrvccState state=" + state);
   1209 
   1210         mSrvccState = state;
   1211 
   1212         if (mSrvccState == Call.SrvccState.COMPLETED) {
   1213             if (mForegroundCall.getConnections().size() > 0) {
   1214                 mHandoverCall.switchWith(mForegroundCall);
   1215             } else if (mBackgroundCall.getConnections().size() > 0) {
   1216                 mHandoverCall.switchWith(mBackgroundCall);
   1217             }
   1218 
   1219             // release wake lock hold
   1220             ImsPhoneConnection con = mHandoverCall.getHandoverConnection();
   1221             if (con != null) {
   1222                 con.releaseWakeLock();
   1223             }
   1224         }
   1225     }
   1226 
   1227     //****** Overridden from Handler
   1228 
   1229     @Override
   1230     public void
   1231     handleMessage (Message msg) {
   1232         AsyncResult ar;
   1233         if (DBG) log("handleMessage what=" + msg.what);
   1234 
   1235         switch (msg.what) {
   1236             case EVENT_HANGUP_PENDINGMO:
   1237                 if (mPendingMO != null) {
   1238                     mPendingMO.onDisconnect();
   1239                     removeConnection(mPendingMO);
   1240                     mPendingMO = null;
   1241                 }
   1242 
   1243                 updatePhoneState();
   1244                 mPhone.notifyPreciseCallStateChanged();
   1245                 break;
   1246             case EVENT_RESUME_BACKGROUND:
   1247                 try {
   1248                     resumeWaitingOrHolding();
   1249                 } catch (CallStateException e) {
   1250                     if (Phone.DEBUG_PHONE) {
   1251                         loge("handleMessage EVENT_RESUME_BACKGROUND exception=" + e);
   1252                     }
   1253                 }
   1254                 break;
   1255             case EVENT_DIAL_PENDINGMO:
   1256                 dialInternal(mPendingMO, mClirMode, VideoProfile.VideoState.AUDIO_ONLY);
   1257                 break;
   1258 
   1259             case EVENT_EXIT_ECM_RESPONSE_CDMA:
   1260                 // no matter the result, we still do the same here
   1261                 if (pendingCallInEcm) {
   1262                     dialInternal(mPendingMO, pendingCallClirMode, pendingCallVideoState);
   1263                     pendingCallInEcm = false;
   1264                 }
   1265                 mPhone.unsetOnEcbModeExitResponse(this);
   1266                 break;
   1267         }
   1268     }
   1269 
   1270     @Override
   1271     protected void log(String msg) {
   1272         Rlog.d(LOG_TAG, "[ImsPhoneCallTracker] " + msg);
   1273     }
   1274 
   1275     protected void loge(String msg) {
   1276         Rlog.e(LOG_TAG, "[ImsPhoneCallTracker] " + msg);
   1277     }
   1278 
   1279     @Override
   1280     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1281         pw.println("ImsPhoneCallTracker extends:");
   1282         super.dump(fd, pw, args);
   1283         pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants);
   1284         pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants);
   1285         pw.println(" mRingingCall=" + mRingingCall);
   1286         pw.println(" mForegroundCall=" + mForegroundCall);
   1287         pw.println(" mBackgroundCall=" + mBackgroundCall);
   1288         pw.println(" mHandoverCall=" + mHandoverCall);
   1289         pw.println(" mPendingMO=" + mPendingMO);
   1290         //pw.println(" mHangupPendingMO=" + mHangupPendingMO);
   1291         pw.println(" mPhone=" + mPhone);
   1292         pw.println(" mDesiredMute=" + mDesiredMute);
   1293         pw.println(" mState=" + mState);
   1294     }
   1295 
   1296     @Override
   1297     protected void handlePollCalls(AsyncResult ar) {
   1298     }
   1299 
   1300     /* package */
   1301     ImsEcbm getEcbmInterface() throws ImsException {
   1302         if (mImsManager == null) {
   1303             throw new ImsException("no ims manager", ImsReasonInfo.CODE_UNSPECIFIED);
   1304         }
   1305 
   1306         ImsEcbm ecbm = mImsManager.getEcbmInterface(mServiceId);
   1307         return ecbm;
   1308     }
   1309 
   1310     public boolean isInEmergencyCall() {
   1311         return mIsInEmergencyCall;
   1312     }
   1313 
   1314     public boolean isVolteEnabled() {
   1315         return mIsVolteEnabled;
   1316     }
   1317 
   1318     public boolean isVtEnabled() {
   1319         return mIsVtEnabled;
   1320     }
   1321 }
   1322