Home | History | Annotate | Download | only in cdma
      1 /*
      2  * Copyright (C) 2006 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.cdma;
     18 
     19 import android.content.Context;
     20 import android.os.AsyncResult;
     21 import android.os.Handler;
     22 import android.os.Message;
     23 import android.os.Registrant;
     24 import android.os.RegistrantList;
     25 import android.telephony.DisconnectCause;
     26 import android.telephony.PhoneNumberUtils;
     27 import android.telephony.ServiceState;
     28 import android.telephony.Rlog;
     29 import android.telephony.TelephonyManager;
     30 
     31 import android.os.SystemProperties;
     32 import android.text.TextUtils;
     33 
     34 import com.android.internal.telephony.CallStateException;
     35 import com.android.internal.telephony.CallTracker;
     36 import com.android.internal.telephony.CommandsInterface;
     37 import com.android.internal.telephony.Connection;
     38 import com.android.internal.telephony.DriverCall;
     39 import com.android.internal.telephony.LastCallFailCause;
     40 import com.android.internal.telephony.Phone;
     41 import com.android.internal.telephony.PhoneBase;
     42 import com.android.internal.telephony.PhoneConstants;
     43 import com.android.internal.telephony.TelephonyProperties;
     44 import com.android.internal.telephony.imsphone.ImsPhone;
     45 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
     46 
     47 import java.io.FileDescriptor;
     48 import java.io.PrintWriter;
     49 import java.util.ArrayList;
     50 import java.util.List;
     51 
     52 
     53 /**
     54  * {@hide}
     55  */
     56 public final class CdmaCallTracker extends CallTracker {
     57     static final String LOG_TAG = "CdmaCallTracker";
     58 
     59     private static final boolean REPEAT_POLLING = false;
     60 
     61     private static final boolean DBG_POLL = false;
     62 
     63     //***** Constants
     64 
     65     static final int MAX_CONNECTIONS = 8;
     66     static final int MAX_CONNECTIONS_PER_CALL = 1; // only 1 connection allowed per call
     67 
     68     //***** Instance Variables
     69 
     70     CdmaConnection mConnections[] = new CdmaConnection[MAX_CONNECTIONS];
     71     RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
     72     RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
     73     RegistrantList mCallWaitingRegistrants =  new RegistrantList();
     74 
     75 
     76     // connections dropped during last poll
     77     ArrayList<CdmaConnection> mDroppedDuringPoll
     78         = new ArrayList<CdmaConnection>(MAX_CONNECTIONS);
     79 
     80     CdmaCall mRingingCall = new CdmaCall(this);
     81     // A call that is ringing or (call) waiting
     82     CdmaCall mForegroundCall = new CdmaCall(this);
     83     CdmaCall mBackgroundCall = new CdmaCall(this);
     84 
     85     CdmaConnection mPendingMO;
     86     boolean mHangupPendingMO;
     87     boolean mPendingCallInEcm=false;
     88     boolean mIsInEmergencyCall = false;
     89     CDMAPhone mPhone;
     90 
     91     boolean mDesiredMute = false;    // false = mute off
     92 
     93     int mPendingCallClirMode;
     94     PhoneConstants.State mState = PhoneConstants.State.IDLE;
     95 
     96     private boolean mIsEcmTimerCanceled = false;
     97 
     98     private int m3WayCallFlashDelay = 0;
     99 //    boolean needsPoll;
    100 
    101 
    102 
    103     //***** Events
    104 
    105     //***** Constructors
    106     CdmaCallTracker(CDMAPhone phone) {
    107         mPhone = phone;
    108         mCi = phone.mCi;
    109         mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
    110         mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null);
    111         mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);
    112         mCi.registerForCallWaitingInfo(this, EVENT_CALL_WAITING_INFO_CDMA, null);
    113         mForegroundCall.setGeneric(false);
    114     }
    115 
    116     public void dispose() {
    117         Rlog.d(LOG_TAG, "CdmaCallTracker dispose");
    118         mCi.unregisterForLineControlInfo(this);
    119         mCi.unregisterForCallStateChanged(this);
    120         mCi.unregisterForOn(this);
    121         mCi.unregisterForNotAvailable(this);
    122         mCi.unregisterForCallWaitingInfo(this);
    123 
    124         clearDisconnected();
    125 
    126     }
    127 
    128     @Override
    129     protected void finalize() {
    130         Rlog.d(LOG_TAG, "CdmaCallTracker finalized");
    131     }
    132 
    133     //***** Instance Methods
    134 
    135     //***** Public Methods
    136     @Override
    137     public void registerForVoiceCallStarted(Handler h, int what, Object obj) {
    138         Registrant r = new Registrant(h, what, obj);
    139         mVoiceCallStartedRegistrants.add(r);
    140         // Notify if in call when registering
    141         if (mState != PhoneConstants.State.IDLE) {
    142             r.notifyRegistrant(new AsyncResult(null, null, null));
    143         }
    144     }
    145     @Override
    146     public void unregisterForVoiceCallStarted(Handler h) {
    147         mVoiceCallStartedRegistrants.remove(h);
    148     }
    149 
    150     @Override
    151     public void registerForVoiceCallEnded(Handler h, int what, Object obj) {
    152         Registrant r = new Registrant(h, what, obj);
    153         mVoiceCallEndedRegistrants.add(r);
    154     }
    155 
    156     @Override
    157     public void unregisterForVoiceCallEnded(Handler h) {
    158         mVoiceCallEndedRegistrants.remove(h);
    159     }
    160 
    161     public void registerForCallWaiting(Handler h, int what, Object obj) {
    162         Registrant r = new Registrant (h, what, obj);
    163         mCallWaitingRegistrants.add(r);
    164     }
    165 
    166     public void unregisterForCallWaiting(Handler h) {
    167         mCallWaitingRegistrants.remove(h);
    168     }
    169 
    170     /**
    171      * clirMode is one of the CLIR_ constants
    172      */
    173     Connection
    174     dial (String dialString, int clirMode) throws CallStateException {
    175         // note that this triggers call state changed notif
    176         clearDisconnected();
    177 
    178         if (!canDial()) {
    179             throw new CallStateException("cannot dial in current state");
    180         }
    181 
    182         TelephonyManager tm =
    183                 (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
    184         String origNumber = dialString;
    185         String operatorIsoContry = tm.getNetworkCountryIsoForPhone(mPhone.getPhoneId());
    186         String simIsoContry = tm.getSimCountryIsoForPhone(mPhone.getPhoneId());
    187         boolean internationalRoaming = !TextUtils.isEmpty(operatorIsoContry)
    188                 && !TextUtils.isEmpty(simIsoContry)
    189                 && !simIsoContry.equals(operatorIsoContry);
    190         if (internationalRoaming) {
    191             if ("us".equals(simIsoContry)) {
    192                 internationalRoaming = internationalRoaming && !"vi".equals(operatorIsoContry);
    193             } else if ("vi".equals(simIsoContry)) {
    194                 internationalRoaming = internationalRoaming && !"us".equals(operatorIsoContry);
    195             }
    196         }
    197         if (internationalRoaming) {
    198             dialString = convertNumberIfNecessary(mPhone, dialString);
    199         }
    200 
    201         String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
    202         boolean isPhoneInEcmMode = inEcm.equals("true");
    203         boolean isEmergencyCall =
    204                 PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString);
    205 
    206         // Cancel Ecm timer if a second emergency call is originating in Ecm mode
    207         if (isPhoneInEcmMode && isEmergencyCall) {
    208             handleEcmTimer(CDMAPhone.CANCEL_ECM_TIMER);
    209         }
    210 
    211         // We are initiating a call therefore even if we previously
    212         // didn't know the state (i.e. Generic was true) we now know
    213         // and therefore can set Generic to false.
    214         mForegroundCall.setGeneric(false);
    215 
    216         // The new call must be assigned to the foreground call.
    217         // That call must be idle, so place anything that's
    218         // there on hold
    219         if (mForegroundCall.getState() == CdmaCall.State.ACTIVE) {
    220             return dialThreeWay(dialString);
    221         }
    222 
    223         mPendingMO = new CdmaConnection(mPhone.getContext(), checkForTestEmergencyNumber(dialString),
    224                 this, mForegroundCall);
    225         mHangupPendingMO = false;
    226 
    227         if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
    228                 || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0 ) {
    229             // Phone number is invalid
    230             mPendingMO.mCause = DisconnectCause.INVALID_NUMBER;
    231 
    232             // handlePollCalls() will notice this call not present
    233             // and will mark it as dropped.
    234             pollCallsWhenSafe();
    235         } else {
    236             // Always unmute when initiating a new call
    237             setMute(false);
    238 
    239             // Check data call
    240             disableDataCallInEmergencyCall(dialString);
    241 
    242             // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
    243             if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
    244                 mCi.dial(mPendingMO.getAddress(), clirMode, obtainCompleteMessage());
    245             } else {
    246                 mPhone.exitEmergencyCallbackMode();
    247                 mPhone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null);
    248                 mPendingCallClirMode=clirMode;
    249                 mPendingCallInEcm=true;
    250             }
    251         }
    252 
    253         if (mNumberConverted) {
    254             mPendingMO.setConverted(origNumber);
    255             mNumberConverted = false;
    256         }
    257 
    258         updatePhoneState();
    259         mPhone.notifyPreciseCallStateChanged();
    260 
    261         return mPendingMO;
    262     }
    263 
    264 
    265     Connection
    266     dial (String dialString) throws CallStateException {
    267         return dial(dialString, CommandsInterface.CLIR_DEFAULT);
    268     }
    269 
    270     private Connection
    271     dialThreeWay (String dialString) {
    272         if (!mForegroundCall.isIdle()) {
    273             // Check data call
    274             disableDataCallInEmergencyCall(dialString);
    275 
    276             // Attach the new connection to foregroundCall
    277             mPendingMO = new CdmaConnection(mPhone.getContext(),
    278                                 checkForTestEmergencyNumber(dialString), this, mForegroundCall);
    279             // Some network need a empty flash before sending the normal one
    280             m3WayCallFlashDelay = mPhone.getContext().getResources()
    281                     .getInteger(com.android.internal.R.integer.config_cdma_3waycall_flash_delay);
    282             if (m3WayCallFlashDelay > 0) {
    283                 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_THREE_WAY_DIAL_BLANK_FLASH));
    284             } else {
    285                 mCi.sendCDMAFeatureCode(mPendingMO.getAddress(),
    286                         obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA));
    287             }
    288             return mPendingMO;
    289         }
    290         return null;
    291     }
    292 
    293     void
    294     acceptCall() throws CallStateException {
    295         if (mRingingCall.getState() == CdmaCall.State.INCOMING) {
    296             Rlog.i("phone", "acceptCall: incoming...");
    297             // Always unmute when answering a new call
    298             setMute(false);
    299             mCi.acceptCall(obtainCompleteMessage());
    300         } else if (mRingingCall.getState() == CdmaCall.State.WAITING) {
    301             CdmaConnection cwConn = (CdmaConnection)(mRingingCall.getLatestConnection());
    302 
    303             // Since there is no network response for supplimentary
    304             // service for CDMA, we assume call waiting is answered.
    305             // ringing Call state change to idle is in CdmaCall.detach
    306             // triggered by updateParent.
    307             cwConn.updateParent(mRingingCall, mForegroundCall);
    308             cwConn.onConnectedInOrOut();
    309             updatePhoneState();
    310             switchWaitingOrHoldingAndActive();
    311         } else {
    312             throw new CallStateException("phone not ringing");
    313         }
    314     }
    315 
    316     void
    317     rejectCall () throws CallStateException {
    318         // AT+CHLD=0 means "release held or UDUB"
    319         // so if the phone isn't ringing, this could hang up held
    320         if (mRingingCall.getState().isRinging()) {
    321             mCi.rejectCall(obtainCompleteMessage());
    322         } else {
    323             throw new CallStateException("phone not ringing");
    324         }
    325     }
    326 
    327     void
    328     switchWaitingOrHoldingAndActive() throws CallStateException {
    329         // Should we bother with this check?
    330         if (mRingingCall.getState() == CdmaCall.State.INCOMING) {
    331             throw new CallStateException("cannot be in the incoming state");
    332         } else if (mForegroundCall.getConnections().size() > 1) {
    333             flashAndSetGenericTrue();
    334         } else {
    335             // Send a flash command to CDMA network for putting the other party on hold.
    336             // For CDMA networks which do not support this the user would just hear a beep
    337             // from the network. For CDMA networks which do support it will put the other
    338             // party on hold.
    339             mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT));
    340         }
    341     }
    342 
    343     void
    344     conference() {
    345         // Should we be checking state?
    346         flashAndSetGenericTrue();
    347     }
    348 
    349     void
    350     explicitCallTransfer() {
    351         mCi.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT));
    352     }
    353 
    354     void
    355     clearDisconnected() {
    356         internalClearDisconnected();
    357 
    358         updatePhoneState();
    359         mPhone.notifyPreciseCallStateChanged();
    360     }
    361 
    362     boolean
    363     canConference() {
    364         return mForegroundCall.getState() == CdmaCall.State.ACTIVE
    365                 && mBackgroundCall.getState() == CdmaCall.State.HOLDING
    366                 && !mBackgroundCall.isFull()
    367                 && !mForegroundCall.isFull();
    368     }
    369 
    370     boolean
    371     canDial() {
    372         boolean ret;
    373         int serviceState = mPhone.getServiceState().getState();
    374         String disableCall = SystemProperties.get(
    375                 TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
    376 
    377         ret = (serviceState != ServiceState.STATE_POWER_OFF)
    378                 && mPendingMO == null
    379                 && !mRingingCall.isRinging()
    380                 && !disableCall.equals("true")
    381                 && (!mForegroundCall.getState().isAlive()
    382                     || (mForegroundCall.getState() == CdmaCall.State.ACTIVE)
    383                     || !mBackgroundCall.getState().isAlive());
    384 
    385         if (!ret) {
    386             log(String.format("canDial is false\n" +
    387                               "((serviceState=%d) != ServiceState.STATE_POWER_OFF)::=%s\n" +
    388                               "&& pendingMO == null::=%s\n" +
    389                               "&& !ringingCall.isRinging()::=%s\n" +
    390                               "&& !disableCall.equals(\"true\")::=%s\n" +
    391                               "&& (!foregroundCall.getState().isAlive()::=%s\n" +
    392                               "   || foregroundCall.getState() == CdmaCall.State.ACTIVE::=%s\n" +
    393                               "   ||!backgroundCall.getState().isAlive())::=%s)",
    394                     serviceState,
    395                     serviceState != ServiceState.STATE_POWER_OFF,
    396                     mPendingMO == null,
    397                     !mRingingCall.isRinging(),
    398                     !disableCall.equals("true"),
    399                     !mForegroundCall.getState().isAlive(),
    400                     mForegroundCall.getState() == CdmaCall.State.ACTIVE,
    401                     !mBackgroundCall.getState().isAlive()));
    402         }
    403         return ret;
    404     }
    405 
    406     boolean
    407     canTransfer() {
    408         Rlog.e(LOG_TAG, "canTransfer: not possible in CDMA");
    409         return false;
    410     }
    411 
    412     //***** Private Instance Methods
    413 
    414     private void
    415     internalClearDisconnected() {
    416         mRingingCall.clearDisconnected();
    417         mForegroundCall.clearDisconnected();
    418         mBackgroundCall.clearDisconnected();
    419     }
    420 
    421     /**
    422      * Obtain a message to use for signalling "invoke getCurrentCalls() when
    423      * this operation and all other pending operations are complete
    424      */
    425     private Message
    426     obtainCompleteMessage() {
    427         return obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
    428     }
    429 
    430     /**
    431      * Obtain a message to use for signalling "invoke getCurrentCalls() when
    432      * this operation and all other pending operations are complete
    433      */
    434     private Message
    435     obtainCompleteMessage(int what) {
    436         mPendingOperations++;
    437         mLastRelevantPoll = null;
    438         mNeedsPoll = true;
    439 
    440         if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" +
    441                 mPendingOperations + ", needsPoll=" + mNeedsPoll);
    442 
    443         return obtainMessage(what);
    444     }
    445 
    446     private void
    447     operationComplete() {
    448         mPendingOperations--;
    449 
    450         if (DBG_POLL) log("operationComplete: pendingOperations=" +
    451                 mPendingOperations + ", needsPoll=" + mNeedsPoll);
    452 
    453         if (mPendingOperations == 0 && mNeedsPoll) {
    454             mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
    455             mCi.getCurrentCalls(mLastRelevantPoll);
    456         } else if (mPendingOperations < 0) {
    457             // this should never happen
    458             Rlog.e(LOG_TAG,"CdmaCallTracker.pendingOperations < 0");
    459             mPendingOperations = 0;
    460         }
    461     }
    462 
    463 
    464 
    465     private void
    466     updatePhoneState() {
    467         PhoneConstants.State oldState = mState;
    468 
    469         if (mRingingCall.isRinging()) {
    470             mState = PhoneConstants.State.RINGING;
    471         } else if (mPendingMO != null ||
    472                 !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
    473             mState = PhoneConstants.State.OFFHOOK;
    474         } else {
    475             ImsPhone imsPhone = (ImsPhone)mPhone.getImsPhone();
    476             if ( mState == PhoneConstants.State.OFFHOOK && (imsPhone != null)){
    477                 imsPhone.callEndCleanupHandOverCallIfAny();
    478             }
    479             mState = PhoneConstants.State.IDLE;
    480         }
    481 
    482         if (mState == PhoneConstants.State.IDLE && oldState != mState) {
    483             mVoiceCallEndedRegistrants.notifyRegistrants(
    484                 new AsyncResult(null, null, null));
    485         } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
    486             mVoiceCallStartedRegistrants.notifyRegistrants (
    487                     new AsyncResult(null, null, null));
    488         }
    489         if (Phone.DEBUG_PHONE) {
    490             log("update phone state, old=" + oldState + " new="+ mState);
    491         }
    492         if (mState != oldState) {
    493             mPhone.notifyPhoneStateChanged();
    494         }
    495     }
    496 
    497     // ***** Overwritten from CallTracker
    498 
    499     @Override
    500     protected void
    501     handlePollCalls(AsyncResult ar) {
    502         List polledCalls;
    503 
    504         if (ar.exception == null) {
    505             polledCalls = (List)ar.result;
    506         } else if (isCommandExceptionRadioNotAvailable(ar.exception)) {
    507             // just a dummy empty ArrayList to cause the loop
    508             // to hang up all the calls
    509             polledCalls = new ArrayList();
    510         } else {
    511             // Radio probably wasn't ready--try again in a bit
    512             // But don't keep polling if the channel is closed
    513             pollCallsAfterDelay();
    514             return;
    515         }
    516 
    517         Connection newRinging = null; //or waiting
    518         Connection newUnknown = null;
    519         boolean hasNonHangupStateChanged = false;   // Any change besides
    520                                                     // a dropped connection
    521         boolean hasAnyCallDisconnected = false;
    522         boolean needsPollDelay = false;
    523         boolean unknownConnectionAppeared = false;
    524 
    525         for (int i = 0, curDC = 0, dcSize = polledCalls.size()
    526                 ; i < mConnections.length; i++) {
    527             CdmaConnection conn = mConnections[i];
    528             DriverCall dc = null;
    529 
    530             // polledCall list is sparse
    531             if (curDC < dcSize) {
    532                 dc = (DriverCall) polledCalls.get(curDC);
    533 
    534                 if (dc.index == i+1) {
    535                     curDC++;
    536                 } else {
    537                     dc = null;
    538                 }
    539             }
    540 
    541             if (DBG_POLL) log("poll: conn[i=" + i + "]=" +
    542                     conn+", dc=" + dc);
    543 
    544             if (conn == null && dc != null) {
    545                 // Connection appeared in CLCC response that we don't know about
    546                 if (mPendingMO != null && mPendingMO.compareTo(dc)) {
    547 
    548                     if (DBG_POLL) log("poll: pendingMO=" + mPendingMO);
    549 
    550                     // It's our pending mobile originating call
    551                     mConnections[i] = mPendingMO;
    552                     mPendingMO.mIndex = i;
    553                     mPendingMO.update(dc);
    554                     mPendingMO = null;
    555 
    556                     // Someone has already asked to hangup this call
    557                     if (mHangupPendingMO) {
    558                         mHangupPendingMO = false;
    559                         // Re-start Ecm timer when an uncompleted emergency call ends
    560                         if (mIsEcmTimerCanceled) {
    561                             handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER);
    562                         }
    563 
    564                         try {
    565                             if (Phone.DEBUG_PHONE) log(
    566                                     "poll: hangupPendingMO, hangup conn " + i);
    567                             hangup(mConnections[i]);
    568                         } catch (CallStateException ex) {
    569                             Rlog.e(LOG_TAG, "unexpected error on hangup");
    570                         }
    571 
    572                         // Do not continue processing this poll
    573                         // Wait for hangup and repoll
    574                         return;
    575                     }
    576                 } else {
    577                     if (Phone.DEBUG_PHONE) {
    578                         log("pendingMo=" + mPendingMO + ", dc=" + dc);
    579                     }
    580                     mConnections[i] = new CdmaConnection(mPhone.getContext(), dc, this, i);
    581 
    582                     Connection hoConnection = getHoConnection(dc);
    583                     if (hoConnection != null) {
    584                         // Single Radio Voice Call Continuity (SRVCC) completed
    585                         mConnections[i].migrateFrom(hoConnection);
    586                         mHandoverConnections.remove(hoConnection);
    587                         mPhone.notifyHandoverStateChanged(mConnections[i]);
    588                     } else {
    589                         // find if the MT call is a new ring or unknown connection
    590                         newRinging = checkMtFindNewRinging(dc,i);
    591                         if (newRinging == null) {
    592                             unknownConnectionAppeared = true;
    593                             newUnknown = mConnections[i];
    594                         }
    595                     }
    596                     checkAndEnableDataCallAfterEmergencyCallDropped();
    597                 }
    598                 hasNonHangupStateChanged = true;
    599             } else if (conn != null && dc == null) {
    600                 // This case means the RIL has no more active call anymore and
    601                 // we need to clean up the foregroundCall and ringingCall.
    602                 // Loop through foreground call connections as
    603                 // it contains the known logical connections.
    604                 int count = mForegroundCall.mConnections.size();
    605                 for (int n = 0; n < count; n++) {
    606                     if (Phone.DEBUG_PHONE) log("adding fgCall cn " + n + " to droppedDuringPoll");
    607                     CdmaConnection cn = (CdmaConnection)mForegroundCall.mConnections.get(n);
    608                     mDroppedDuringPoll.add(cn);
    609                 }
    610                 count = mRingingCall.mConnections.size();
    611                 // Loop through ringing call connections as
    612                 // it may contain the known logical connections.
    613                 for (int n = 0; n < count; n++) {
    614                     if (Phone.DEBUG_PHONE) log("adding rgCall cn " + n + " to droppedDuringPoll");
    615                     CdmaConnection cn = (CdmaConnection)mRingingCall.mConnections.get(n);
    616                     mDroppedDuringPoll.add(cn);
    617                 }
    618                 mForegroundCall.setGeneric(false);
    619                 mRingingCall.setGeneric(false);
    620 
    621                 // Re-start Ecm timer when the connected emergency call ends
    622                 if (mIsEcmTimerCanceled) {
    623                     handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER);
    624                 }
    625                 // If emergency call is not going through while dialing
    626                 checkAndEnableDataCallAfterEmergencyCallDropped();
    627 
    628                 // Dropped connections are removed from the CallTracker
    629                 // list but kept in the Call list
    630                 mConnections[i] = null;
    631             } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
    632                 // Call collision case
    633                 if (conn.isIncoming() != dc.isMT) {
    634                     if (dc.isMT == true){
    635                         // Mt call takes precedence than Mo,drops Mo
    636                         mDroppedDuringPoll.add(conn);
    637                         // find if the MT call is a new ring or unknown connection
    638                         newRinging = checkMtFindNewRinging(dc,i);
    639                         if (newRinging == null) {
    640                             unknownConnectionAppeared = true;
    641                             newUnknown = conn;
    642                         }
    643                         checkAndEnableDataCallAfterEmergencyCallDropped();
    644                     } else {
    645                         // Call info stored in conn is not consistent with the call info from dc.
    646                         // We should follow the rule of MT calls taking precedence over MO calls
    647                         // when there is conflict, so here we drop the call info from dc and
    648                         // continue to use the call info from conn, and only take a log.
    649                         Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc);
    650                     }
    651                 } else {
    652                     boolean changed;
    653                     changed = conn.update(dc);
    654                     hasNonHangupStateChanged = hasNonHangupStateChanged || changed;
    655                 }
    656             }
    657 
    658             if (REPEAT_POLLING) {
    659                 if (dc != null) {
    660                     // FIXME with RIL, we should not need this anymore
    661                     if ((dc.state == DriverCall.State.DIALING
    662                             /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/)
    663                         || (dc.state == DriverCall.State.ALERTING
    664                             /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/)
    665                         || (dc.state == DriverCall.State.INCOMING
    666                             /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/)
    667                         || (dc.state == DriverCall.State.WAITING
    668                             /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/)
    669                     ) {
    670                         // Sometimes there's no unsolicited notification
    671                         // for state transitions
    672                         needsPollDelay = true;
    673                     }
    674                 }
    675             }
    676         }
    677 
    678         // This is the first poll after an ATD.
    679         // We expect the pending call to appear in the list
    680         // If it does not, we land here
    681         if (mPendingMO != null) {
    682             Rlog.d(LOG_TAG,"Pending MO dropped before poll fg state:"
    683                             + mForegroundCall.getState());
    684 
    685             mDroppedDuringPoll.add(mPendingMO);
    686             mPendingMO = null;
    687             mHangupPendingMO = false;
    688             if( mPendingCallInEcm) {
    689                 mPendingCallInEcm = false;
    690             }
    691             checkAndEnableDataCallAfterEmergencyCallDropped();
    692         }
    693 
    694         if (newRinging != null) {
    695             mPhone.notifyNewRingingConnection(newRinging);
    696         }
    697 
    698         // clear the "local hangup" and "missed/rejected call"
    699         // cases from the "dropped during poll" list
    700         // These cases need no "last call fail" reason
    701         for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
    702             CdmaConnection conn = mDroppedDuringPoll.get(i);
    703 
    704             if (conn.isIncoming() && conn.getConnectTime() == 0) {
    705                 // Missed or rejected call
    706                 int cause;
    707                 if (conn.mCause == DisconnectCause.LOCAL) {
    708                     cause = DisconnectCause.INCOMING_REJECTED;
    709                 } else {
    710                     cause = DisconnectCause.INCOMING_MISSED;
    711                 }
    712 
    713                 if (Phone.DEBUG_PHONE) {
    714                     log("missed/rejected call, conn.cause=" + conn.mCause);
    715                     log("setting cause to " + cause);
    716                 }
    717                 mDroppedDuringPoll.remove(i);
    718                 hasAnyCallDisconnected |= conn.onDisconnect(cause);
    719             } else if (conn.mCause == DisconnectCause.LOCAL
    720                     || conn.mCause == DisconnectCause.INVALID_NUMBER) {
    721                 mDroppedDuringPoll.remove(i);
    722                 hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
    723             }
    724         }
    725 
    726         /* Disconnect any pending Handover connections */
    727         for (Connection hoConnection : mHandoverConnections) {
    728             log("handlePollCalls - disconnect hoConn= " + hoConnection.toString());
    729             ((ImsPhoneConnection)hoConnection).onDisconnect(DisconnectCause.NOT_VALID);
    730             mHandoverConnections.remove(hoConnection);
    731         }
    732 
    733         // Any non-local disconnects: determine cause
    734         if (mDroppedDuringPoll.size() > 0) {
    735             mCi.getLastCallFailCause(
    736                 obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
    737         }
    738 
    739         if (needsPollDelay) {
    740             pollCallsAfterDelay();
    741         }
    742 
    743         // Cases when we can no longer keep disconnected Connection's
    744         // with their previous calls
    745         // 1) the phone has started to ring
    746         // 2) A Call/Connection object has changed state...
    747         //    we may have switched or held or answered (but not hung up)
    748         if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) {
    749             internalClearDisconnected();
    750         }
    751 
    752         updatePhoneState();
    753 
    754         if (unknownConnectionAppeared) {
    755             mPhone.notifyUnknownConnection(newUnknown);
    756         }
    757 
    758         if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
    759             mPhone.notifyPreciseCallStateChanged();
    760         }
    761 
    762         //dumpState();
    763     }
    764 
    765     //***** Called from CdmaConnection
    766     /*package*/ void
    767     hangup (CdmaConnection conn) throws CallStateException {
    768         if (conn.mOwner != this) {
    769             throw new CallStateException ("CdmaConnection " + conn
    770                                     + "does not belong to CdmaCallTracker " + this);
    771         }
    772 
    773         if (conn == mPendingMO) {
    774             // We're hanging up an outgoing call that doesn't have it's
    775             // GSM index assigned yet
    776 
    777             if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true");
    778             mHangupPendingMO = true;
    779         } else if ((conn.getCall() == mRingingCall)
    780                 && (mRingingCall.getState() == CdmaCall.State.WAITING)) {
    781             // Handle call waiting hang up case.
    782             //
    783             // The ringingCall state will change to IDLE in CdmaCall.detach
    784             // if the ringing call connection size is 0. We don't specifically
    785             // set the ringing call state to IDLE here to avoid a race condition
    786             // where a new call waiting could get a hang up from an old call
    787             // waiting ringingCall.
    788             //
    789             // PhoneApp does the call log itself since only PhoneApp knows
    790             // the hangup reason is user ignoring or timing out. So conn.onDisconnect()
    791             // is not called here. Instead, conn.onLocalDisconnect() is called.
    792             conn.onLocalDisconnect();
    793             updatePhoneState();
    794             mPhone.notifyPreciseCallStateChanged();
    795             return;
    796         } else {
    797             try {
    798                 mCi.hangupConnection (conn.getCDMAIndex(), obtainCompleteMessage());
    799             } catch (CallStateException ex) {
    800                 // Ignore "connection not found"
    801                 // Call may have hung up already
    802                 Rlog.w(LOG_TAG,"CdmaCallTracker WARN: hangup() on absent connection "
    803                                 + conn);
    804             }
    805         }
    806 
    807         conn.onHangupLocal();
    808     }
    809 
    810     /*package*/ void
    811     separate (CdmaConnection conn) throws CallStateException {
    812         if (conn.mOwner != this) {
    813             throw new CallStateException ("CdmaConnection " + conn
    814                                     + "does not belong to CdmaCallTracker " + this);
    815         }
    816         try {
    817             mCi.separateConnection (conn.getCDMAIndex(),
    818                 obtainCompleteMessage(EVENT_SEPARATE_RESULT));
    819         } catch (CallStateException ex) {
    820             // Ignore "connection not found"
    821             // Call may have hung up already
    822             Rlog.w(LOG_TAG,"CdmaCallTracker WARN: separate() on absent connection "
    823                           + conn);
    824         }
    825     }
    826 
    827     //***** Called from CDMAPhone
    828 
    829     /*package*/ void
    830     setMute(boolean mute) {
    831         mDesiredMute = mute;
    832         mCi.setMute(mDesiredMute, null);
    833     }
    834 
    835     /*package*/ boolean
    836     getMute() {
    837         return mDesiredMute;
    838     }
    839 
    840 
    841     //***** Called from CdmaCall
    842 
    843     /* package */ void
    844     hangup (CdmaCall call) throws CallStateException {
    845         if (call.getConnections().size() == 0) {
    846             throw new CallStateException("no connections in call");
    847         }
    848 
    849         if (call == mRingingCall) {
    850             if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background");
    851             mCi.hangupWaitingOrBackground(obtainCompleteMessage());
    852         } else if (call == mForegroundCall) {
    853             if (call.isDialingOrAlerting()) {
    854                 if (Phone.DEBUG_PHONE) {
    855                     log("(foregnd) hangup dialing or alerting...");
    856                 }
    857                 hangup((CdmaConnection)(call.getConnections().get(0)));
    858             } else {
    859                 hangupForegroundResumeBackground();
    860             }
    861         } else if (call == mBackgroundCall) {
    862             if (mRingingCall.isRinging()) {
    863                 if (Phone.DEBUG_PHONE) {
    864                     log("hangup all conns in background call");
    865                 }
    866                 hangupAllConnections(call);
    867             } else {
    868                 hangupWaitingOrBackground();
    869             }
    870         } else {
    871             throw new RuntimeException ("CdmaCall " + call +
    872                     "does not belong to CdmaCallTracker " + this);
    873         }
    874 
    875         call.onHangupLocal();
    876         mPhone.notifyPreciseCallStateChanged();
    877     }
    878 
    879     /* package */
    880     void hangupWaitingOrBackground() {
    881         if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground");
    882         mCi.hangupWaitingOrBackground(obtainCompleteMessage());
    883     }
    884 
    885     /* package */
    886     void hangupForegroundResumeBackground() {
    887         if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground");
    888         mCi.hangupForegroundResumeBackground(obtainCompleteMessage());
    889     }
    890 
    891     void hangupConnectionByIndex(CdmaCall call, int index)
    892             throws CallStateException {
    893         int count = call.mConnections.size();
    894         for (int i = 0; i < count; i++) {
    895             CdmaConnection cn = (CdmaConnection)call.mConnections.get(i);
    896             if (cn.getCDMAIndex() == index) {
    897                 mCi.hangupConnection(index, obtainCompleteMessage());
    898                 return;
    899             }
    900         }
    901 
    902         throw new CallStateException("no gsm index found");
    903     }
    904 
    905     void hangupAllConnections(CdmaCall call) {
    906         try {
    907             int count = call.mConnections.size();
    908             for (int i = 0; i < count; i++) {
    909                 CdmaConnection cn = (CdmaConnection)call.mConnections.get(i);
    910                 mCi.hangupConnection(cn.getCDMAIndex(), obtainCompleteMessage());
    911             }
    912         } catch (CallStateException ex) {
    913             Rlog.e(LOG_TAG, "hangupConnectionByIndex caught " + ex);
    914         }
    915     }
    916 
    917     /* package */
    918     CdmaConnection getConnectionByIndex(CdmaCall call, int index)
    919             throws CallStateException {
    920         int count = call.mConnections.size();
    921         for (int i = 0; i < count; i++) {
    922             CdmaConnection cn = (CdmaConnection)call.mConnections.get(i);
    923             if (cn.getCDMAIndex() == index) {
    924                 return cn;
    925             }
    926         }
    927 
    928         return null;
    929     }
    930 
    931     private void flashAndSetGenericTrue() {
    932         mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT));
    933 
    934         // Set generic to true because in CDMA it is not known what
    935         // the status of the call is after a call waiting is answered,
    936         // 3 way call merged or a switch between calls.
    937         mForegroundCall.setGeneric(true);
    938         mPhone.notifyPreciseCallStateChanged();
    939     }
    940 
    941     private void handleRadioNotAvailable() {
    942         // handlePollCalls will clear out its
    943         // call list when it gets the CommandException
    944         // error result from this
    945         pollCallsWhenSafe();
    946     }
    947 
    948     private void notifyCallWaitingInfo(CdmaCallWaitingNotification obj) {
    949         if (mCallWaitingRegistrants != null) {
    950             mCallWaitingRegistrants.notifyRegistrants(new AsyncResult(null, obj, null));
    951         }
    952     }
    953 
    954     private void handleCallWaitingInfo (CdmaCallWaitingNotification cw) {
    955         // Check how many connections in foregroundCall.
    956         // If the connection in foregroundCall is more
    957         // than one, then the connection information is
    958         // not reliable anymore since it means either
    959         // call waiting is connected or 3 way call is
    960         // dialed before, so set generic.
    961         if (mForegroundCall.mConnections.size() > 1 ) {
    962             mForegroundCall.setGeneric(true);
    963         }
    964 
    965         // Create a new CdmaConnection which attaches itself to ringingCall.
    966         mRingingCall.setGeneric(false);
    967         new CdmaConnection(mPhone.getContext(), cw, this, mRingingCall);
    968         updatePhoneState();
    969 
    970         // Finally notify application
    971         notifyCallWaitingInfo(cw);
    972     }
    973     //****** Overridden from Handler
    974 
    975     @Override
    976     public void
    977     handleMessage (Message msg) {
    978         AsyncResult ar;
    979 
    980         if (!mPhone.mIsTheCurrentActivePhone) {
    981             Rlog.w(LOG_TAG, "Ignoring events received on inactive CdmaPhone");
    982             return;
    983         }
    984         switch (msg.what) {
    985             case EVENT_POLL_CALLS_RESULT:{
    986                 Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");
    987                 ar = (AsyncResult)msg.obj;
    988 
    989                 if(msg == mLastRelevantPoll) {
    990                     if(DBG_POLL) log(
    991                             "handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
    992                     mNeedsPoll = false;
    993                     mLastRelevantPoll = null;
    994                     handlePollCalls((AsyncResult)msg.obj);
    995                 }
    996             }
    997             break;
    998 
    999             case EVENT_OPERATION_COMPLETE:
   1000                 operationComplete();
   1001             break;
   1002 
   1003             case EVENT_SWITCH_RESULT:
   1004                  // In GSM call operationComplete() here which gets the
   1005                  // current call list. But in CDMA there is no list so
   1006                  // there is nothing to do.
   1007             break;
   1008 
   1009             case EVENT_GET_LAST_CALL_FAIL_CAUSE:
   1010                 int causeCode;
   1011                 String vendorCause = null;
   1012                 ar = (AsyncResult)msg.obj;
   1013 
   1014                 operationComplete();
   1015 
   1016                 if (ar.exception != null) {
   1017                     // An exception occurred...just treat the disconnect
   1018                     // cause as "normal"
   1019                     causeCode = CallFailCause.NORMAL_CLEARING;
   1020                     Rlog.i(LOG_TAG,
   1021                             "Exception during getLastCallFailCause, assuming normal disconnect");
   1022                 } else {
   1023                     LastCallFailCause failCause = (LastCallFailCause)ar.result;
   1024                     causeCode = failCause.causeCode;
   1025                     vendorCause = failCause.vendorCause;
   1026                 }
   1027 
   1028                 for (int i = 0, s =  mDroppedDuringPoll.size()
   1029                         ; i < s ; i++
   1030                 ) {
   1031                     CdmaConnection conn = mDroppedDuringPoll.get(i);
   1032 
   1033                     conn.onRemoteDisconnect(causeCode, vendorCause);
   1034                 }
   1035 
   1036                 updatePhoneState();
   1037 
   1038                 mPhone.notifyPreciseCallStateChanged();
   1039                 mDroppedDuringPoll.clear();
   1040             break;
   1041 
   1042             case EVENT_REPOLL_AFTER_DELAY:
   1043             case EVENT_CALL_STATE_CHANGE:
   1044                 pollCallsWhenSafe();
   1045             break;
   1046 
   1047             case EVENT_RADIO_AVAILABLE:
   1048                 handleRadioAvailable();
   1049             break;
   1050 
   1051             case EVENT_RADIO_NOT_AVAILABLE:
   1052                 handleRadioNotAvailable();
   1053             break;
   1054 
   1055             case EVENT_EXIT_ECM_RESPONSE_CDMA:
   1056                 // no matter the result, we still do the same here
   1057                 if (mPendingCallInEcm) {
   1058                     mCi.dial(mPendingMO.getAddress(), mPendingCallClirMode, obtainCompleteMessage());
   1059                     mPendingCallInEcm = false;
   1060                 }
   1061                 mPhone.unsetOnEcbModeExitResponse(this);
   1062             break;
   1063 
   1064             case EVENT_CALL_WAITING_INFO_CDMA:
   1065                ar = (AsyncResult)msg.obj;
   1066                if (ar.exception == null) {
   1067                    handleCallWaitingInfo((CdmaCallWaitingNotification)ar.result);
   1068                    Rlog.d(LOG_TAG, "Event EVENT_CALL_WAITING_INFO_CDMA Received");
   1069                }
   1070             break;
   1071 
   1072             case EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA:
   1073                 ar = (AsyncResult)msg.obj;
   1074                 if (ar.exception == null) {
   1075                     // Assume 3 way call is connected
   1076                     mPendingMO.onConnectedInOrOut();
   1077                     mPendingMO = null;
   1078                 }
   1079             break;
   1080 
   1081             case EVENT_THREE_WAY_DIAL_BLANK_FLASH:
   1082                 ar = (AsyncResult) msg.obj;
   1083                 if (ar.exception == null) {
   1084                     postDelayed(
   1085                             new Runnable() {
   1086                                 public void run() {
   1087                                     if (mPendingMO != null) {
   1088                                         mCi.sendCDMAFeatureCode(mPendingMO.getAddress(),
   1089                                                 obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA));
   1090                                     }
   1091                                 }
   1092                             }, m3WayCallFlashDelay);
   1093                 } else {
   1094                     mPendingMO = null;
   1095                     Rlog.w(LOG_TAG, "exception happened on Blank Flash for 3-way call");
   1096                 }
   1097             break;
   1098 
   1099             default:{
   1100                throw new RuntimeException("unexpected event not handled");
   1101             }
   1102         }
   1103     }
   1104 
   1105     /**
   1106      * Handle Ecm timer to be canceled or re-started
   1107      */
   1108     private void handleEcmTimer(int action) {
   1109         mPhone.handleTimerInEmergencyCallbackMode(action);
   1110         switch(action) {
   1111         case CDMAPhone.CANCEL_ECM_TIMER: mIsEcmTimerCanceled = true; break;
   1112         case CDMAPhone.RESTART_ECM_TIMER: mIsEcmTimerCanceled = false; break;
   1113         default:
   1114             Rlog.e(LOG_TAG, "handleEcmTimer, unsupported action " + action);
   1115         }
   1116     }
   1117 
   1118     /**
   1119      * Disable data call when emergency call is connected
   1120      */
   1121     private void disableDataCallInEmergencyCall(String dialString) {
   1122         if (PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString)) {
   1123             if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall");
   1124             mIsInEmergencyCall = true;
   1125             mPhone.mDcTracker.setInternalDataEnabled(false);
   1126             mPhone.notifyEmergencyCallRegistrants(true);
   1127         }
   1128     }
   1129 
   1130     /**
   1131      * Check and enable data call after an emergency call is dropped if it's
   1132      * not in ECM
   1133      */
   1134     private void checkAndEnableDataCallAfterEmergencyCallDropped() {
   1135         if (mIsInEmergencyCall) {
   1136             mIsInEmergencyCall = false;
   1137             String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
   1138             if (Phone.DEBUG_PHONE) {
   1139                 log("checkAndEnableDataCallAfterEmergencyCallDropped,inEcm=" + inEcm);
   1140             }
   1141             if (inEcm.compareTo("false") == 0) {
   1142                 // Re-initiate data connection
   1143                 mPhone.mDcTracker.setInternalDataEnabled(true);
   1144                 mPhone.notifyEmergencyCallRegistrants(false);
   1145             }
   1146         }
   1147     }
   1148 
   1149     /**
   1150      * Check the MT call to see if it's a new ring or
   1151      * a unknown connection.
   1152      */
   1153     private Connection checkMtFindNewRinging(DriverCall dc, int i) {
   1154 
   1155         Connection newRinging = null;
   1156 
   1157         // it's a ringing call
   1158         if (mConnections[i].getCall() == mRingingCall) {
   1159             newRinging = mConnections[i];
   1160             if (Phone.DEBUG_PHONE) log("Notify new ring " + dc);
   1161         } else {
   1162             // Something strange happened: a call which is neither
   1163             // a ringing call nor the one we created. It could be the
   1164             // call collision result from RIL
   1165             Rlog.e(LOG_TAG,"Phantom call appeared " + dc);
   1166             // If it's a connected call, set the connect time so that
   1167             // it's non-zero.  It may not be accurate, but at least
   1168             // it won't appear as a Missed Call.
   1169             if (dc.state != DriverCall.State.ALERTING
   1170                 && dc.state != DriverCall.State.DIALING) {
   1171                 mConnections[i].onConnectedInOrOut();
   1172                 if (dc.state == DriverCall.State.HOLDING) {
   1173                     // We've transitioned into HOLDING
   1174                     mConnections[i].onStartedHolding();
   1175                 }
   1176             }
   1177         }
   1178         return newRinging;
   1179     }
   1180 
   1181     /**
   1182      * Check if current call is in emergency call
   1183      *
   1184      * @return true if it is in emergency call
   1185      *         false if it is not in emergency call
   1186      */
   1187     boolean isInEmergencyCall() {
   1188         return mIsInEmergencyCall;
   1189     }
   1190 
   1191     @Override
   1192     protected void log(String msg) {
   1193         Rlog.d(LOG_TAG, "[CdmaCallTracker] " + msg);
   1194     }
   1195 
   1196     @Override
   1197     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1198         pw.println("GsmCallTracker extends:");
   1199         super.dump(fd, pw, args);
   1200         pw.println("droppedDuringPoll: length=" + mConnections.length);
   1201         for(int i=0; i < mConnections.length; i++) {
   1202             pw.printf(" mConnections[%d]=%s\n", i, mConnections[i]);
   1203         }
   1204         pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants);
   1205         pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants);
   1206         pw.println(" mCallWaitingRegistrants=" + mCallWaitingRegistrants);
   1207         pw.println("droppedDuringPoll: size=" + mDroppedDuringPoll.size());
   1208         for(int i = 0; i < mDroppedDuringPoll.size(); i++) {
   1209             pw.printf( " mDroppedDuringPoll[%d]=%s\n", i, mDroppedDuringPoll.get(i));
   1210         }
   1211         pw.println(" mRingingCall=" + mRingingCall);
   1212         pw.println(" mForegroundCall=" + mForegroundCall);
   1213         pw.println(" mBackgroundCall=" + mBackgroundCall);
   1214         pw.println(" mPendingMO=" + mPendingMO);
   1215         pw.println(" mHangupPendingMO=" + mHangupPendingMO);
   1216         pw.println(" mPendingCallInEcm=" + mPendingCallInEcm);
   1217         pw.println(" mIsInEmergencyCall=" + mIsInEmergencyCall);
   1218         pw.println(" mPhone=" + mPhone);
   1219         pw.println(" mDesiredMute=" + mDesiredMute);
   1220         pw.println(" mPendingCallClirMode=" + mPendingCallClirMode);
   1221         pw.println(" mState=" + mState);
   1222         pw.println(" mIsEcmTimerCanceled=" + mIsEcmTimerCanceled);
   1223     }
   1224     @Override
   1225     public PhoneConstants.State getState() {
   1226         return mState;
   1227     }
   1228 }
   1229