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