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