GsmCallTracker.java revision cd17a8e56bf5fa1af49d4447e65dc69e4741e7bf
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.PhoneNumberUtils;
26import android.telephony.ServiceState;
27import android.telephony.TelephonyManager;
28import android.telephony.gsm.GsmCellLocation;
29import android.util.EventLog;
30import android.telephony.Rlog;
31
32import com.android.internal.telephony.CallStateException;
33import com.android.internal.telephony.CallTracker;
34import com.android.internal.telephony.CommandsInterface;
35import com.android.internal.telephony.Connection;
36import com.android.internal.telephony.DriverCall;
37import com.android.internal.telephony.EventLogTags;
38import com.android.internal.telephony.Phone;
39import com.android.internal.telephony.PhoneConstants;
40import com.android.internal.telephony.TelephonyProperties;
41import com.android.internal.telephony.UUSInfo;
42import com.android.internal.telephony.gsm.CallFailCause;
43import com.android.internal.telephony.gsm.GSMPhone;
44import com.android.internal.telephony.gsm.GsmCall;
45import com.android.internal.telephony.gsm.GsmConnection;
46
47import java.io.FileDescriptor;
48import java.io.PrintWriter;
49import java.util.List;
50import java.util.ArrayList;
51
52/**
53 * {@hide}
54 */
55public final class GsmCallTracker extends CallTracker {
56    static final String LOG_TAG = "GSM";
57    private static final boolean REPEAT_POLLING = false;
58
59    private static final boolean DBG_POLL = false;
60
61    //***** Constants
62
63    static final int MAX_CONNECTIONS = 7;   // only 7 connections allowed in GSM
64    static final int MAX_CONNECTIONS_PER_CALL = 5; // only 5 connections allowed per call
65
66    //***** Instance Variables
67    GsmConnection connections[] = new GsmConnection[MAX_CONNECTIONS];
68    RegistrantList voiceCallEndedRegistrants = new RegistrantList();
69    RegistrantList voiceCallStartedRegistrants = new RegistrantList();
70
71
72    // connections dropped during last poll
73    ArrayList<GsmConnection> droppedDuringPoll
74        = new ArrayList<GsmConnection>(MAX_CONNECTIONS);
75
76    GsmCall ringingCall = new GsmCall(this);
77            // A call that is ringing or (call) waiting
78    GsmCall foregroundCall = new GsmCall(this);
79    GsmCall backgroundCall = new GsmCall(this);
80
81    GsmConnection pendingMO;
82    boolean hangupPendingMO;
83
84    GSMPhone phone;
85
86    boolean desiredMute = false;    // false = mute off
87
88    PhoneConstants.State state = PhoneConstants.State.IDLE;
89
90
91
92    //***** Events
93
94
95    //***** Constructors
96
97    GsmCallTracker (GSMPhone phone) {
98        this.phone = phone;
99        cm = phone.mCM;
100
101        cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
102
103        cm.registerForOn(this, EVENT_RADIO_AVAILABLE, null);
104        cm.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);
105    }
106
107    public void dispose() {
108        //Unregister for all events
109        cm.unregisterForCallStateChanged(this);
110        cm.unregisterForOn(this);
111        cm.unregisterForNotAvailable(this);
112
113        for(GsmConnection c : connections) {
114            try {
115                if(c != null) hangup(c);
116            } catch (CallStateException ex) {
117                Rlog.e(LOG_TAG, "unexpected error on hangup during dispose");
118            }
119        }
120
121        try {
122            if(pendingMO != null) hangup(pendingMO);
123        } catch (CallStateException ex) {
124            Rlog.e(LOG_TAG, "unexpected error on hangup during dispose");
125        }
126
127        clearDisconnected();
128    }
129
130    protected void finalize() {
131        Rlog.d(LOG_TAG, "GsmCallTracker finalized");
132    }
133
134    //***** Instance Methods
135
136    //***** Public Methods
137    public void registerForVoiceCallStarted(Handler h, int what, Object obj) {
138        Registrant r = new Registrant(h, what, obj);
139        voiceCallStartedRegistrants.add(r);
140    }
141
142    public void unregisterForVoiceCallStarted(Handler h) {
143        voiceCallStartedRegistrants.remove(h);
144    }
145
146    public void registerForVoiceCallEnded(Handler h, int what, Object obj) {
147        Registrant r = new Registrant(h, what, obj);
148        voiceCallEndedRegistrants.add(r);
149    }
150
151    public void unregisterForVoiceCallEnded(Handler h) {
152        voiceCallEndedRegistrants.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>) foregroundCall.connections.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) throws CallStateException {
175        // note that this triggers call state changed notif
176        clearDisconnected();
177
178        if (!canDial()) {
179            throw new CallStateException("cannot dial in current state");
180        }
181
182        // The new call must be assigned to the foreground call.
183        // That call must be idle, so place anything that's
184        // there on hold
185        if (foregroundCall.getState() == GsmCall.State.ACTIVE) {
186            // this will probably be done by the radio anyway
187            // but the dial might fail before this happens
188            // and we need to make sure the foreground call is clear
189            // for the newly dialed connection
190            switchWaitingOrHoldingAndActive();
191
192            // Fake local state so that
193            // a) foregroundCall is empty for the newly dialed connection
194            // b) hasNonHangupStateChanged remains false in the
195            // next poll, so that we don't clear a failed dialing call
196            fakeHoldForegroundBeforeDial();
197        }
198
199        if (foregroundCall.getState() != GsmCall.State.IDLE) {
200            //we should have failed in !canDial() above before we get here
201            throw new CallStateException("cannot dial in current state");
202        }
203
204        pendingMO = new GsmConnection(phone.getContext(), checkForTestEmergencyNumber(dialString),
205                this, foregroundCall);
206        hangupPendingMO = false;
207
208        if (pendingMO.address == null || pendingMO.address.length() == 0
209            || pendingMO.address.indexOf(PhoneNumberUtils.WILD) >= 0
210        ) {
211            // Phone number is invalid
212            pendingMO.cause = Connection.DisconnectCause.INVALID_NUMBER;
213
214            // handlePollCalls() will notice this call not present
215            // and will mark it as dropped.
216            pollCallsWhenSafe();
217        } else {
218            // Always unmute when initiating a new call
219            setMute(false);
220
221            cm.dial(pendingMO.address, clirMode, uusInfo, obtainCompleteMessage());
222        }
223
224        updatePhoneState();
225        phone.notifyPreciseCallStateChanged();
226
227        return pendingMO;
228    }
229
230    Connection
231    dial(String dialString) throws CallStateException {
232        return dial(dialString, CommandsInterface.CLIR_DEFAULT, null);
233    }
234
235    Connection
236    dial(String dialString, UUSInfo uusInfo) throws CallStateException {
237        return dial(dialString, CommandsInterface.CLIR_DEFAULT, uusInfo);
238    }
239
240    Connection
241    dial(String dialString, int clirMode) throws CallStateException {
242        return dial(dialString, clirMode, null);
243    }
244
245    void
246    acceptCall () throws CallStateException {
247        // FIXME if SWITCH fails, should retry with ANSWER
248        // in case the active/holding call disappeared and this
249        // is no longer call waiting
250
251        if (ringingCall.getState() == GsmCall.State.INCOMING) {
252            Rlog.i("phone", "acceptCall: incoming...");
253            // Always unmute when answering a new call
254            setMute(false);
255            cm.acceptCall(obtainCompleteMessage());
256        } else if (ringingCall.getState() == GsmCall.State.WAITING) {
257            setMute(false);
258            switchWaitingOrHoldingAndActive();
259        } else {
260            throw new CallStateException("phone not ringing");
261        }
262    }
263
264    void
265    rejectCall () throws CallStateException {
266        // AT+CHLD=0 means "release held or UDUB"
267        // so if the phone isn't ringing, this could hang up held
268        if (ringingCall.getState().isRinging()) {
269            cm.rejectCall(obtainCompleteMessage());
270        } else {
271            throw new CallStateException("phone not ringing");
272        }
273    }
274
275    void
276    switchWaitingOrHoldingAndActive() throws CallStateException {
277        // Should we bother with this check?
278        if (ringingCall.getState() == GsmCall.State.INCOMING) {
279            throw new CallStateException("cannot be in the incoming state");
280        } else {
281            cm.switchWaitingOrHoldingAndActive(
282                    obtainCompleteMessage(EVENT_SWITCH_RESULT));
283        }
284    }
285
286    void
287    conference() throws CallStateException {
288        cm.conference(obtainCompleteMessage(EVENT_CONFERENCE_RESULT));
289    }
290
291    void
292    explicitCallTransfer() throws CallStateException {
293        cm.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT));
294    }
295
296    void
297    clearDisconnected() {
298        internalClearDisconnected();
299
300        updatePhoneState();
301        phone.notifyPreciseCallStateChanged();
302    }
303
304    boolean
305    canConference() {
306        return foregroundCall.getState() == GsmCall.State.ACTIVE
307                && backgroundCall.getState() == GsmCall.State.HOLDING
308                && !backgroundCall.isFull()
309                && !foregroundCall.isFull();
310    }
311
312    boolean
313    canDial() {
314        boolean ret;
315        int serviceState = phone.getServiceState().getState();
316        String disableCall = SystemProperties.get(
317                TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
318
319        ret = (serviceState != ServiceState.STATE_POWER_OFF)
320                && pendingMO == null
321                && !ringingCall.isRinging()
322                && !disableCall.equals("true")
323                && (!foregroundCall.getState().isAlive()
324                    || !backgroundCall.getState().isAlive());
325
326        return ret;
327    }
328
329    boolean
330    canTransfer() {
331        return foregroundCall.getState() == GsmCall.State.ACTIVE
332                && backgroundCall.getState() == GsmCall.State.HOLDING;
333    }
334
335    //***** Private Instance Methods
336
337    private void
338    internalClearDisconnected() {
339        ringingCall.clearDisconnected();
340        foregroundCall.clearDisconnected();
341        backgroundCall.clearDisconnected();
342    }
343
344    /**
345     * Obtain a message to use for signalling "invoke getCurrentCalls() when
346     * this operation and all other pending operations are complete
347     */
348    private Message
349    obtainCompleteMessage() {
350        return obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
351    }
352
353    /**
354     * Obtain a message to use for signalling "invoke getCurrentCalls() when
355     * this operation and all other pending operations are complete
356     */
357    private Message
358    obtainCompleteMessage(int what) {
359        pendingOperations++;
360        lastRelevantPoll = null;
361        needsPoll = true;
362
363        if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" +
364                pendingOperations + ", needsPoll=" + needsPoll);
365
366        return obtainMessage(what);
367    }
368
369    private void
370    operationComplete() {
371        pendingOperations--;
372
373        if (DBG_POLL) log("operationComplete: pendingOperations=" +
374                pendingOperations + ", needsPoll=" + needsPoll);
375
376        if (pendingOperations == 0 && needsPoll) {
377            lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
378            cm.getCurrentCalls(lastRelevantPoll);
379        } else if (pendingOperations < 0) {
380            // this should never happen
381            Rlog.e(LOG_TAG,"GsmCallTracker.pendingOperations < 0");
382            pendingOperations = 0;
383        }
384    }
385
386    private void
387    updatePhoneState() {
388        PhoneConstants.State oldState = state;
389
390        if (ringingCall.isRinging()) {
391            state = PhoneConstants.State.RINGING;
392        } else if (pendingMO != null ||
393                !(foregroundCall.isIdle() && backgroundCall.isIdle())) {
394            state = PhoneConstants.State.OFFHOOK;
395        } else {
396            state = PhoneConstants.State.IDLE;
397        }
398
399        if (state == PhoneConstants.State.IDLE && oldState != state) {
400            voiceCallEndedRegistrants.notifyRegistrants(
401                new AsyncResult(null, null, null));
402        } else if (oldState == PhoneConstants.State.IDLE && oldState != state) {
403            voiceCallStartedRegistrants.notifyRegistrants (
404                    new AsyncResult(null, null, null));
405        }
406
407        if (state != oldState) {
408            phone.notifyPhoneStateChanged();
409        }
410    }
411
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 needsPollDelay = false;
433        boolean unknownConnectionAppeared = false;
434
435        for (int i = 0, curDC = 0, dcSize = polledCalls.size()
436                ; i < connections.length; i++) {
437            GsmConnection conn = connections[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 (pendingMO != null && pendingMO.compareTo(dc)) {
457
458                    if (DBG_POLL) log("poll: pendingMO=" + pendingMO);
459
460                    // It's our pending mobile originating call
461                    connections[i] = pendingMO;
462                    pendingMO.index = i;
463                    pendingMO.update(dc);
464                    pendingMO = null;
465
466                    // Someone has already asked to hangup this call
467                    if (hangupPendingMO) {
468                        hangupPendingMO = false;
469                        try {
470                            if (Phone.DEBUG_PHONE) log(
471                                    "poll: hangupPendingMO, hangup conn " + i);
472                            hangup(connections[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                    connections[i] = new GsmConnection(phone.getContext(), dc, this, i);
483
484                    // it's a ringing call
485                    if (connections[i].getCall() == ringingCall) {
486                        newRinging = connections[i];
487                    } else {
488                        // Something strange happened: a call appeared
489                        // which is neither a ringing call or one we created.
490                        // Either we've crashed and re-attached to an existing
491                        // call, or something else (eg, SIM) initiated the call.
492
493                        Rlog.i(LOG_TAG,"Phantom call appeared " + dc);
494
495                        // If it's a connected call, set the connect time so that
496                        // it's non-zero.  It may not be accurate, but at least
497                        // it won't appear as a Missed Call.
498                        if (dc.state != DriverCall.State.ALERTING
499                                && dc.state != DriverCall.State.DIALING) {
500                            connections[i].onConnectedInOrOut();
501                            if (dc.state == DriverCall.State.HOLDING) {
502                                // We've transitioned into HOLDING
503                                connections[i].onStartedHolding();
504                            }
505                        }
506
507                        unknownConnectionAppeared = true;
508                    }
509                }
510                hasNonHangupStateChanged = true;
511            } else if (conn != null && dc == null) {
512                // Connection missing in CLCC response that we were
513                // tracking.
514                droppedDuringPoll.add(conn);
515                // Dropped connections are removed from the CallTracker
516                // list but kept in the GsmCall list
517                connections[i] = null;
518            } else if (conn != null && dc != null && !conn.compareTo(dc)) {
519                // Connection in CLCC response does not match what
520                // we were tracking. Assume dropped call and new call
521
522                droppedDuringPoll.add(conn);
523                connections[i] = new GsmConnection (phone.getContext(), dc, this, i);
524
525                if (connections[i].getCall() == ringingCall) {
526                    newRinging = connections[i];
527                } // else something strange happened
528                hasNonHangupStateChanged = true;
529            } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
530                boolean changed;
531                changed = conn.update(dc);
532                hasNonHangupStateChanged = hasNonHangupStateChanged || changed;
533            }
534
535            if (REPEAT_POLLING) {
536                if (dc != null) {
537                    // FIXME with RIL, we should not need this anymore
538                    if ((dc.state == DriverCall.State.DIALING
539                            /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/)
540                        || (dc.state == DriverCall.State.ALERTING
541                            /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/)
542                        || (dc.state == DriverCall.State.INCOMING
543                            /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/)
544                        || (dc.state == DriverCall.State.WAITING
545                            /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/)
546                    ) {
547                        // Sometimes there's no unsolicited notification
548                        // for state transitions
549                        needsPollDelay = true;
550                    }
551                }
552            }
553        }
554
555        // This is the first poll after an ATD.
556        // We expect the pending call to appear in the list
557        // If it does not, we land here
558        if (pendingMO != null) {
559            Rlog.d(LOG_TAG,"Pending MO dropped before poll fg state:"
560                            + foregroundCall.getState());
561
562            droppedDuringPoll.add(pendingMO);
563            pendingMO = null;
564            hangupPendingMO = false;
565        }
566
567        if (newRinging != null) {
568            phone.notifyNewRingingConnection(newRinging);
569        }
570
571        // clear the "local hangup" and "missed/rejected call"
572        // cases from the "dropped during poll" list
573        // These cases need no "last call fail" reason
574        for (int i = droppedDuringPoll.size() - 1; i >= 0 ; i--) {
575            GsmConnection conn = droppedDuringPoll.get(i);
576
577            if (conn.isIncoming() && conn.getConnectTime() == 0) {
578                // Missed or rejected call
579                Connection.DisconnectCause cause;
580                if (conn.cause == Connection.DisconnectCause.LOCAL) {
581                    cause = Connection.DisconnectCause.INCOMING_REJECTED;
582                } else {
583                    cause = Connection.DisconnectCause.INCOMING_MISSED;
584                }
585
586                if (Phone.DEBUG_PHONE) {
587                    log("missed/rejected call, conn.cause=" + conn.cause);
588                    log("setting cause to " + cause);
589                }
590                droppedDuringPoll.remove(i);
591                conn.onDisconnect(cause);
592            } else if (conn.cause == Connection.DisconnectCause.LOCAL) {
593                // Local hangup
594                droppedDuringPoll.remove(i);
595                conn.onDisconnect(Connection.DisconnectCause.LOCAL);
596            } else if (conn.cause ==
597                Connection.DisconnectCause.INVALID_NUMBER) {
598                droppedDuringPoll.remove(i);
599                conn.onDisconnect(Connection.DisconnectCause.INVALID_NUMBER);
600            }
601        }
602
603        // Any non-local disconnects: determine cause
604        if (droppedDuringPoll.size() > 0) {
605            cm.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) {
619            internalClearDisconnected();
620        }
621
622        updatePhoneState();
623
624        if (unknownConnectionAppeared) {
625            phone.notifyUnknownConnection();
626        }
627
628        if (hasNonHangupStateChanged || newRinging != null) {
629            phone.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:" + state);
648
649        Rlog.i(LOG_TAG,"Ringing call: " + ringingCall.toString());
650
651        l = ringingCall.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: " + foregroundCall.toString());
657
658        l = foregroundCall.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: " + backgroundCall.toString());
664
665        l = backgroundCall.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.owner != this) {
677            throw new CallStateException ("GsmConnection " + conn
678                                    + "does not belong to GsmCallTracker " + this);
679        }
680
681        if (conn == pendingMO) {
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            hangupPendingMO = true;
687        } else {
688            try {
689                cm.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.owner != this) {
704            throw new CallStateException ("GsmConnection " + conn
705                                    + "does not belong to GsmCallTracker " + this);
706        }
707        try {
708            cm.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        desiredMute = mute;
723        cm.setMute(desiredMute, null);
724    }
725
726    /*package*/ boolean
727    getMute() {
728        return desiredMute;
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 == ringingCall) {
741            if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background");
742            cm.hangupWaitingOrBackground(obtainCompleteMessage());
743        } else if (call == foregroundCall) {
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 {
750                hangupForegroundResumeBackground();
751            }
752        } else if (call == backgroundCall) {
753            if (ringingCall.isRinging()) {
754                if (Phone.DEBUG_PHONE) {
755                    log("hangup all conns in background call");
756                }
757                hangupAllConnections(call);
758            } else {
759                hangupWaitingOrBackground();
760            }
761        } else {
762            throw new RuntimeException ("GsmCall " + call +
763                    "does not belong to GsmCallTracker " + this);
764        }
765
766        call.onHangupLocal();
767        phone.notifyPreciseCallStateChanged();
768    }
769
770    /* package */
771    void hangupWaitingOrBackground() {
772        if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground");
773        cm.hangupWaitingOrBackground(obtainCompleteMessage());
774    }
775
776    /* package */
777    void hangupForegroundResumeBackground() {
778        if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground");
779        cm.hangupForegroundResumeBackground(obtainCompleteMessage());
780    }
781
782    void hangupConnectionByIndex(GsmCall call, int index)
783            throws CallStateException {
784        int count = call.connections.size();
785        for (int i = 0; i < count; i++) {
786            GsmConnection cn = (GsmConnection)call.connections.get(i);
787            if (cn.getGSMIndex() == index) {
788                cm.hangupConnection(index, obtainCompleteMessage());
789                return;
790            }
791        }
792
793        throw new CallStateException("no gsm index found");
794    }
795
796    void hangupAllConnections(GsmCall call) throws CallStateException{
797        try {
798            int count = call.connections.size();
799            for (int i = 0; i < count; i++) {
800                GsmConnection cn = (GsmConnection)call.connections.get(i);
801                cm.hangupConnection(cn.getGSMIndex(), obtainCompleteMessage());
802            }
803        } catch (CallStateException ex) {
804            Rlog.e(LOG_TAG, "hangupConnectionByIndex caught " + ex);
805        }
806    }
807
808    /* package */
809    GsmConnection getConnectionByIndex(GsmCall call, int index)
810            throws CallStateException {
811        int count = call.connections.size();
812        for (int i = 0; i < count; i++) {
813            GsmConnection cn = (GsmConnection)call.connections.get(i);
814            if (cn.getGSMIndex() == index) {
815                return cn;
816            }
817        }
818
819        return null;
820    }
821
822    private Phone.SuppService getFailedService(int what) {
823        switch (what) {
824            case EVENT_SWITCH_RESULT:
825                return Phone.SuppService.SWITCH;
826            case EVENT_CONFERENCE_RESULT:
827                return Phone.SuppService.CONFERENCE;
828            case EVENT_SEPARATE_RESULT:
829                return Phone.SuppService.SEPARATE;
830            case EVENT_ECT_RESULT:
831                return Phone.SuppService.TRANSFER;
832        }
833        return Phone.SuppService.UNKNOWN;
834    }
835
836    //****** Overridden from Handler
837
838    public void
839    handleMessage (Message msg) {
840        AsyncResult ar;
841
842        switch (msg.what) {
843            case EVENT_POLL_CALLS_RESULT:
844                ar = (AsyncResult)msg.obj;
845
846                if (msg == lastRelevantPoll) {
847                    if (DBG_POLL) log(
848                            "handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
849                    needsPoll = false;
850                    lastRelevantPoll = null;
851                    handlePollCalls((AsyncResult)msg.obj);
852                }
853            break;
854
855            case EVENT_OPERATION_COMPLETE:
856                ar = (AsyncResult)msg.obj;
857                operationComplete();
858            break;
859
860            case EVENT_SWITCH_RESULT:
861            case EVENT_CONFERENCE_RESULT:
862            case EVENT_SEPARATE_RESULT:
863            case EVENT_ECT_RESULT:
864                ar = (AsyncResult)msg.obj;
865                if (ar.exception != null) {
866                    phone.notifySuppServiceFailed(getFailedService(msg.what));
867                }
868                operationComplete();
869            break;
870
871            case EVENT_GET_LAST_CALL_FAIL_CAUSE:
872                int causeCode;
873                ar = (AsyncResult)msg.obj;
874
875                operationComplete();
876
877                if (ar.exception != null) {
878                    // An exception occurred...just treat the disconnect
879                    // cause as "normal"
880                    causeCode = CallFailCause.NORMAL_CLEARING;
881                    Rlog.i(LOG_TAG,
882                            "Exception during getLastCallFailCause, assuming normal disconnect");
883                } else {
884                    causeCode = ((int[])ar.result)[0];
885                }
886                // Log the causeCode if its not normal
887                if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL ||
888                    causeCode == CallFailCause.TEMPORARY_FAILURE ||
889                    causeCode == CallFailCause.SWITCHING_CONGESTION ||
890                    causeCode == CallFailCause.CHANNEL_NOT_AVAIL ||
891                    causeCode == CallFailCause.QOS_NOT_AVAIL ||
892                    causeCode == CallFailCause.BEARER_NOT_AVAIL ||
893                    causeCode == CallFailCause.ERROR_UNSPECIFIED) {
894                    GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
895                    EventLog.writeEvent(EventLogTags.CALL_DROP,
896                            causeCode, loc != null ? loc.getCid() : -1,
897                            TelephonyManager.getDefault().getNetworkType());
898                }
899
900                for (int i = 0, s =  droppedDuringPoll.size()
901                        ; i < s ; i++
902                ) {
903                    GsmConnection conn = droppedDuringPoll.get(i);
904
905                    conn.onRemoteDisconnect(causeCode);
906                }
907
908                updatePhoneState();
909
910                phone.notifyPreciseCallStateChanged();
911                droppedDuringPoll.clear();
912            break;
913
914            case EVENT_REPOLL_AFTER_DELAY:
915            case EVENT_CALL_STATE_CHANGE:
916                pollCallsWhenSafe();
917            break;
918
919            case EVENT_RADIO_AVAILABLE:
920                handleRadioAvailable();
921            break;
922
923            case EVENT_RADIO_NOT_AVAILABLE:
924                handleRadioNotAvailable();
925            break;
926        }
927    }
928
929    protected void log(String msg) {
930        Rlog.d(LOG_TAG, "[GsmCallTracker] " + msg);
931    }
932
933    @Override
934    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
935        pw.println("GsmCallTracker extends:");
936        super.dump(fd, pw, args);
937        pw.println("connections: length=" + connections.length);
938        for(int i=0; i < connections.length; i++) {
939            pw.printf("  connections[%d]=%s\n", i, connections[i]);
940        }
941        pw.println(" voiceCallEndedRegistrants=" + voiceCallEndedRegistrants);
942        pw.println(" voiceCallStartedRegistrants=" + voiceCallStartedRegistrants);
943        pw.println(" droppedDuringPoll: size=" + droppedDuringPoll.size());
944        for(int i = 0; i < droppedDuringPoll.size(); i++) {
945            pw.printf( "  droppedDuringPoll[%d]=%s\n", i, droppedDuringPoll.get(i));
946        }
947        pw.println(" ringingCall=" + ringingCall);
948        pw.println(" foregroundCall=" + foregroundCall);
949        pw.println(" backgroundCall=" + backgroundCall);
950        pw.println(" pendingMO=" + pendingMO);
951        pw.println(" hangupPendingMO=" + hangupPendingMO);
952        pw.println(" phone=" + phone);
953        pw.println(" desiredMute=" + desiredMute);
954        pw.println(" state=" + state);
955    }
956}
957