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