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