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