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