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