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