CdmaCallTracker.java revision bb097a7ba08252c516e578ce30cdb658b63d7c9b
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.cdma;
18
19import android.os.AsyncResult;
20import android.os.Handler;
21import android.os.Message;
22import android.os.Registrant;
23import android.os.RegistrantList;
24import android.telephony.DisconnectCause;
25import android.telephony.PhoneNumberUtils;
26import android.telephony.ServiceState;
27import android.telephony.Rlog;
28import android.os.SystemProperties;
29
30import com.android.internal.telephony.CallStateException;
31import com.android.internal.telephony.CallTracker;
32import com.android.internal.telephony.CommandsInterface;
33import com.android.internal.telephony.Connection;
34import com.android.internal.telephony.DriverCall;
35import com.android.internal.telephony.Phone;
36import com.android.internal.telephony.PhoneBase;
37import com.android.internal.telephony.PhoneConstants;
38import com.android.internal.telephony.TelephonyProperties;
39
40import java.io.FileDescriptor;
41import java.io.PrintWriter;
42import java.util.ArrayList;
43import java.util.List;
44
45
46/**
47 * {@hide}
48 */
49public final class CdmaCallTracker extends CallTracker {
50    static final String LOG_TAG = "CdmaCallTracker";
51
52    private static final boolean REPEAT_POLLING = false;
53
54    private static final boolean DBG_POLL = false;
55
56    //***** Constants
57
58    static final int MAX_CONNECTIONS = 8;
59    static final int MAX_CONNECTIONS_PER_CALL = 1; // only 1 connection allowed per call
60
61    //***** Instance Variables
62
63    CdmaConnection mConnections[] = new CdmaConnection[MAX_CONNECTIONS];
64    RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
65    RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
66    RegistrantList mCallWaitingRegistrants =  new RegistrantList();
67
68
69    // connections dropped during last poll
70    ArrayList<CdmaConnection> mDroppedDuringPoll
71        = new ArrayList<CdmaConnection>(MAX_CONNECTIONS);
72
73    CdmaCall mRingingCall = new CdmaCall(this);
74    // A call that is ringing or (call) waiting
75    CdmaCall mForegroundCall = new CdmaCall(this);
76    CdmaCall mBackgroundCall = new CdmaCall(this);
77
78    CdmaConnection mPendingMO;
79    boolean mHangupPendingMO;
80    boolean mPendingCallInEcm=false;
81    boolean mIsInEmergencyCall = false;
82    CDMAPhone mPhone;
83
84    boolean mDesiredMute = false;    // false = mute off
85
86    int mPendingCallClirMode;
87    PhoneConstants.State mState = PhoneConstants.State.IDLE;
88
89    private boolean mIsEcmTimerCanceled = false;
90
91    private int m3WayCallFlashDelay = 0;
92//    boolean needsPoll;
93
94
95
96    //***** Events
97
98    //***** Constructors
99    CdmaCallTracker(CDMAPhone phone) {
100        mPhone = phone;
101        mCi = phone.mCi;
102        mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
103        mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null);
104        mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);
105        mCi.registerForCallWaitingInfo(this, EVENT_CALL_WAITING_INFO_CDMA, null);
106        mForegroundCall.setGeneric(false);
107    }
108
109    public void dispose() {
110        Rlog.d(LOG_TAG, "CdmaCallTracker dispose");
111        mCi.unregisterForLineControlInfo(this);
112        mCi.unregisterForCallStateChanged(this);
113        mCi.unregisterForOn(this);
114        mCi.unregisterForNotAvailable(this);
115        mCi.unregisterForCallWaitingInfo(this);
116
117        clearDisconnected();
118
119    }
120
121    @Override
122    protected void finalize() {
123        Rlog.d(LOG_TAG, "CdmaCallTracker finalized");
124    }
125
126    //***** Instance Methods
127
128    //***** Public Methods
129    @Override
130    public void registerForVoiceCallStarted(Handler h, int what, Object obj) {
131        Registrant r = new Registrant(h, what, obj);
132        mVoiceCallStartedRegistrants.add(r);
133        // Notify if in call when registering
134        if (mState != PhoneConstants.State.IDLE) {
135            r.notifyRegistrant(new AsyncResult(null, null, null));
136        }
137    }
138    @Override
139    public void unregisterForVoiceCallStarted(Handler h) {
140        mVoiceCallStartedRegistrants.remove(h);
141    }
142
143    @Override
144    public void registerForVoiceCallEnded(Handler h, int what, Object obj) {
145        Registrant r = new Registrant(h, what, obj);
146        mVoiceCallEndedRegistrants.add(r);
147    }
148
149    @Override
150    public void unregisterForVoiceCallEnded(Handler h) {
151        mVoiceCallEndedRegistrants.remove(h);
152    }
153
154    public void registerForCallWaiting(Handler h, int what, Object obj) {
155        Registrant r = new Registrant (h, what, obj);
156        mCallWaitingRegistrants.add(r);
157    }
158
159    public void unregisterForCallWaiting(Handler h) {
160        mCallWaitingRegistrants.remove(h);
161    }
162
163    /**
164     * clirMode is one of the CLIR_ constants
165     */
166    Connection
167    dial (String dialString, int clirMode) throws CallStateException {
168        // note that this triggers call state changed notif
169        clearDisconnected();
170
171        if (!canDial()) {
172            throw new CallStateException("cannot dial in current state");
173        }
174
175        String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
176        boolean isPhoneInEcmMode = inEcm.equals("true");
177        boolean isEmergencyCall =
178                PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString);
179
180        // Cancel Ecm timer if a second emergency call is originating in Ecm mode
181        if (isPhoneInEcmMode && isEmergencyCall) {
182            handleEcmTimer(CDMAPhone.CANCEL_ECM_TIMER);
183        }
184
185        // We are initiating a call therefore even if we previously
186        // didn't know the state (i.e. Generic was true) we now know
187        // and therefore can set Generic to false.
188        mForegroundCall.setGeneric(false);
189
190        // The new call must be assigned to the foreground call.
191        // That call must be idle, so place anything that's
192        // there on hold
193        if (mForegroundCall.getState() == CdmaCall.State.ACTIVE) {
194            return dialThreeWay(dialString);
195        }
196
197        mPendingMO = new CdmaConnection(mPhone.getContext(), checkForTestEmergencyNumber(dialString),
198                this, mForegroundCall);
199        mHangupPendingMO = false;
200
201        if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
202                || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0 ) {
203            // Phone number is invalid
204            mPendingMO.mCause = DisconnectCause.INVALID_NUMBER;
205
206            // handlePollCalls() will notice this call not present
207            // and will mark it as dropped.
208            pollCallsWhenSafe();
209        } else {
210            // Always unmute when initiating a new call
211            setMute(false);
212
213            // Check data call
214            disableDataCallInEmergencyCall(dialString);
215
216            // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
217            if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
218                mCi.dial(mPendingMO.getAddress(), clirMode, obtainCompleteMessage());
219            } else {
220                mPhone.exitEmergencyCallbackMode();
221                mPhone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null);
222                mPendingCallClirMode=clirMode;
223                mPendingCallInEcm=true;
224            }
225        }
226
227        updatePhoneState();
228        mPhone.notifyPreciseCallStateChanged();
229
230        return mPendingMO;
231    }
232
233
234    Connection
235    dial (String dialString) throws CallStateException {
236        return dial(dialString, CommandsInterface.CLIR_DEFAULT);
237    }
238
239    private Connection
240    dialThreeWay (String dialString) {
241        if (!mForegroundCall.isIdle()) {
242            // Check data call
243            disableDataCallInEmergencyCall(dialString);
244
245            // Attach the new connection to foregroundCall
246            mPendingMO = new CdmaConnection(mPhone.getContext(),
247                                checkForTestEmergencyNumber(dialString), this, mForegroundCall);
248            // Some network need a empty flash before sending the normal one
249            m3WayCallFlashDelay = mPhone.getContext().getResources()
250                    .getInteger(com.android.internal.R.integer.config_cdma_3waycall_flash_delay);
251            if (m3WayCallFlashDelay > 0) {
252                mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_THREE_WAY_DIAL_BLANK_FLASH));
253            } else {
254                mCi.sendCDMAFeatureCode(mPendingMO.getAddress(),
255                        obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA));
256            }
257            return mPendingMO;
258        }
259        return null;
260    }
261
262    void
263    acceptCall() throws CallStateException {
264        if (mRingingCall.getState() == CdmaCall.State.INCOMING) {
265            Rlog.i("phone", "acceptCall: incoming...");
266            // Always unmute when answering a new call
267            setMute(false);
268            mCi.acceptCall(obtainCompleteMessage());
269        } else if (mRingingCall.getState() == CdmaCall.State.WAITING) {
270            CdmaConnection cwConn = (CdmaConnection)(mRingingCall.getLatestConnection());
271
272            // Since there is no network response for supplimentary
273            // service for CDMA, we assume call waiting is answered.
274            // ringing Call state change to idle is in CdmaCall.detach
275            // triggered by updateParent.
276            cwConn.updateParent(mRingingCall, mForegroundCall);
277            cwConn.onConnectedInOrOut();
278            updatePhoneState();
279            switchWaitingOrHoldingAndActive();
280        } else {
281            throw new CallStateException("phone not ringing");
282        }
283    }
284
285    void
286    rejectCall () throws CallStateException {
287        // AT+CHLD=0 means "release held or UDUB"
288        // so if the phone isn't ringing, this could hang up held
289        if (mRingingCall.getState().isRinging()) {
290            mCi.rejectCall(obtainCompleteMessage());
291        } else {
292            throw new CallStateException("phone not ringing");
293        }
294    }
295
296    void
297    switchWaitingOrHoldingAndActive() throws CallStateException {
298        // Should we bother with this check?
299        if (mRingingCall.getState() == CdmaCall.State.INCOMING) {
300            throw new CallStateException("cannot be in the incoming state");
301        } else if (mForegroundCall.getConnections().size() > 1) {
302            flashAndSetGenericTrue();
303        } else {
304            // Send a flash command to CDMA network for putting the other party on hold.
305            // For CDMA networks which do not support this the user would just hear a beep
306            // from the network. For CDMA networks which do support it will put the other
307            // party on hold.
308            mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT));
309        }
310    }
311
312    void
313    conference() {
314        // Should we be checking state?
315        flashAndSetGenericTrue();
316    }
317
318    void
319    explicitCallTransfer() {
320        mCi.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT));
321    }
322
323    void
324    clearDisconnected() {
325        internalClearDisconnected();
326
327        updatePhoneState();
328        mPhone.notifyPreciseCallStateChanged();
329    }
330
331    boolean
332    canConference() {
333        return mForegroundCall.getState() == CdmaCall.State.ACTIVE
334                && mBackgroundCall.getState() == CdmaCall.State.HOLDING
335                && !mBackgroundCall.isFull()
336                && !mForegroundCall.isFull();
337    }
338
339    boolean
340    canDial() {
341        boolean ret;
342        int serviceState = mPhone.getServiceState().getState();
343        String disableCall = SystemProperties.get(
344                TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
345
346        ret = (serviceState != ServiceState.STATE_POWER_OFF)
347                && mPendingMO == null
348                && !mRingingCall.isRinging()
349                && !disableCall.equals("true")
350                && (!mForegroundCall.getState().isAlive()
351                    || (mForegroundCall.getState() == CdmaCall.State.ACTIVE)
352                    || !mBackgroundCall.getState().isAlive());
353
354        if (!ret) {
355            log(String.format("canDial is false\n" +
356                              "((serviceState=%d) != ServiceState.STATE_POWER_OFF)::=%s\n" +
357                              "&& pendingMO == null::=%s\n" +
358                              "&& !ringingCall.isRinging()::=%s\n" +
359                              "&& !disableCall.equals(\"true\")::=%s\n" +
360                              "&& (!foregroundCall.getState().isAlive()::=%s\n" +
361                              "   || foregroundCall.getState() == CdmaCall.State.ACTIVE::=%s\n" +
362                              "   ||!backgroundCall.getState().isAlive())::=%s)",
363                    serviceState,
364                    serviceState != ServiceState.STATE_POWER_OFF,
365                    mPendingMO == null,
366                    !mRingingCall.isRinging(),
367                    !disableCall.equals("true"),
368                    !mForegroundCall.getState().isAlive(),
369                    mForegroundCall.getState() == CdmaCall.State.ACTIVE,
370                    !mBackgroundCall.getState().isAlive()));
371        }
372        return ret;
373    }
374
375    boolean
376    canTransfer() {
377        Rlog.e(LOG_TAG, "canTransfer: not possible in CDMA");
378        return false;
379    }
380
381    //***** Private Instance Methods
382
383    private void
384    internalClearDisconnected() {
385        mRingingCall.clearDisconnected();
386        mForegroundCall.clearDisconnected();
387        mBackgroundCall.clearDisconnected();
388    }
389
390    /**
391     * Obtain a message to use for signalling "invoke getCurrentCalls() when
392     * this operation and all other pending operations are complete
393     */
394    private Message
395    obtainCompleteMessage() {
396        return obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
397    }
398
399    /**
400     * Obtain a message to use for signalling "invoke getCurrentCalls() when
401     * this operation and all other pending operations are complete
402     */
403    private Message
404    obtainCompleteMessage(int what) {
405        mPendingOperations++;
406        mLastRelevantPoll = null;
407        mNeedsPoll = true;
408
409        if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" +
410                mPendingOperations + ", needsPoll=" + mNeedsPoll);
411
412        return obtainMessage(what);
413    }
414
415    private void
416    operationComplete() {
417        mPendingOperations--;
418
419        if (DBG_POLL) log("operationComplete: pendingOperations=" +
420                mPendingOperations + ", needsPoll=" + mNeedsPoll);
421
422        if (mPendingOperations == 0 && mNeedsPoll) {
423            mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
424            mCi.getCurrentCalls(mLastRelevantPoll);
425        } else if (mPendingOperations < 0) {
426            // this should never happen
427            Rlog.e(LOG_TAG,"CdmaCallTracker.pendingOperations < 0");
428            mPendingOperations = 0;
429        }
430    }
431
432
433
434    private void
435    updatePhoneState() {
436        PhoneConstants.State oldState = mState;
437
438        if (mRingingCall.isRinging()) {
439            mState = PhoneConstants.State.RINGING;
440        } else if (mPendingMO != null ||
441                !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
442            mState = PhoneConstants.State.OFFHOOK;
443        } else {
444            mState = PhoneConstants.State.IDLE;
445        }
446
447        if (mState == PhoneConstants.State.IDLE && oldState != mState) {
448            mVoiceCallEndedRegistrants.notifyRegistrants(
449                new AsyncResult(null, null, null));
450        } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
451            mVoiceCallStartedRegistrants.notifyRegistrants (
452                    new AsyncResult(null, null, null));
453        }
454        if (Phone.DEBUG_PHONE) {
455            log("update phone state, old=" + oldState + " new="+ mState);
456        }
457        if (mState != oldState) {
458            mPhone.notifyPhoneStateChanged();
459        }
460    }
461
462    // ***** Overwritten from CallTracker
463
464    @Override
465    protected void
466    handlePollCalls(AsyncResult ar) {
467        List polledCalls;
468
469        if (ar.exception == null) {
470            polledCalls = (List)ar.result;
471        } else if (isCommandExceptionRadioNotAvailable(ar.exception)) {
472            // just a dummy empty ArrayList to cause the loop
473            // to hang up all the calls
474            polledCalls = new ArrayList();
475        } else {
476            // Radio probably wasn't ready--try again in a bit
477            // But don't keep polling if the channel is closed
478            pollCallsAfterDelay();
479            return;
480        }
481
482        Connection newRinging = null; //or waiting
483        boolean hasNonHangupStateChanged = false;   // Any change besides
484                                                    // a dropped connection
485        boolean hasAnyCallDisconnected = false;
486        boolean needsPollDelay = false;
487        boolean unknownConnectionAppeared = false;
488
489        for (int i = 0, curDC = 0, dcSize = polledCalls.size()
490                ; i < mConnections.length; i++) {
491            CdmaConnection conn = mConnections[i];
492            DriverCall dc = null;
493
494            // polledCall list is sparse
495            if (curDC < dcSize) {
496                dc = (DriverCall) polledCalls.get(curDC);
497
498                if (dc.index == i+1) {
499                    curDC++;
500                } else {
501                    dc = null;
502                }
503            }
504
505            if (DBG_POLL) log("poll: conn[i=" + i + "]=" +
506                    conn+", dc=" + dc);
507
508            if (conn == null && dc != null) {
509                // Connection appeared in CLCC response that we don't know about
510                if (mPendingMO != null && mPendingMO.compareTo(dc)) {
511
512                    if (DBG_POLL) log("poll: pendingMO=" + mPendingMO);
513
514                    // It's our pending mobile originating call
515                    mConnections[i] = mPendingMO;
516                    mPendingMO.mIndex = i;
517                    mPendingMO.update(dc);
518                    mPendingMO = null;
519
520                    // Someone has already asked to hangup this call
521                    if (mHangupPendingMO) {
522                        mHangupPendingMO = false;
523                        // Re-start Ecm timer when an uncompleted emergency call ends
524                        if (mIsEcmTimerCanceled) {
525                            handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER);
526                        }
527
528                        try {
529                            if (Phone.DEBUG_PHONE) log(
530                                    "poll: hangupPendingMO, hangup conn " + i);
531                            hangup(mConnections[i]);
532                        } catch (CallStateException ex) {
533                            Rlog.e(LOG_TAG, "unexpected error on hangup");
534                        }
535
536                        // Do not continue processing this poll
537                        // Wait for hangup and repoll
538                        return;
539                    }
540                } else {
541                    if (Phone.DEBUG_PHONE) {
542                        log("pendingMo=" + mPendingMO + ", dc=" + dc);
543                    }
544                    mConnections[i] = new CdmaConnection(mPhone.getContext(), dc, this, i);
545
546                    if (mHandoverConnection != null) {
547                        // Single Radio Voice Call Continuity (SRVCC) completed
548                        mPhone.migrateFrom((PhoneBase) mPhone.getImsPhone());
549                        mConnections[i].migrateFrom(mHandoverConnection);
550                        mPhone.notifyHandoverStateChanged(mConnections[i]);
551                        mHandoverConnection = null;
552                    } else {
553                        // find if the MT call is a new ring or unknown connection
554                        newRinging = checkMtFindNewRinging(dc,i);
555                        if (newRinging == null) {
556                            unknownConnectionAppeared = true;
557                        }
558                    }
559                    checkAndEnableDataCallAfterEmergencyCallDropped();
560                }
561                hasNonHangupStateChanged = true;
562            } else if (conn != null && dc == null) {
563                // This case means the RIL has no more active call anymore and
564                // we need to clean up the foregroundCall and ringingCall.
565                // Loop through foreground call connections as
566                // it contains the known logical connections.
567                int count = mForegroundCall.mConnections.size();
568                for (int n = 0; n < count; n++) {
569                    if (Phone.DEBUG_PHONE) log("adding fgCall cn " + n + " to droppedDuringPoll");
570                    CdmaConnection cn = (CdmaConnection)mForegroundCall.mConnections.get(n);
571                    mDroppedDuringPoll.add(cn);
572                }
573                count = mRingingCall.mConnections.size();
574                // Loop through ringing call connections as
575                // it may contain the known logical connections.
576                for (int n = 0; n < count; n++) {
577                    if (Phone.DEBUG_PHONE) log("adding rgCall cn " + n + " to droppedDuringPoll");
578                    CdmaConnection cn = (CdmaConnection)mRingingCall.mConnections.get(n);
579                    mDroppedDuringPoll.add(cn);
580                }
581                mForegroundCall.setGeneric(false);
582                mRingingCall.setGeneric(false);
583
584                // Re-start Ecm timer when the connected emergency call ends
585                if (mIsEcmTimerCanceled) {
586                    handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER);
587                }
588                // If emergency call is not going through while dialing
589                checkAndEnableDataCallAfterEmergencyCallDropped();
590
591                // Dropped connections are removed from the CallTracker
592                // list but kept in the Call list
593                mConnections[i] = null;
594            } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
595                // Call collision case
596                if (conn.isIncoming() != dc.isMT) {
597                    if (dc.isMT == true){
598                        // Mt call takes precedence than Mo,drops Mo
599                        mDroppedDuringPoll.add(conn);
600                        // find if the MT call is a new ring or unknown connection
601                        newRinging = checkMtFindNewRinging(dc,i);
602                        if (newRinging == null) {
603                            unknownConnectionAppeared = true;
604                        }
605                        checkAndEnableDataCallAfterEmergencyCallDropped();
606                    } else {
607                        // Call info stored in conn is not consistent with the call info from dc.
608                        // We should follow the rule of MT calls taking precedence over MO calls
609                        // when there is conflict, so here we drop the call info from dc and
610                        // continue to use the call info from conn, and only take a log.
611                        Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc);
612                    }
613                } else {
614                    boolean changed;
615                    changed = conn.update(dc);
616                    hasNonHangupStateChanged = hasNonHangupStateChanged || changed;
617                }
618            }
619
620            if (REPEAT_POLLING) {
621                if (dc != null) {
622                    // FIXME with RIL, we should not need this anymore
623                    if ((dc.state == DriverCall.State.DIALING
624                            /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/)
625                        || (dc.state == DriverCall.State.ALERTING
626                            /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/)
627                        || (dc.state == DriverCall.State.INCOMING
628                            /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/)
629                        || (dc.state == DriverCall.State.WAITING
630                            /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/)
631                    ) {
632                        // Sometimes there's no unsolicited notification
633                        // for state transitions
634                        needsPollDelay = true;
635                    }
636                }
637            }
638        }
639
640        // This is the first poll after an ATD.
641        // We expect the pending call to appear in the list
642        // If it does not, we land here
643        if (mPendingMO != null) {
644            Rlog.d(LOG_TAG,"Pending MO dropped before poll fg state:"
645                            + mForegroundCall.getState());
646
647            mDroppedDuringPoll.add(mPendingMO);
648            mPendingMO = null;
649            mHangupPendingMO = false;
650            if( mPendingCallInEcm) {
651                mPendingCallInEcm = false;
652            }
653        }
654
655        if (newRinging != null) {
656            mPhone.notifyNewRingingConnection(newRinging);
657        }
658
659        // clear the "local hangup" and "missed/rejected call"
660        // cases from the "dropped during poll" list
661        // These cases need no "last call fail" reason
662        for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
663            CdmaConnection conn = mDroppedDuringPoll.get(i);
664
665            if (conn.isIncoming() && conn.getConnectTime() == 0) {
666                // Missed or rejected call
667                int cause;
668                if (conn.mCause == DisconnectCause.LOCAL) {
669                    cause = DisconnectCause.INCOMING_REJECTED;
670                } else {
671                    cause = DisconnectCause.INCOMING_MISSED;
672                }
673
674                if (Phone.DEBUG_PHONE) {
675                    log("missed/rejected call, conn.cause=" + conn.mCause);
676                    log("setting cause to " + cause);
677                }
678                mDroppedDuringPoll.remove(i);
679                hasAnyCallDisconnected |= conn.onDisconnect(cause);
680            } else if (conn.mCause == DisconnectCause.LOCAL
681                    || conn.mCause == DisconnectCause.INVALID_NUMBER) {
682                mDroppedDuringPoll.remove(i);
683                hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
684            }
685        }
686
687        // Any non-local disconnects: determine cause
688        if (mDroppedDuringPoll.size() > 0) {
689            mCi.getLastCallFailCause(
690                obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
691        }
692
693        if (needsPollDelay) {
694            pollCallsAfterDelay();
695        }
696
697        // Cases when we can no longer keep disconnected Connection's
698        // with their previous calls
699        // 1) the phone has started to ring
700        // 2) A Call/Connection object has changed state...
701        //    we may have switched or held or answered (but not hung up)
702        if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) {
703            internalClearDisconnected();
704        }
705
706        updatePhoneState();
707
708        if (unknownConnectionAppeared) {
709            mPhone.notifyUnknownConnection();
710        }
711
712        if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
713            mPhone.notifyPreciseCallStateChanged();
714        }
715
716        //dumpState();
717    }
718
719    //***** Called from CdmaConnection
720    /*package*/ void
721    hangup (CdmaConnection conn) throws CallStateException {
722        if (conn.mOwner != this) {
723            throw new CallStateException ("CdmaConnection " + conn
724                                    + "does not belong to CdmaCallTracker " + this);
725        }
726
727        if (conn == mPendingMO) {
728            // We're hanging up an outgoing call that doesn't have it's
729            // GSM index assigned yet
730
731            if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true");
732            mHangupPendingMO = true;
733        } else if ((conn.getCall() == mRingingCall)
734                && (mRingingCall.getState() == CdmaCall.State.WAITING)) {
735            // Handle call waiting hang up case.
736            //
737            // The ringingCall state will change to IDLE in CdmaCall.detach
738            // if the ringing call connection size is 0. We don't specifically
739            // set the ringing call state to IDLE here to avoid a race condition
740            // where a new call waiting could get a hang up from an old call
741            // waiting ringingCall.
742            //
743            // PhoneApp does the call log itself since only PhoneApp knows
744            // the hangup reason is user ignoring or timing out. So conn.onDisconnect()
745            // is not called here. Instead, conn.onLocalDisconnect() is called.
746            conn.onLocalDisconnect();
747            updatePhoneState();
748            mPhone.notifyPreciseCallStateChanged();
749            return;
750        } else {
751            try {
752                mCi.hangupConnection (conn.getCDMAIndex(), obtainCompleteMessage());
753            } catch (CallStateException ex) {
754                // Ignore "connection not found"
755                // Call may have hung up already
756                Rlog.w(LOG_TAG,"CdmaCallTracker WARN: hangup() on absent connection "
757                                + conn);
758            }
759        }
760
761        conn.onHangupLocal();
762    }
763
764    /*package*/ void
765    separate (CdmaConnection conn) throws CallStateException {
766        if (conn.mOwner != this) {
767            throw new CallStateException ("CdmaConnection " + conn
768                                    + "does not belong to CdmaCallTracker " + this);
769        }
770        try {
771            mCi.separateConnection (conn.getCDMAIndex(),
772                obtainCompleteMessage(EVENT_SEPARATE_RESULT));
773        } catch (CallStateException ex) {
774            // Ignore "connection not found"
775            // Call may have hung up already
776            Rlog.w(LOG_TAG,"CdmaCallTracker WARN: separate() on absent connection "
777                          + conn);
778        }
779    }
780
781    //***** Called from CDMAPhone
782
783    /*package*/ void
784    setMute(boolean mute) {
785        mDesiredMute = mute;
786        mCi.setMute(mDesiredMute, null);
787    }
788
789    /*package*/ boolean
790    getMute() {
791        return mDesiredMute;
792    }
793
794
795    //***** Called from CdmaCall
796
797    /* package */ void
798    hangup (CdmaCall call) throws CallStateException {
799        if (call.getConnections().size() == 0) {
800            throw new CallStateException("no connections in call");
801        }
802
803        if (call == mRingingCall) {
804            if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background");
805            mCi.hangupWaitingOrBackground(obtainCompleteMessage());
806        } else if (call == mForegroundCall) {
807            if (call.isDialingOrAlerting()) {
808                if (Phone.DEBUG_PHONE) {
809                    log("(foregnd) hangup dialing or alerting...");
810                }
811                hangup((CdmaConnection)(call.getConnections().get(0)));
812            } else {
813                hangupForegroundResumeBackground();
814            }
815        } else if (call == mBackgroundCall) {
816            if (mRingingCall.isRinging()) {
817                if (Phone.DEBUG_PHONE) {
818                    log("hangup all conns in background call");
819                }
820                hangupAllConnections(call);
821            } else {
822                hangupWaitingOrBackground();
823            }
824        } else {
825            throw new RuntimeException ("CdmaCall " + call +
826                    "does not belong to CdmaCallTracker " + this);
827        }
828
829        call.onHangupLocal();
830        mPhone.notifyPreciseCallStateChanged();
831    }
832
833    /* package */
834    void hangupWaitingOrBackground() {
835        if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground");
836        mCi.hangupWaitingOrBackground(obtainCompleteMessage());
837    }
838
839    /* package */
840    void hangupForegroundResumeBackground() {
841        if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground");
842        mCi.hangupForegroundResumeBackground(obtainCompleteMessage());
843    }
844
845    void hangupConnectionByIndex(CdmaCall call, int index)
846            throws CallStateException {
847        int count = call.mConnections.size();
848        for (int i = 0; i < count; i++) {
849            CdmaConnection cn = (CdmaConnection)call.mConnections.get(i);
850            if (cn.getCDMAIndex() == index) {
851                mCi.hangupConnection(index, obtainCompleteMessage());
852                return;
853            }
854        }
855
856        throw new CallStateException("no gsm index found");
857    }
858
859    void hangupAllConnections(CdmaCall call) {
860        try {
861            int count = call.mConnections.size();
862            for (int i = 0; i < count; i++) {
863                CdmaConnection cn = (CdmaConnection)call.mConnections.get(i);
864                mCi.hangupConnection(cn.getCDMAIndex(), obtainCompleteMessage());
865            }
866        } catch (CallStateException ex) {
867            Rlog.e(LOG_TAG, "hangupConnectionByIndex caught " + ex);
868        }
869    }
870
871    /* package */
872    CdmaConnection getConnectionByIndex(CdmaCall call, int index)
873            throws CallStateException {
874        int count = call.mConnections.size();
875        for (int i = 0; i < count; i++) {
876            CdmaConnection cn = (CdmaConnection)call.mConnections.get(i);
877            if (cn.getCDMAIndex() == index) {
878                return cn;
879            }
880        }
881
882        return null;
883    }
884
885    private void flashAndSetGenericTrue() {
886        mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT));
887
888        // Set generic to true because in CDMA it is not known what
889        // the status of the call is after a call waiting is answered,
890        // 3 way call merged or a switch between calls.
891        mForegroundCall.setGeneric(true);
892        mPhone.notifyPreciseCallStateChanged();
893    }
894
895    private void handleRadioNotAvailable() {
896        // handlePollCalls will clear out its
897        // call list when it gets the CommandException
898        // error result from this
899        pollCallsWhenSafe();
900    }
901
902    private void notifyCallWaitingInfo(CdmaCallWaitingNotification obj) {
903        if (mCallWaitingRegistrants != null) {
904            mCallWaitingRegistrants.notifyRegistrants(new AsyncResult(null, obj, null));
905        }
906    }
907
908    private void handleCallWaitingInfo (CdmaCallWaitingNotification cw) {
909        // Check how many connections in foregroundCall.
910        // If the connection in foregroundCall is more
911        // than one, then the connection information is
912        // not reliable anymore since it means either
913        // call waiting is connected or 3 way call is
914        // dialed before, so set generic.
915        if (mForegroundCall.mConnections.size() > 1 ) {
916            mForegroundCall.setGeneric(true);
917        }
918
919        // Create a new CdmaConnection which attaches itself to ringingCall.
920        mRingingCall.setGeneric(false);
921        new CdmaConnection(mPhone.getContext(), cw, this, mRingingCall);
922        updatePhoneState();
923
924        // Finally notify application
925        notifyCallWaitingInfo(cw);
926    }
927    //****** Overridden from Handler
928
929    @Override
930    public void
931    handleMessage (Message msg) {
932        AsyncResult ar;
933
934        if (!mPhone.mIsTheCurrentActivePhone) {
935            Rlog.w(LOG_TAG, "Ignoring events received on inactive CdmaPhone");
936            return;
937        }
938        switch (msg.what) {
939            case EVENT_POLL_CALLS_RESULT:{
940                Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");
941                ar = (AsyncResult)msg.obj;
942
943                if(msg == mLastRelevantPoll) {
944                    if(DBG_POLL) log(
945                            "handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
946                    mNeedsPoll = false;
947                    mLastRelevantPoll = null;
948                    handlePollCalls((AsyncResult)msg.obj);
949                }
950            }
951            break;
952
953            case EVENT_OPERATION_COMPLETE:
954                operationComplete();
955            break;
956
957            case EVENT_SWITCH_RESULT:
958                 // In GSM call operationComplete() here which gets the
959                 // current call list. But in CDMA there is no list so
960                 // there is nothing to do.
961            break;
962
963            case EVENT_GET_LAST_CALL_FAIL_CAUSE:
964                int causeCode;
965                ar = (AsyncResult)msg.obj;
966
967                operationComplete();
968
969                if (ar.exception != null) {
970                    // An exception occurred...just treat the disconnect
971                    // cause as "normal"
972                    causeCode = CallFailCause.NORMAL_CLEARING;
973                    Rlog.i(LOG_TAG,
974                            "Exception during getLastCallFailCause, assuming normal disconnect");
975                } else {
976                    causeCode = ((int[])ar.result)[0];
977                }
978
979                for (int i = 0, s =  mDroppedDuringPoll.size()
980                        ; i < s ; i++
981                ) {
982                    CdmaConnection conn = mDroppedDuringPoll.get(i);
983
984                    conn.onRemoteDisconnect(causeCode);
985                }
986
987                updatePhoneState();
988
989                mPhone.notifyPreciseCallStateChanged();
990                mDroppedDuringPoll.clear();
991            break;
992
993            case EVENT_REPOLL_AFTER_DELAY:
994            case EVENT_CALL_STATE_CHANGE:
995                pollCallsWhenSafe();
996            break;
997
998            case EVENT_RADIO_AVAILABLE:
999                handleRadioAvailable();
1000            break;
1001
1002            case EVENT_RADIO_NOT_AVAILABLE:
1003                handleRadioNotAvailable();
1004            break;
1005
1006            case EVENT_EXIT_ECM_RESPONSE_CDMA:
1007                // no matter the result, we still do the same here
1008                if (mPendingCallInEcm) {
1009                    mCi.dial(mPendingMO.getAddress(), mPendingCallClirMode, obtainCompleteMessage());
1010                    mPendingCallInEcm = false;
1011                }
1012                mPhone.unsetOnEcbModeExitResponse(this);
1013            break;
1014
1015            case EVENT_CALL_WAITING_INFO_CDMA:
1016               ar = (AsyncResult)msg.obj;
1017               if (ar.exception == null) {
1018                   handleCallWaitingInfo((CdmaCallWaitingNotification)ar.result);
1019                   Rlog.d(LOG_TAG, "Event EVENT_CALL_WAITING_INFO_CDMA Received");
1020               }
1021            break;
1022
1023            case EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA:
1024                ar = (AsyncResult)msg.obj;
1025                if (ar.exception == null) {
1026                    // Assume 3 way call is connected
1027                    mPendingMO.onConnectedInOrOut();
1028                    mPendingMO = null;
1029                }
1030            break;
1031
1032            case EVENT_THREE_WAY_DIAL_BLANK_FLASH:
1033                ar = (AsyncResult) msg.obj;
1034                if (ar.exception == null) {
1035                    postDelayed(
1036                            new Runnable() {
1037                                public void run() {
1038                                    if (mPendingMO != null) {
1039                                        mCi.sendCDMAFeatureCode(mPendingMO.getAddress(),
1040                                                obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA));
1041                                    }
1042                                }
1043                            }, m3WayCallFlashDelay);
1044                } else {
1045                    mPendingMO = null;
1046                    Rlog.w(LOG_TAG, "exception happened on Blank Flash for 3-way call");
1047                }
1048            break;
1049
1050            default:{
1051               throw new RuntimeException("unexpected event not handled");
1052            }
1053        }
1054    }
1055
1056    /**
1057     * Handle Ecm timer to be canceled or re-started
1058     */
1059    private void handleEcmTimer(int action) {
1060        mPhone.handleTimerInEmergencyCallbackMode(action);
1061        switch(action) {
1062        case CDMAPhone.CANCEL_ECM_TIMER: mIsEcmTimerCanceled = true; break;
1063        case CDMAPhone.RESTART_ECM_TIMER: mIsEcmTimerCanceled = false; break;
1064        default:
1065            Rlog.e(LOG_TAG, "handleEcmTimer, unsupported action " + action);
1066        }
1067    }
1068
1069    /**
1070     * Disable data call when emergency call is connected
1071     */
1072    private void disableDataCallInEmergencyCall(String dialString) {
1073        if (PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString)) {
1074            if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall");
1075            mIsInEmergencyCall = true;
1076            mPhone.mDcTracker.setInternalDataEnabled(false);
1077        }
1078    }
1079
1080    /**
1081     * Check and enable data call after an emergency call is dropped if it's
1082     * not in ECM
1083     */
1084    private void checkAndEnableDataCallAfterEmergencyCallDropped() {
1085        if (mIsInEmergencyCall) {
1086            mIsInEmergencyCall = false;
1087            String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
1088            if (Phone.DEBUG_PHONE) {
1089                log("checkAndEnableDataCallAfterEmergencyCallDropped,inEcm=" + inEcm);
1090            }
1091            if (inEcm.compareTo("false") == 0) {
1092                // Re-initiate data connection
1093                mPhone.mDcTracker.setInternalDataEnabled(true);
1094            }
1095        }
1096    }
1097
1098    /**
1099     * Check the MT call to see if it's a new ring or
1100     * a unknown connection.
1101     */
1102    private Connection checkMtFindNewRinging(DriverCall dc, int i) {
1103
1104        Connection newRinging = null;
1105
1106        // it's a ringing call
1107        if (mConnections[i].getCall() == mRingingCall) {
1108            newRinging = mConnections[i];
1109            if (Phone.DEBUG_PHONE) log("Notify new ring " + dc);
1110        } else {
1111            // Something strange happened: a call which is neither
1112            // a ringing call nor the one we created. It could be the
1113            // call collision result from RIL
1114            Rlog.e(LOG_TAG,"Phantom call appeared " + dc);
1115            // If it's a connected call, set the connect time so that
1116            // it's non-zero.  It may not be accurate, but at least
1117            // it won't appear as a Missed Call.
1118            if (dc.state != DriverCall.State.ALERTING
1119                && dc.state != DriverCall.State.DIALING) {
1120                mConnections[i].onConnectedInOrOut();
1121                if (dc.state == DriverCall.State.HOLDING) {
1122                    // We've transitioned into HOLDING
1123                    mConnections[i].onStartedHolding();
1124                }
1125            }
1126        }
1127        return newRinging;
1128    }
1129
1130    /**
1131     * Check if current call is in emergency call
1132     *
1133     * @return true if it is in emergency call
1134     *         false if it is not in emergency call
1135     */
1136    boolean isInEmergencyCall() {
1137        return mIsInEmergencyCall;
1138    }
1139
1140    @Override
1141    protected void log(String msg) {
1142        Rlog.d(LOG_TAG, "[CdmaCallTracker] " + msg);
1143    }
1144
1145    @Override
1146    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1147        pw.println("GsmCallTracker extends:");
1148        super.dump(fd, pw, args);
1149        pw.println("droppedDuringPoll: length=" + mConnections.length);
1150        for(int i=0; i < mConnections.length; i++) {
1151            pw.printf(" mConnections[%d]=%s\n", i, mConnections[i]);
1152        }
1153        pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants);
1154        pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants);
1155        pw.println(" mCallWaitingRegistrants=" + mCallWaitingRegistrants);
1156        pw.println("droppedDuringPoll: size=" + mDroppedDuringPoll.size());
1157        for(int i = 0; i < mDroppedDuringPoll.size(); i++) {
1158            pw.printf( " mDroppedDuringPoll[%d]=%s\n", i, mDroppedDuringPoll.get(i));
1159        }
1160        pw.println(" mRingingCall=" + mRingingCall);
1161        pw.println(" mForegroundCall=" + mForegroundCall);
1162        pw.println(" mBackgroundCall=" + mBackgroundCall);
1163        pw.println(" mPendingMO=" + mPendingMO);
1164        pw.println(" mHangupPendingMO=" + mHangupPendingMO);
1165        pw.println(" mPendingCallInEcm=" + mPendingCallInEcm);
1166        pw.println(" mIsInEmergencyCall=" + mIsInEmergencyCall);
1167        pw.println(" mPhone=" + mPhone);
1168        pw.println(" mDesiredMute=" + mDesiredMute);
1169        pw.println(" mPendingCallClirMode=" + mPendingCallClirMode);
1170        pw.println(" mState=" + mState);
1171        pw.println(" mIsEcmTimerCanceled=" + mIsEcmTimerCanceled);
1172    }
1173}
1174