Home | History | Annotate | Download | only in gsm
      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.gsm;
     18 
     19 import android.os.AsyncResult;
     20 import android.os.Bundle;
     21 import android.os.Handler;
     22 import android.os.Message;
     23 import android.os.Registrant;
     24 import android.os.RegistrantList;
     25 import android.os.SystemProperties;
     26 import android.telephony.DisconnectCause;
     27 import android.telephony.PhoneNumberUtils;
     28 import android.telephony.ServiceState;
     29 import android.telephony.TelephonyManager;
     30 import android.telephony.gsm.GsmCellLocation;
     31 import android.util.EventLog;
     32 import android.telephony.Rlog;
     33 
     34 import com.android.internal.telephony.Call;
     35 import com.android.internal.telephony.CallStateException;
     36 import com.android.internal.telephony.CallTracker;
     37 import com.android.internal.telephony.CommandsInterface;
     38 import com.android.internal.telephony.Connection;
     39 import com.android.internal.telephony.DriverCall;
     40 import com.android.internal.telephony.EventLogTags;
     41 import com.android.internal.telephony.LastCallFailCause;
     42 import com.android.internal.telephony.Phone;
     43 import com.android.internal.telephony.PhoneBase;
     44 import com.android.internal.telephony.PhoneConstants;
     45 import com.android.internal.telephony.TelephonyProperties;
     46 import com.android.internal.telephony.UUSInfo;
     47 import com.android.internal.telephony.gsm.CallFailCause;
     48 import com.android.internal.telephony.gsm.GSMPhone;
     49 import com.android.internal.telephony.gsm.GsmCall;
     50 import com.android.internal.telephony.gsm.GsmConnection;
     51 import com.android.internal.telephony.imsphone.ImsPhone;
     52 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
     53 
     54 import java.io.FileDescriptor;
     55 import java.io.PrintWriter;
     56 import java.util.List;
     57 import java.util.ArrayList;
     58 
     59 /**
     60  * {@hide}
     61  */
     62 public final class GsmCallTracker extends CallTracker {
     63     static final String LOG_TAG = "GsmCallTracker";
     64     private static final boolean REPEAT_POLLING = false;
     65 
     66     private static final boolean DBG_POLL = false;
     67 
     68     //***** Constants
     69 
     70     static final int MAX_CONNECTIONS = 7;   // only 7 connections allowed in GSM
     71     static final int MAX_CONNECTIONS_PER_CALL = 5; // only 5 connections allowed per call
     72 
     73     //***** Instance Variables
     74     GsmConnection mConnections[] = new GsmConnection[MAX_CONNECTIONS];
     75     RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
     76     RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
     77 
     78 
     79     // connections dropped during last poll
     80     ArrayList<GsmConnection> mDroppedDuringPoll
     81         = new ArrayList<GsmConnection>(MAX_CONNECTIONS);
     82 
     83     GsmCall mRingingCall = new GsmCall(this);
     84             // A call that is ringing or (call) waiting
     85     GsmCall mForegroundCall = new GsmCall(this);
     86     GsmCall mBackgroundCall = new GsmCall(this);
     87 
     88     GsmConnection mPendingMO;
     89     boolean mHangupPendingMO;
     90 
     91     GSMPhone mPhone;
     92 
     93     boolean mDesiredMute = false;    // false = mute off
     94 
     95     PhoneConstants.State mState = PhoneConstants.State.IDLE;
     96 
     97     Call.SrvccState mSrvccState = Call.SrvccState.NONE;
     98 
     99     //***** Events
    100 
    101 
    102     //***** Constructors
    103 
    104     GsmCallTracker (GSMPhone phone) {
    105         this.mPhone = phone;
    106         mCi = phone.mCi;
    107 
    108         mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
    109 
    110         mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null);
    111         mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);
    112     }
    113 
    114     public void dispose() {
    115         Rlog.d(LOG_TAG, "GsmCallTracker dispose");
    116         //Unregister for all events
    117         mCi.unregisterForCallStateChanged(this);
    118         mCi.unregisterForOn(this);
    119         mCi.unregisterForNotAvailable(this);
    120 
    121 
    122         clearDisconnected();
    123     }
    124 
    125     @Override
    126     protected void finalize() {
    127         Rlog.d(LOG_TAG, "GsmCallTracker finalized");
    128     }
    129 
    130     //***** Instance Methods
    131 
    132     //***** Public Methods
    133     @Override
    134     public void registerForVoiceCallStarted(Handler h, int what, Object obj) {
    135         Registrant r = new Registrant(h, what, obj);
    136         mVoiceCallStartedRegistrants.add(r);
    137     }
    138 
    139     @Override
    140     public void unregisterForVoiceCallStarted(Handler h) {
    141         mVoiceCallStartedRegistrants.remove(h);
    142     }
    143 
    144     @Override
    145     public void registerForVoiceCallEnded(Handler h, int what, Object obj) {
    146         Registrant r = new Registrant(h, what, obj);
    147         mVoiceCallEndedRegistrants.add(r);
    148     }
    149 
    150     @Override
    151     public void unregisterForVoiceCallEnded(Handler h) {
    152         mVoiceCallEndedRegistrants.remove(h);
    153     }
    154 
    155     private void
    156     fakeHoldForegroundBeforeDial() {
    157         List<Connection> connCopy;
    158 
    159         // We need to make a copy here, since fakeHoldBeforeDial()
    160         // modifies the lists, and we don't want to reverse the order
    161         connCopy = (List<Connection>) mForegroundCall.mConnections.clone();
    162 
    163         for (int i = 0, s = connCopy.size() ; i < s ; i++) {
    164             GsmConnection conn = (GsmConnection)connCopy.get(i);
    165 
    166             conn.fakeHoldBeforeDial();
    167         }
    168     }
    169 
    170     /**
    171      * clirMode is one of the CLIR_ constants
    172      */
    173     synchronized Connection
    174     dial (String dialString, int clirMode, UUSInfo uusInfo, Bundle intentExtras)
    175             throws CallStateException {
    176         // note that this triggers call state changed notif
    177         clearDisconnected();
    178 
    179         if (!canDial()) {
    180             throw new CallStateException("cannot dial in current state");
    181         }
    182 
    183         String origNumber = dialString;
    184         dialString = convertNumberIfNecessary(mPhone, dialString);
    185 
    186         // The new call must be assigned to the foreground call.
    187         // That call must be idle, so place anything that's
    188         // there on hold
    189         if (mForegroundCall.getState() == GsmCall.State.ACTIVE) {
    190             // this will probably be done by the radio anyway
    191             // but the dial might fail before this happens
    192             // and we need to make sure the foreground call is clear
    193             // for the newly dialed connection
    194             switchWaitingOrHoldingAndActive();
    195             // This is a hack to delay DIAL so that it is sent out to RIL only after
    196             // EVENT_SWITCH_RESULT is received. We've seen failures when adding a new call to
    197             // multi-way conference calls due to DIAL being sent out before SWITCH is processed
    198             try {
    199                 Thread.sleep(500);
    200             } catch (InterruptedException e) {
    201                 // do nothing
    202             }
    203 
    204             // Fake local state so that
    205             // a) foregroundCall is empty for the newly dialed connection
    206             // b) hasNonHangupStateChanged remains false in the
    207             // next poll, so that we don't clear a failed dialing call
    208             fakeHoldForegroundBeforeDial();
    209         }
    210 
    211         if (mForegroundCall.getState() != GsmCall.State.IDLE) {
    212             //we should have failed in !canDial() above before we get here
    213             throw new CallStateException("cannot dial in current state");
    214         }
    215 
    216         mPendingMO = new GsmConnection(mPhone.getContext(), checkForTestEmergencyNumber(dialString),
    217                 this, mForegroundCall);
    218         mHangupPendingMO = false;
    219 
    220         if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
    221                 || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0
    222         ) {
    223             // Phone number is invalid
    224             mPendingMO.mCause = DisconnectCause.INVALID_NUMBER;
    225 
    226             // handlePollCalls() will notice this call not present
    227             // and will mark it as dropped.
    228             pollCallsWhenSafe();
    229         } else {
    230             // Always unmute when initiating a new call
    231             setMute(false);
    232 
    233             mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage());
    234         }
    235 
    236         if (mNumberConverted) {
    237             mPendingMO.setConverted(origNumber);
    238             mNumberConverted = false;
    239         }
    240 
    241         updatePhoneState();
    242         mPhone.notifyPreciseCallStateChanged();
    243 
    244         return mPendingMO;
    245     }
    246 
    247     Connection
    248     dial(String dialString) throws CallStateException {
    249         return dial(dialString, CommandsInterface.CLIR_DEFAULT, null);
    250     }
    251 
    252     Connection
    253     dial(String dialString, UUSInfo uusInfo, Bundle intentExtras) throws CallStateException {
    254         return dial(dialString, CommandsInterface.CLIR_DEFAULT, uusInfo, intentExtras);
    255     }
    256 
    257     Connection
    258     dial(String dialString, int clirMode, Bundle intentExtras) throws CallStateException {
    259         return dial(dialString, clirMode, null, intentExtras);
    260     }
    261 
    262     void
    263     acceptCall () throws CallStateException {
    264         // FIXME if SWITCH fails, should retry with ANSWER
    265         // in case the active/holding call disappeared and this
    266         // is no longer call waiting
    267 
    268         if (mRingingCall.getState() == GsmCall.State.INCOMING) {
    269             Rlog.i("phone", "acceptCall: incoming...");
    270             // Always unmute when answering a new call
    271             setMute(false);
    272             mCi.acceptCall(obtainCompleteMessage());
    273         } else if (mRingingCall.getState() == GsmCall.State.WAITING) {
    274             setMute(false);
    275             switchWaitingOrHoldingAndActive();
    276         } else {
    277             throw new CallStateException("phone not ringing");
    278         }
    279     }
    280 
    281     void
    282     rejectCall () throws CallStateException {
    283         // AT+CHLD=0 means "release held or UDUB"
    284         // so if the phone isn't ringing, this could hang up held
    285         if (mRingingCall.getState().isRinging()) {
    286             mCi.rejectCall(obtainCompleteMessage());
    287         } else {
    288             throw new CallStateException("phone not ringing");
    289         }
    290     }
    291 
    292     void
    293     switchWaitingOrHoldingAndActive() throws CallStateException {
    294         // Should we bother with this check?
    295         if (mRingingCall.getState() == GsmCall.State.INCOMING) {
    296             throw new CallStateException("cannot be in the incoming state");
    297         } else {
    298             mCi.switchWaitingOrHoldingAndActive(
    299                     obtainCompleteMessage(EVENT_SWITCH_RESULT));
    300         }
    301     }
    302 
    303     void
    304     conference() {
    305         mCi.conference(obtainCompleteMessage(EVENT_CONFERENCE_RESULT));
    306     }
    307 
    308     void
    309     explicitCallTransfer() {
    310         mCi.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT));
    311     }
    312 
    313     void
    314     clearDisconnected() {
    315         internalClearDisconnected();
    316 
    317         updatePhoneState();
    318         mPhone.notifyPreciseCallStateChanged();
    319     }
    320 
    321     boolean
    322     canConference() {
    323         return mForegroundCall.getState() == GsmCall.State.ACTIVE
    324                 && mBackgroundCall.getState() == GsmCall.State.HOLDING
    325                 && !mBackgroundCall.isFull()
    326                 && !mForegroundCall.isFull();
    327     }
    328 
    329     boolean
    330     canDial() {
    331         boolean ret;
    332         int serviceState = mPhone.getServiceState().getState();
    333         String disableCall = SystemProperties.get(
    334                 TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
    335 
    336         ret = (serviceState != ServiceState.STATE_POWER_OFF)
    337                 && mPendingMO == null
    338                 && !mRingingCall.isRinging()
    339                 && !disableCall.equals("true")
    340                 && (!mForegroundCall.getState().isAlive()
    341                     || !mBackgroundCall.getState().isAlive());
    342 
    343         return ret;
    344     }
    345 
    346     boolean
    347     canTransfer() {
    348         return (mForegroundCall.getState() == GsmCall.State.ACTIVE
    349                 || mForegroundCall.getState() == GsmCall.State.ALERTING
    350                 || mForegroundCall.getState() == GsmCall.State.DIALING)
    351             && mBackgroundCall.getState() == GsmCall.State.HOLDING;
    352     }
    353 
    354     //***** Private Instance Methods
    355 
    356     private void
    357     internalClearDisconnected() {
    358         mRingingCall.clearDisconnected();
    359         mForegroundCall.clearDisconnected();
    360         mBackgroundCall.clearDisconnected();
    361     }
    362 
    363     /**
    364      * Obtain a message to use for signalling "invoke getCurrentCalls() when
    365      * this operation and all other pending operations are complete
    366      */
    367     private Message
    368     obtainCompleteMessage() {
    369         return obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
    370     }
    371 
    372     /**
    373      * Obtain a message to use for signalling "invoke getCurrentCalls() when
    374      * this operation and all other pending operations are complete
    375      */
    376     private Message
    377     obtainCompleteMessage(int what) {
    378         mPendingOperations++;
    379         mLastRelevantPoll = null;
    380         mNeedsPoll = true;
    381 
    382         if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" +
    383                 mPendingOperations + ", needsPoll=" + mNeedsPoll);
    384 
    385         return obtainMessage(what);
    386     }
    387 
    388     private void
    389     operationComplete() {
    390         mPendingOperations--;
    391 
    392         if (DBG_POLL) log("operationComplete: pendingOperations=" +
    393                 mPendingOperations + ", needsPoll=" + mNeedsPoll);
    394 
    395         if (mPendingOperations == 0 && mNeedsPoll) {
    396             mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
    397             mCi.getCurrentCalls(mLastRelevantPoll);
    398         } else if (mPendingOperations < 0) {
    399             // this should never happen
    400             Rlog.e(LOG_TAG,"GsmCallTracker.pendingOperations < 0");
    401             mPendingOperations = 0;
    402         }
    403     }
    404 
    405     private void
    406     updatePhoneState() {
    407         PhoneConstants.State oldState = mState;
    408         if (mRingingCall.isRinging()) {
    409             mState = PhoneConstants.State.RINGING;
    410         } else if (mPendingMO != null ||
    411                 !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
    412             mState = PhoneConstants.State.OFFHOOK;
    413         } else {
    414             ImsPhone imsPhone = (ImsPhone)mPhone.getImsPhone();
    415             if ( mState == PhoneConstants.State.OFFHOOK && (imsPhone != null)){
    416                 imsPhone.callEndCleanupHandOverCallIfAny();
    417             }
    418             mState = PhoneConstants.State.IDLE;
    419         }
    420 
    421         if (mState == PhoneConstants.State.IDLE && oldState != mState) {
    422             mVoiceCallEndedRegistrants.notifyRegistrants(
    423                 new AsyncResult(null, null, null));
    424         } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
    425             mVoiceCallStartedRegistrants.notifyRegistrants (
    426                     new AsyncResult(null, null, null));
    427         }
    428 
    429         if (mState != oldState) {
    430             mPhone.notifyPhoneStateChanged();
    431         }
    432     }
    433 
    434     @Override
    435     protected synchronized void
    436     handlePollCalls(AsyncResult ar) {
    437         List polledCalls;
    438 
    439         if (ar.exception == null) {
    440             polledCalls = (List)ar.result;
    441         } else if (isCommandExceptionRadioNotAvailable(ar.exception)) {
    442             // just a dummy empty ArrayList to cause the loop
    443             // to hang up all the calls
    444             polledCalls = new ArrayList();
    445         } else {
    446             // Radio probably wasn't ready--try again in a bit
    447             // But don't keep polling if the channel is closed
    448             pollCallsAfterDelay();
    449             return;
    450         }
    451 
    452         Connection newRinging = null; //or waiting
    453         Connection newUnknown = null;
    454         boolean hasNonHangupStateChanged = false;   // Any change besides
    455                                                     // a dropped connection
    456         boolean hasAnyCallDisconnected = false;
    457         boolean needsPollDelay = false;
    458         boolean unknownConnectionAppeared = false;
    459 
    460         for (int i = 0, curDC = 0, dcSize = polledCalls.size()
    461                 ; i < mConnections.length; i++) {
    462             GsmConnection conn = mConnections[i];
    463             DriverCall dc = null;
    464 
    465             // polledCall list is sparse
    466             if (curDC < dcSize) {
    467                 dc = (DriverCall) polledCalls.get(curDC);
    468 
    469                 if (dc.index == i+1) {
    470                     curDC++;
    471                 } else {
    472                     dc = null;
    473                 }
    474             }
    475 
    476             if (DBG_POLL) log("poll: conn[i=" + i + "]=" +
    477                     conn+", dc=" + dc);
    478 
    479             if (conn == null && dc != null) {
    480                 // Connection appeared in CLCC response that we don't know about
    481                 if (mPendingMO != null && mPendingMO.compareTo(dc)) {
    482 
    483                     if (DBG_POLL) log("poll: pendingMO=" + mPendingMO);
    484 
    485                     // It's our pending mobile originating call
    486                     mConnections[i] = mPendingMO;
    487                     mPendingMO.mIndex = i;
    488                     mPendingMO.update(dc);
    489                     mPendingMO = null;
    490 
    491                     // Someone has already asked to hangup this call
    492                     if (mHangupPendingMO) {
    493                         mHangupPendingMO = false;
    494                         try {
    495                             if (Phone.DEBUG_PHONE) log(
    496                                     "poll: hangupPendingMO, hangup conn " + i);
    497                             hangup(mConnections[i]);
    498                         } catch (CallStateException ex) {
    499                             Rlog.e(LOG_TAG, "unexpected error on hangup");
    500                         }
    501 
    502                         // Do not continue processing this poll
    503                         // Wait for hangup and repoll
    504                         return;
    505                     }
    506                 } else {
    507                     mConnections[i] = new GsmConnection(mPhone.getContext(), dc, this, i);
    508 
    509                     Connection hoConnection = getHoConnection(dc);
    510                     if (hoConnection != null) {
    511                         // Single Radio Voice Call Continuity (SRVCC) completed
    512                         mConnections[i].migrateFrom(hoConnection);
    513                         if (!hoConnection.isMultiparty()) {
    514                             // Remove only if it is not multiparty
    515                             mHandoverConnections.remove(hoConnection);
    516                         }
    517                         mPhone.notifyHandoverStateChanged(mConnections[i]);
    518                     } else if ( mConnections[i].getCall() == mRingingCall ) { // it's a ringing call
    519                         newRinging = mConnections[i];
    520                     } else {
    521                         // Something strange happened: a call appeared
    522                         // which is neither a ringing call or one we created.
    523                         // Either we've crashed and re-attached to an existing
    524                         // call, or something else (eg, SIM) initiated the call.
    525 
    526                         Rlog.i(LOG_TAG,"Phantom call appeared " + dc);
    527 
    528                         // If it's a connected call, set the connect time so that
    529                         // it's non-zero.  It may not be accurate, but at least
    530                         // it won't appear as a Missed Call.
    531                         if (dc.state != DriverCall.State.ALERTING
    532                                 && dc.state != DriverCall.State.DIALING) {
    533                             mConnections[i].onConnectedInOrOut();
    534                             if (dc.state == DriverCall.State.HOLDING) {
    535                                 // We've transitioned into HOLDING
    536                                 mConnections[i].onStartedHolding();
    537                             }
    538                         }
    539 
    540                         newUnknown = mConnections[i];
    541 
    542                         unknownConnectionAppeared = true;
    543                     }
    544                 }
    545                 hasNonHangupStateChanged = true;
    546             } else if (conn != null && dc == null) {
    547                 // Connection missing in CLCC response that we were
    548                 // tracking.
    549                 mDroppedDuringPoll.add(conn);
    550                 // Dropped connections are removed from the CallTracker
    551                 // list but kept in the GsmCall list
    552                 mConnections[i] = null;
    553             } else if (conn != null && dc != null && !conn.compareTo(dc)) {
    554                 // Connection in CLCC response does not match what
    555                 // we were tracking. Assume dropped call and new call
    556 
    557                 mDroppedDuringPoll.add(conn);
    558                 mConnections[i] = new GsmConnection (mPhone.getContext(), dc, this, i);
    559 
    560                 if (mConnections[i].getCall() == mRingingCall) {
    561                     newRinging = mConnections[i];
    562                 } // else something strange happened
    563                 hasNonHangupStateChanged = true;
    564             } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
    565                 boolean changed;
    566                 changed = conn.update(dc);
    567                 hasNonHangupStateChanged = hasNonHangupStateChanged || changed;
    568             }
    569 
    570             if (REPEAT_POLLING) {
    571                 if (dc != null) {
    572                     // FIXME with RIL, we should not need this anymore
    573                     if ((dc.state == DriverCall.State.DIALING
    574                             /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/)
    575                         || (dc.state == DriverCall.State.ALERTING
    576                             /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/)
    577                         || (dc.state == DriverCall.State.INCOMING
    578                             /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/)
    579                         || (dc.state == DriverCall.State.WAITING
    580                             /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/)
    581                     ) {
    582                         // Sometimes there's no unsolicited notification
    583                         // for state transitions
    584                         needsPollDelay = true;
    585                     }
    586                 }
    587             }
    588         }
    589 
    590         // This is the first poll after an ATD.
    591         // We expect the pending call to appear in the list
    592         // If it does not, we land here
    593         if (mPendingMO != null) {
    594             Rlog.d(LOG_TAG,"Pending MO dropped before poll fg state:"
    595                             + mForegroundCall.getState());
    596 
    597             mDroppedDuringPoll.add(mPendingMO);
    598             mPendingMO = null;
    599             mHangupPendingMO = false;
    600         }
    601 
    602         if (newRinging != null) {
    603             mPhone.notifyNewRingingConnection(newRinging);
    604         }
    605 
    606         // clear the "local hangup" and "missed/rejected call"
    607         // cases from the "dropped during poll" list
    608         // These cases need no "last call fail" reason
    609         for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
    610             GsmConnection conn = mDroppedDuringPoll.get(i);
    611 
    612             if (conn.isIncoming() && conn.getConnectTime() == 0) {
    613                 // Missed or rejected call
    614                 int cause;
    615                 if (conn.mCause == DisconnectCause.LOCAL) {
    616                     cause = DisconnectCause.INCOMING_REJECTED;
    617                 } else {
    618                     cause = DisconnectCause.INCOMING_MISSED;
    619                 }
    620 
    621                 if (Phone.DEBUG_PHONE) {
    622                     log("missed/rejected call, conn.cause=" + conn.mCause);
    623                     log("setting cause to " + cause);
    624                 }
    625                 mDroppedDuringPoll.remove(i);
    626                 hasAnyCallDisconnected |= conn.onDisconnect(cause);
    627             } else if (conn.mCause == DisconnectCause.LOCAL
    628                     || conn.mCause == DisconnectCause.INVALID_NUMBER) {
    629                 mDroppedDuringPoll.remove(i);
    630                 hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
    631             }
    632         }
    633 
    634         /* Disconnect any pending Handover connections */
    635         for (Connection hoConnection : mHandoverConnections) {
    636             log("handlePollCalls - disconnect hoConn= " + hoConnection.toString());
    637             ((ImsPhoneConnection)hoConnection).onDisconnect(DisconnectCause.NOT_VALID);
    638             mHandoverConnections.remove(hoConnection);
    639         }
    640 
    641         // Any non-local disconnects: determine cause
    642         if (mDroppedDuringPoll.size() > 0) {
    643             mCi.getLastCallFailCause(
    644                 obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
    645         }
    646 
    647         if (needsPollDelay) {
    648             pollCallsAfterDelay();
    649         }
    650 
    651         // Cases when we can no longer keep disconnected Connection's
    652         // with their previous calls
    653         // 1) the phone has started to ring
    654         // 2) A Call/Connection object has changed state...
    655         //    we may have switched or held or answered (but not hung up)
    656         if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) {
    657             internalClearDisconnected();
    658         }
    659 
    660         updatePhoneState();
    661 
    662         if (unknownConnectionAppeared) {
    663             mPhone.notifyUnknownConnection(newUnknown);
    664         }
    665 
    666         if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
    667             mPhone.notifyPreciseCallStateChanged();
    668         }
    669 
    670         //dumpState();
    671     }
    672 
    673     private void
    674     handleRadioNotAvailable() {
    675         // handlePollCalls will clear out its
    676         // call list when it gets the CommandException
    677         // error result from this
    678         pollCallsWhenSafe();
    679     }
    680 
    681     private void
    682     dumpState() {
    683         List l;
    684 
    685         Rlog.i(LOG_TAG,"Phone State:" + mState);
    686 
    687         Rlog.i(LOG_TAG,"Ringing call: " + mRingingCall.toString());
    688 
    689         l = mRingingCall.getConnections();
    690         for (int i = 0, s = l.size(); i < s; i++) {
    691             Rlog.i(LOG_TAG,l.get(i).toString());
    692         }
    693 
    694         Rlog.i(LOG_TAG,"Foreground call: " + mForegroundCall.toString());
    695 
    696         l = mForegroundCall.getConnections();
    697         for (int i = 0, s = l.size(); i < s; i++) {
    698             Rlog.i(LOG_TAG,l.get(i).toString());
    699         }
    700 
    701         Rlog.i(LOG_TAG,"Background call: " + mBackgroundCall.toString());
    702 
    703         l = mBackgroundCall.getConnections();
    704         for (int i = 0, s = l.size(); i < s; i++) {
    705             Rlog.i(LOG_TAG,l.get(i).toString());
    706         }
    707 
    708     }
    709 
    710     //***** Called from GsmConnection
    711 
    712     /*package*/ void
    713     hangup (GsmConnection conn) throws CallStateException {
    714         if (conn.mOwner != this) {
    715             throw new CallStateException ("GsmConnection " + conn
    716                                     + "does not belong to GsmCallTracker " + this);
    717         }
    718 
    719         if (conn == mPendingMO) {
    720             // We're hanging up an outgoing call that doesn't have it's
    721             // GSM index assigned yet
    722 
    723             if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true");
    724             mHangupPendingMO = true;
    725         } else {
    726             try {
    727                 mCi.hangupConnection (conn.getGSMIndex(), obtainCompleteMessage());
    728             } catch (CallStateException ex) {
    729                 // Ignore "connection not found"
    730                 // Call may have hung up already
    731                 Rlog.w(LOG_TAG,"GsmCallTracker WARN: hangup() on absent connection "
    732                                 + conn);
    733             }
    734         }
    735 
    736         conn.onHangupLocal();
    737     }
    738 
    739     /*package*/ void
    740     separate (GsmConnection conn) throws CallStateException {
    741         if (conn.mOwner != this) {
    742             throw new CallStateException ("GsmConnection " + conn
    743                                     + "does not belong to GsmCallTracker " + this);
    744         }
    745         try {
    746             mCi.separateConnection (conn.getGSMIndex(),
    747                 obtainCompleteMessage(EVENT_SEPARATE_RESULT));
    748         } catch (CallStateException ex) {
    749             // Ignore "connection not found"
    750             // Call may have hung up already
    751             Rlog.w(LOG_TAG,"GsmCallTracker WARN: separate() on absent connection "
    752                           + conn);
    753         }
    754     }
    755 
    756     //***** Called from GSMPhone
    757 
    758     /*package*/ void
    759     setMute(boolean mute) {
    760         mDesiredMute = mute;
    761         mCi.setMute(mDesiredMute, null);
    762     }
    763 
    764     /*package*/ boolean
    765     getMute() {
    766         return mDesiredMute;
    767     }
    768 
    769 
    770     //***** Called from GsmCall
    771 
    772     /* package */ void
    773     hangup (GsmCall call) throws CallStateException {
    774         if (call.getConnections().size() == 0) {
    775             throw new CallStateException("no connections in call");
    776         }
    777 
    778         if (call == mRingingCall) {
    779             if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background");
    780             mCi.hangupWaitingOrBackground(obtainCompleteMessage());
    781         } else if (call == mForegroundCall) {
    782             if (call.isDialingOrAlerting()) {
    783                 if (Phone.DEBUG_PHONE) {
    784                     log("(foregnd) hangup dialing or alerting...");
    785                 }
    786                 hangup((GsmConnection)(call.getConnections().get(0)));
    787             } else if (mRingingCall.isRinging()) {
    788                 // Do not auto-answer ringing on CHUP, instead just end active calls
    789                 log("hangup all conns in active/background call, without affecting ringing call");
    790                 hangupAllConnections(call);
    791             } else {
    792                 hangupForegroundResumeBackground();
    793             }
    794         } else if (call == mBackgroundCall) {
    795             if (mRingingCall.isRinging()) {
    796                 if (Phone.DEBUG_PHONE) {
    797                     log("hangup all conns in background call");
    798                 }
    799                 hangupAllConnections(call);
    800             } else {
    801                 hangupWaitingOrBackground();
    802             }
    803         } else {
    804             throw new RuntimeException ("GsmCall " + call +
    805                     "does not belong to GsmCallTracker " + this);
    806         }
    807 
    808         call.onHangupLocal();
    809         mPhone.notifyPreciseCallStateChanged();
    810     }
    811 
    812     /* package */
    813     void hangupWaitingOrBackground() {
    814         if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground");
    815         mCi.hangupWaitingOrBackground(obtainCompleteMessage());
    816     }
    817 
    818     /* package */
    819     void hangupForegroundResumeBackground() {
    820         if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground");
    821         mCi.hangupForegroundResumeBackground(obtainCompleteMessage());
    822     }
    823 
    824     void hangupConnectionByIndex(GsmCall call, int index)
    825             throws CallStateException {
    826         int count = call.mConnections.size();
    827         for (int i = 0; i < count; i++) {
    828             GsmConnection cn = (GsmConnection)call.mConnections.get(i);
    829             if (cn.getGSMIndex() == index) {
    830                 mCi.hangupConnection(index, obtainCompleteMessage());
    831                 return;
    832             }
    833         }
    834 
    835         throw new CallStateException("no gsm index found");
    836     }
    837 
    838     void hangupAllConnections(GsmCall call) {
    839         try {
    840             int count = call.mConnections.size();
    841             for (int i = 0; i < count; i++) {
    842                 GsmConnection cn = (GsmConnection)call.mConnections.get(i);
    843                 mCi.hangupConnection(cn.getGSMIndex(), obtainCompleteMessage());
    844             }
    845         } catch (CallStateException ex) {
    846             Rlog.e(LOG_TAG, "hangupConnectionByIndex caught " + ex);
    847         }
    848     }
    849 
    850     /* package */
    851     GsmConnection getConnectionByIndex(GsmCall call, int index)
    852             throws CallStateException {
    853         int count = call.mConnections.size();
    854         for (int i = 0; i < count; i++) {
    855             GsmConnection cn = (GsmConnection)call.mConnections.get(i);
    856             if (cn.getGSMIndex() == index) {
    857                 return cn;
    858             }
    859         }
    860 
    861         return null;
    862     }
    863 
    864     private Phone.SuppService getFailedService(int what) {
    865         switch (what) {
    866             case EVENT_SWITCH_RESULT:
    867                 return Phone.SuppService.SWITCH;
    868             case EVENT_CONFERENCE_RESULT:
    869                 return Phone.SuppService.CONFERENCE;
    870             case EVENT_SEPARATE_RESULT:
    871                 return Phone.SuppService.SEPARATE;
    872             case EVENT_ECT_RESULT:
    873                 return Phone.SuppService.TRANSFER;
    874         }
    875         return Phone.SuppService.UNKNOWN;
    876     }
    877 
    878     //****** Overridden from Handler
    879 
    880     @Override
    881     public void
    882     handleMessage (Message msg) {
    883         AsyncResult ar;
    884 
    885         if (!mPhone.mIsTheCurrentActivePhone) {
    886             Rlog.e(LOG_TAG, "Received message " + msg +
    887                     "[" + msg.what + "] while being destroyed. Ignoring.");
    888             return;
    889         }
    890         switch (msg.what) {
    891             case EVENT_POLL_CALLS_RESULT:
    892                 ar = (AsyncResult)msg.obj;
    893 
    894                 if (msg == mLastRelevantPoll) {
    895                     if (DBG_POLL) log(
    896                             "handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
    897                     mNeedsPoll = false;
    898                     mLastRelevantPoll = null;
    899                     handlePollCalls((AsyncResult)msg.obj);
    900                 }
    901             break;
    902 
    903             case EVENT_OPERATION_COMPLETE:
    904                 ar = (AsyncResult)msg.obj;
    905                 operationComplete();
    906             break;
    907 
    908             case EVENT_SWITCH_RESULT:
    909             case EVENT_CONFERENCE_RESULT:
    910             case EVENT_SEPARATE_RESULT:
    911             case EVENT_ECT_RESULT:
    912                 ar = (AsyncResult)msg.obj;
    913                 if (ar.exception != null) {
    914                     mPhone.notifySuppServiceFailed(getFailedService(msg.what));
    915                 }
    916                 operationComplete();
    917             break;
    918 
    919             case EVENT_GET_LAST_CALL_FAIL_CAUSE:
    920                 int causeCode;
    921                 String vendorCause = null;
    922                 ar = (AsyncResult)msg.obj;
    923 
    924                 operationComplete();
    925 
    926                 if (ar.exception != null) {
    927                     // An exception occurred...just treat the disconnect
    928                     // cause as "normal"
    929                     causeCode = CallFailCause.NORMAL_CLEARING;
    930                     Rlog.i(LOG_TAG,
    931                             "Exception during getLastCallFailCause, assuming normal disconnect");
    932                 } else {
    933                     LastCallFailCause failCause = (LastCallFailCause)ar.result;
    934                     causeCode = failCause.causeCode;
    935                     vendorCause = failCause.vendorCause;
    936                 }
    937                 // Log the causeCode if its not normal
    938                 if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL ||
    939                     causeCode == CallFailCause.TEMPORARY_FAILURE ||
    940                     causeCode == CallFailCause.SWITCHING_CONGESTION ||
    941                     causeCode == CallFailCause.CHANNEL_NOT_AVAIL ||
    942                     causeCode == CallFailCause.QOS_NOT_AVAIL ||
    943                     causeCode == CallFailCause.BEARER_NOT_AVAIL ||
    944                     causeCode == CallFailCause.ERROR_UNSPECIFIED) {
    945                     GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
    946                     EventLog.writeEvent(EventLogTags.CALL_DROP,
    947                             causeCode, loc != null ? loc.getCid() : -1,
    948                             TelephonyManager.getDefault().getNetworkType());
    949                 }
    950 
    951                 for (int i = 0, s =  mDroppedDuringPoll.size()
    952                         ; i < s ; i++
    953                 ) {
    954                     GsmConnection conn = mDroppedDuringPoll.get(i);
    955 
    956                     conn.onRemoteDisconnect(causeCode, vendorCause);
    957                 }
    958 
    959                 updatePhoneState();
    960 
    961                 mPhone.notifyPreciseCallStateChanged();
    962                 mDroppedDuringPoll.clear();
    963             break;
    964 
    965             case EVENT_REPOLL_AFTER_DELAY:
    966             case EVENT_CALL_STATE_CHANGE:
    967                 pollCallsWhenSafe();
    968             break;
    969 
    970             case EVENT_RADIO_AVAILABLE:
    971                 handleRadioAvailable();
    972             break;
    973 
    974             case EVENT_RADIO_NOT_AVAILABLE:
    975                 handleRadioNotAvailable();
    976             break;
    977         }
    978     }
    979 
    980     @Override
    981     protected void log(String msg) {
    982         Rlog.d(LOG_TAG, "[GsmCallTracker] " + msg);
    983     }
    984 
    985     @Override
    986     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    987         pw.println("GsmCallTracker extends:");
    988         super.dump(fd, pw, args);
    989         pw.println("mConnections: length=" + mConnections.length);
    990         for(int i=0; i < mConnections.length; i++) {
    991             pw.printf("  mConnections[%d]=%s\n", i, mConnections[i]);
    992         }
    993         pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants);
    994         pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants);
    995         pw.println(" mDroppedDuringPoll: size=" + mDroppedDuringPoll.size());
    996         for(int i = 0; i < mDroppedDuringPoll.size(); i++) {
    997             pw.printf( "  mDroppedDuringPoll[%d]=%s\n", i, mDroppedDuringPoll.get(i));
    998         }
    999         pw.println(" mRingingCall=" + mRingingCall);
   1000         pw.println(" mForegroundCall=" + mForegroundCall);
   1001         pw.println(" mBackgroundCall=" + mBackgroundCall);
   1002         pw.println(" mPendingMO=" + mPendingMO);
   1003         pw.println(" mHangupPendingMO=" + mHangupPendingMO);
   1004         pw.println(" mPhone=" + mPhone);
   1005         pw.println(" mDesiredMute=" + mDesiredMute);
   1006         pw.println(" mState=" + mState);
   1007     }
   1008     @Override
   1009     public PhoneConstants.State getState() {
   1010         return mState;
   1011     }
   1012 }
   1013