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