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