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