CdmaConnection.java revision 22d85a8e3a575a6d01d2c788587971657dfe20c6
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 com.android.internal.telephony.*;
20import android.content.Context;
21import android.os.AsyncResult;
22import android.os.Handler;
23import android.os.Looper;
24import android.os.Message;
25import android.os.PowerManager;
26import android.os.Registrant;
27import android.os.SystemClock;
28import android.telephony.Rlog;
29import android.text.TextUtils;
30
31import android.telephony.PhoneNumberUtils;
32import android.telephony.ServiceState;
33
34import com.android.internal.telephony.uicc.UiccCardApplication;
35import com.android.internal.telephony.uicc.UiccController;
36import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
37
38/**
39 * {@hide}
40 */
41public class CdmaConnection extends Connection {
42    static final String LOG_TAG = "CdmaConnection";
43    private static final boolean VDBG = false;
44
45    //***** Instance Variables
46
47    CdmaCallTracker mOwner;
48    CdmaCall mParent;
49
50
51    String mAddress;             // MAY BE NULL!!!
52    String mDialString;          // outgoing calls only
53    String mPostDialString;      // outgoing calls only
54    boolean mIsIncoming;
55    boolean mDisconnected;
56    int mIndex;          // index in CdmaCallTracker.connections[], -1 if unassigned
57
58    /*
59     * These time/timespan values are based on System.currentTimeMillis(),
60     * i.e., "wall clock" time.
61     */
62    long mCreateTime;
63    long mConnectTime;
64    long mDisconnectTime;
65
66    /*
67     * These time/timespan values are based on SystemClock.elapsedRealTime(),
68     * i.e., time since boot.  They are appropriate for comparison and
69     * calculating deltas.
70     */
71    long mConnectTimeReal;
72    long mDuration;
73    long mHoldingStartTime;  // The time when the Connection last transitioned
74                            // into HOLDING
75
76    int mNextPostDialChar;       // index into postDialString
77
78    DisconnectCause mCause = DisconnectCause.NOT_DISCONNECTED;
79    PostDialState mPostDialState = PostDialState.NOT_STARTED;
80    int mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
81
82
83    Handler mHandler;
84
85    private PowerManager.WakeLock mPartialWakeLock;
86
87    //***** Event Constants
88    static final int EVENT_DTMF_DONE = 1;
89    static final int EVENT_PAUSE_DONE = 2;
90    static final int EVENT_NEXT_POST_DIAL = 3;
91    static final int EVENT_WAKE_LOCK_TIMEOUT = 4;
92
93    //***** Constants
94    static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000;
95    static final int PAUSE_DELAY_MILLIS = 2 * 1000;
96
97    //***** Inner Classes
98
99    class MyHandler extends Handler {
100        MyHandler(Looper l) {super(l);}
101
102        @Override
103        public void
104        handleMessage(Message msg) {
105
106            switch (msg.what) {
107                case EVENT_NEXT_POST_DIAL:
108                case EVENT_DTMF_DONE:
109                case EVENT_PAUSE_DONE:
110                    processNextPostDialChar();
111                    break;
112                case EVENT_WAKE_LOCK_TIMEOUT:
113                    releaseWakeLock();
114                    break;
115            }
116        }
117    }
118
119    //***** Constructors
120
121    /** This is probably an MT call that we first saw in a CLCC response */
122    /*package*/
123    CdmaConnection (Context context, DriverCall dc, CdmaCallTracker ct, int index) {
124        createWakeLock(context);
125        acquireWakeLock();
126
127        mOwner = ct;
128        mHandler = new MyHandler(mOwner.getLooper());
129
130        mAddress = dc.number;
131
132        mIsIncoming = dc.isMT;
133        mCreateTime = System.currentTimeMillis();
134        mCnapName = dc.name;
135        mCnapNamePresentation = dc.namePresentation;
136        mNumberPresentation = dc.numberPresentation;
137
138        mIndex = index;
139
140        mParent = parentFromDCState (dc.state);
141        mParent.attach(this, dc);
142    }
143
144    /** This is an MO call/three way call, created when dialing */
145    /*package*/
146    CdmaConnection(Context context, String dialString, CdmaCallTracker ct, CdmaCall parent) {
147        createWakeLock(context);
148        acquireWakeLock();
149
150        mOwner = ct;
151        mHandler = new MyHandler(mOwner.getLooper());
152
153        mDialString = dialString;
154        Rlog.d(LOG_TAG, "[CDMAConn] CdmaConnection: dialString=" + dialString);
155        dialString = formatDialString(dialString);
156        Rlog.d(LOG_TAG, "[CDMAConn] CdmaConnection:formated dialString=" + dialString);
157
158        mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
159        mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
160
161        mIndex = -1;
162
163        mIsIncoming = false;
164        mCnapName = null;
165        mCnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
166        mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
167        mCreateTime = System.currentTimeMillis();
168
169        if (parent != null) {
170            mParent = parent;
171
172            //for the three way call case, not change parent state
173            if (parent.mState == CdmaCall.State.ACTIVE) {
174                parent.attachFake(this, CdmaCall.State.ACTIVE);
175            } else {
176                parent.attachFake(this, CdmaCall.State.DIALING);
177            }
178        }
179    }
180
181    /** This is a Call waiting call*/
182    CdmaConnection(Context context, CdmaCallWaitingNotification cw, CdmaCallTracker ct,
183            CdmaCall parent) {
184        createWakeLock(context);
185        acquireWakeLock();
186
187        mOwner = ct;
188        mHandler = new MyHandler(mOwner.getLooper());
189        mAddress = cw.number;
190        mNumberPresentation = cw.numberPresentation;
191        mCnapName = cw.name;
192        mCnapNamePresentation = cw.namePresentation;
193        mIndex = -1;
194        mIsIncoming = true;
195        mCreateTime = System.currentTimeMillis();
196        mConnectTime = 0;
197        mParent = parent;
198        parent.attachFake(this, CdmaCall.State.WAITING);
199    }
200
201    public void dispose() {
202    }
203
204    static boolean
205    equalsHandlesNulls (Object a, Object b) {
206        return (a == null) ? (b == null) : a.equals (b);
207    }
208
209    /*package*/ boolean
210    compareTo(DriverCall c) {
211        // On mobile originated (MO) calls, the phone number may have changed
212        // due to a SIM Toolkit call control modification.
213        //
214        // We assume we know when MO calls are created (since we created them)
215        // and therefore don't need to compare the phone number anyway.
216        if (! (mIsIncoming || c.isMT)) return true;
217
218        // ... but we can compare phone numbers on MT calls, and we have
219        // no control over when they begin, so we might as well
220
221        String cAddress = PhoneNumberUtils.stringFromStringAndTOA(c.number, c.TOA);
222        return mIsIncoming == c.isMT && equalsHandlesNulls(mAddress, cAddress);
223    }
224
225
226    @Override
227    public String getOrigDialString(){
228        return mDialString;
229    }
230
231    @Override
232    public String getAddress() {
233        return mAddress;
234    }
235
236    @Override
237    public CdmaCall getCall() {
238        return mParent;
239    }
240
241    @Override
242    public long getCreateTime() {
243        return mCreateTime;
244    }
245
246    @Override
247    public long getConnectTime() {
248        return mConnectTime;
249    }
250
251    @Override
252    public long getDisconnectTime() {
253        return mDisconnectTime;
254    }
255
256    @Override
257    public long getDurationMillis() {
258        if (mConnectTimeReal == 0) {
259            return 0;
260        } else if (mDuration == 0) {
261            return SystemClock.elapsedRealtime() - mConnectTimeReal;
262        } else {
263            return mDuration;
264        }
265    }
266
267    @Override
268    public long getHoldDurationMillis() {
269        if (getState() != CdmaCall.State.HOLDING) {
270            // If not holding, return 0
271            return 0;
272        } else {
273            return SystemClock.elapsedRealtime() - mHoldingStartTime;
274        }
275    }
276
277    @Override
278    public DisconnectCause getDisconnectCause() {
279        return mCause;
280    }
281
282    @Override
283    public boolean isIncoming() {
284        return mIsIncoming;
285    }
286
287    @Override
288    public CdmaCall.State getState() {
289        if (mDisconnected) {
290            return CdmaCall.State.DISCONNECTED;
291        } else {
292            return super.getState();
293        }
294    }
295
296    @Override
297    public void hangup() throws CallStateException {
298        if (!mDisconnected) {
299            mOwner.hangup(this);
300        } else {
301            throw new CallStateException ("disconnected");
302        }
303    }
304
305    @Override
306    public void separate() throws CallStateException {
307        if (!mDisconnected) {
308            mOwner.separate(this);
309        } else {
310            throw new CallStateException ("disconnected");
311        }
312    }
313
314    @Override
315    public PostDialState getPostDialState() {
316        return mPostDialState;
317    }
318
319    @Override
320    public void proceedAfterWaitChar() {
321        if (mPostDialState != PostDialState.WAIT) {
322            Rlog.w(LOG_TAG, "CdmaConnection.proceedAfterWaitChar(): Expected "
323                + "getPostDialState() to be WAIT but was " + mPostDialState);
324            return;
325        }
326
327        setPostDialState(PostDialState.STARTED);
328
329        processNextPostDialChar();
330    }
331
332    @Override
333    public void proceedAfterWildChar(String str) {
334        if (mPostDialState != PostDialState.WILD) {
335            Rlog.w(LOG_TAG, "CdmaConnection.proceedAfterWaitChar(): Expected "
336                + "getPostDialState() to be WILD but was " + mPostDialState);
337            return;
338        }
339
340        setPostDialState(PostDialState.STARTED);
341
342        // make a new postDialString, with the wild char replacement string
343        // at the beginning, followed by the remaining postDialString.
344
345        StringBuilder buf = new StringBuilder(str);
346        buf.append(mPostDialString.substring(mNextPostDialChar));
347        mPostDialString = buf.toString();
348        mNextPostDialChar = 0;
349        if (Phone.DEBUG_PHONE) {
350            log("proceedAfterWildChar: new postDialString is " +
351                    mPostDialString);
352        }
353
354        processNextPostDialChar();
355    }
356
357    @Override
358    public void cancelPostDial() {
359        setPostDialState(PostDialState.CANCELLED);
360    }
361
362    /**
363     * Called when this Connection is being hung up locally (eg, user pressed "end")
364     * Note that at this point, the hangup request has been dispatched to the radio
365     * but no response has yet been received so update() has not yet been called
366     */
367    void
368    onHangupLocal() {
369        mCause = DisconnectCause.LOCAL;
370    }
371
372    DisconnectCause
373    disconnectCauseFromCode(int causeCode) {
374        /**
375         * See 22.001 Annex F.4 for mapping of cause codes
376         * to local tones
377         */
378
379        switch (causeCode) {
380            case CallFailCause.USER_BUSY:
381                return DisconnectCause.BUSY;
382            case CallFailCause.NO_CIRCUIT_AVAIL:
383                return DisconnectCause.CONGESTION;
384            case CallFailCause.ACM_LIMIT_EXCEEDED:
385                return DisconnectCause.LIMIT_EXCEEDED;
386            case CallFailCause.CALL_BARRED:
387                return DisconnectCause.CALL_BARRED;
388            case CallFailCause.FDN_BLOCKED:
389                return DisconnectCause.FDN_BLOCKED;
390            case CallFailCause.CDMA_LOCKED_UNTIL_POWER_CYCLE:
391                return DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE;
392            case CallFailCause.CDMA_DROP:
393                return DisconnectCause.CDMA_DROP;
394            case CallFailCause.CDMA_INTERCEPT:
395                return DisconnectCause.CDMA_INTERCEPT;
396            case CallFailCause.CDMA_REORDER:
397                return DisconnectCause.CDMA_REORDER;
398            case CallFailCause.CDMA_SO_REJECT:
399                return DisconnectCause.CDMA_SO_REJECT;
400            case CallFailCause.CDMA_RETRY_ORDER:
401                return DisconnectCause.CDMA_RETRY_ORDER;
402            case CallFailCause.CDMA_ACCESS_FAILURE:
403                return DisconnectCause.CDMA_ACCESS_FAILURE;
404            case CallFailCause.CDMA_PREEMPTED:
405                return DisconnectCause.CDMA_PREEMPTED;
406            case CallFailCause.CDMA_NOT_EMERGENCY:
407                return DisconnectCause.CDMA_NOT_EMERGENCY;
408            case CallFailCause.CDMA_ACCESS_BLOCKED:
409                return DisconnectCause.CDMA_ACCESS_BLOCKED;
410            case CallFailCause.ERROR_UNSPECIFIED:
411            case CallFailCause.NORMAL_CLEARING:
412            default:
413                CDMAPhone phone = mOwner.mPhone;
414                int serviceState = phone.getServiceState().getState();
415                UiccCardApplication app = UiccController
416                        .getInstance()
417                        .getUiccCardApplication(UiccController.APP_FAM_3GPP2);
418                AppState uiccAppState = (app != null) ? app.getState() : AppState.APPSTATE_UNKNOWN;
419                if (serviceState == ServiceState.STATE_POWER_OFF) {
420                    return DisconnectCause.POWER_OFF;
421                } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE
422                        || serviceState == ServiceState.STATE_EMERGENCY_ONLY) {
423                    return DisconnectCause.OUT_OF_SERVICE;
424                } else if (phone.mCdmaSubscriptionSource ==
425                        CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM
426                        && uiccAppState != AppState.APPSTATE_READY) {
427                    return DisconnectCause.ICC_ERROR;
428                } else if (causeCode==CallFailCause.NORMAL_CLEARING) {
429                    return DisconnectCause.NORMAL;
430                } else {
431                    return DisconnectCause.ERROR_UNSPECIFIED;
432                }
433        }
434    }
435
436    /*package*/ void
437    onRemoteDisconnect(int causeCode) {
438        onDisconnect(disconnectCauseFromCode(causeCode));
439    }
440
441    /** Called when the radio indicates the connection has been disconnected */
442    /*package*/ void
443    onDisconnect(DisconnectCause cause) {
444        mCause = cause;
445
446        if (!mDisconnected) {
447            doDisconnect();
448            if (VDBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
449
450            mOwner.mPhone.notifyDisconnect(this);
451
452            if (mParent != null) {
453                mParent.connectionDisconnected(this);
454            }
455        }
456        releaseWakeLock();
457    }
458
459    /** Called when the call waiting connection has been hung up */
460    /*package*/ void
461    onLocalDisconnect() {
462        if (!mDisconnected) {
463            doDisconnect();
464            if (VDBG) Rlog.d(LOG_TAG, "onLoalDisconnect" );
465
466            if (mParent != null) {
467                mParent.detach(this);
468            }
469        }
470        releaseWakeLock();
471    }
472
473    // Returns true if state has changed, false if nothing changed
474    /*package*/ boolean
475    update (DriverCall dc) {
476        CdmaCall newParent;
477        boolean changed = false;
478        boolean wasConnectingInOrOut = isConnectingInOrOut();
479        boolean wasHolding = (getState() == CdmaCall.State.HOLDING);
480
481        newParent = parentFromDCState(dc.state);
482
483        if (Phone.DEBUG_PHONE) log("parent= " +mParent +", newParent= " + newParent);
484
485        if (!equalsHandlesNulls(mAddress, dc.number)) {
486            if (Phone.DEBUG_PHONE) log("update: phone # changed!");
487            mAddress = dc.number;
488            changed = true;
489        }
490
491        // A null cnapName should be the same as ""
492        if (TextUtils.isEmpty(dc.name)) {
493            if (!TextUtils.isEmpty(mCnapName)) {
494                changed = true;
495                mCnapName = "";
496            }
497        } else if (!dc.name.equals(mCnapName)) {
498            changed = true;
499            mCnapName = dc.name;
500        }
501
502        if (Phone.DEBUG_PHONE) log("--dssds----"+mCnapName);
503        mCnapNamePresentation = dc.namePresentation;
504        mNumberPresentation = dc.numberPresentation;
505
506        if (newParent != mParent) {
507            if (mParent != null) {
508                mParent.detach(this);
509            }
510            newParent.attach(this, dc);
511            mParent = newParent;
512            changed = true;
513        } else {
514            boolean parentStateChange;
515            parentStateChange = mParent.update (this, dc);
516            changed = changed || parentStateChange;
517        }
518
519        /** Some state-transition events */
520
521        if (Phone.DEBUG_PHONE) log(
522                "Update, wasConnectingInOrOut=" + wasConnectingInOrOut +
523                ", wasHolding=" + wasHolding +
524                ", isConnectingInOrOut=" + isConnectingInOrOut() +
525                ", changed=" + changed);
526
527
528        if (wasConnectingInOrOut && !isConnectingInOrOut()) {
529            onConnectedInOrOut();
530        }
531
532        if (changed && !wasHolding && (getState() == CdmaCall.State.HOLDING)) {
533            // We've transitioned into HOLDING
534            onStartedHolding();
535        }
536
537        return changed;
538    }
539
540    /**
541     * Called when this Connection is in the foregroundCall
542     * when a dial is initiated.
543     * We know we're ACTIVE, and we know we're going to end up
544     * HOLDING in the backgroundCall
545     */
546    void
547    fakeHoldBeforeDial() {
548        if (mParent != null) {
549            mParent.detach(this);
550        }
551
552        mParent = mOwner.mBackgroundCall;
553        mParent.attachFake(this, CdmaCall.State.HOLDING);
554
555        onStartedHolding();
556    }
557
558    /*package*/ int
559    getCDMAIndex() throws CallStateException {
560        if (mIndex >= 0) {
561            return mIndex + 1;
562        } else {
563            throw new CallStateException ("CDMA connection index not assigned");
564        }
565    }
566
567    /**
568     * An incoming or outgoing call has connected
569     */
570    void
571    onConnectedInOrOut() {
572        mConnectTime = System.currentTimeMillis();
573        mConnectTimeReal = SystemClock.elapsedRealtime();
574        mDuration = 0;
575
576        // bug #678474: incoming call interpreted as missed call, even though
577        // it sounds like the user has picked up the call.
578        if (Phone.DEBUG_PHONE) {
579            log("onConnectedInOrOut: connectTime=" + mConnectTime);
580        }
581
582        if (!mIsIncoming) {
583            // outgoing calls only
584            processNextPostDialChar();
585        } else {
586            // Only release wake lock for incoming calls, for outgoing calls the wake lock
587            // will be released after any pause-dial is completed
588            releaseWakeLock();
589        }
590    }
591
592    private void
593    doDisconnect() {
594       mIndex = -1;
595       mDisconnectTime = System.currentTimeMillis();
596       mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal;
597       mDisconnected = true;
598    }
599
600    /*package*/ void
601    onStartedHolding() {
602        mHoldingStartTime = SystemClock.elapsedRealtime();
603    }
604    /**
605     * Performs the appropriate action for a post-dial char, but does not
606     * notify application. returns false if the character is invalid and
607     * should be ignored
608     */
609    private boolean
610    processPostDialChar(char c) {
611        if (PhoneNumberUtils.is12Key(c)) {
612            mOwner.mCi.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
613        } else if (c == PhoneNumberUtils.PAUSE) {
614            setPostDialState(PostDialState.PAUSE);
615
616            // Upon occurrences of the separator, the UE shall
617            // pause again for 2 seconds before sending any
618            // further DTMF digits.
619            mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_PAUSE_DONE),
620                                            PAUSE_DELAY_MILLIS);
621        } else if (c == PhoneNumberUtils.WAIT) {
622            setPostDialState(PostDialState.WAIT);
623        } else if (c == PhoneNumberUtils.WILD) {
624            setPostDialState(PostDialState.WILD);
625        } else {
626            return false;
627        }
628
629        return true;
630    }
631
632    @Override
633    public String getRemainingPostDialString() {
634        if (mPostDialState == PostDialState.CANCELLED
635                || mPostDialState == PostDialState.COMPLETE
636                || mPostDialString == null
637                || mPostDialString.length() <= mNextPostDialChar) {
638            return "";
639        }
640
641        String subStr = mPostDialString.substring(mNextPostDialChar);
642        if (subStr != null) {
643            int wIndex = subStr.indexOf(PhoneNumberUtils.WAIT);
644            int pIndex = subStr.indexOf(PhoneNumberUtils.PAUSE);
645
646            if (wIndex > 0 && (wIndex < pIndex || pIndex <= 0)) {
647                subStr = subStr.substring(0, wIndex);
648            } else if (pIndex > 0) {
649                subStr = subStr.substring(0, pIndex);
650            }
651        }
652        return subStr;
653    }
654
655    public void updateParent(CdmaCall oldParent, CdmaCall newParent){
656        if (newParent != oldParent) {
657            if (oldParent != null) {
658                oldParent.detach(this);
659            }
660            newParent.attachFake(this, CdmaCall.State.ACTIVE);
661            mParent = newParent;
662        }
663    }
664
665    @Override
666    protected void finalize()
667    {
668        /**
669         * It is understood that This finializer is not guaranteed
670         * to be called and the release lock call is here just in
671         * case there is some path that doesn't call onDisconnect
672         * and or onConnectedInOrOut.
673         */
674        if (mPartialWakeLock.isHeld()) {
675            Rlog.e(LOG_TAG, "[CdmaConn] UNEXPECTED; mPartialWakeLock is held when finalizing.");
676        }
677        releaseWakeLock();
678    }
679
680    void processNextPostDialChar() {
681        char c = 0;
682        Registrant postDialHandler;
683
684        if (mPostDialState == PostDialState.CANCELLED) {
685            releaseWakeLock();
686            //Rlog.v("CDMA", "##### processNextPostDialChar: postDialState == CANCELLED, bail");
687            return;
688        }
689
690        if (mPostDialString == null ||
691                mPostDialString.length() <= mNextPostDialChar) {
692            setPostDialState(PostDialState.COMPLETE);
693
694            // We were holding a wake lock until pause-dial was complete, so give it up now
695            releaseWakeLock();
696
697            // notifyMessage.arg1 is 0 on complete
698            c = 0;
699        } else {
700            boolean isValid;
701
702            setPostDialState(PostDialState.STARTED);
703
704            c = mPostDialString.charAt(mNextPostDialChar++);
705
706            isValid = processPostDialChar(c);
707
708            if (!isValid) {
709                // Will call processNextPostDialChar
710                mHandler.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget();
711                // Don't notify application
712                Rlog.e("CDMA", "processNextPostDialChar: c=" + c + " isn't valid!");
713                return;
714            }
715        }
716
717        postDialHandler = mOwner.mPhone.mPostDialHandler;
718
719        Message notifyMessage;
720
721        if (postDialHandler != null &&
722                (notifyMessage = postDialHandler.messageForRegistrant()) != null) {
723            // The AsyncResult.result is the Connection object
724            PostDialState state = mPostDialState;
725            AsyncResult ar = AsyncResult.forMessage(notifyMessage);
726            ar.result = this;
727            ar.userObj = state;
728
729            // arg1 is the character that was/is being processed
730            notifyMessage.arg1 = c;
731
732            notifyMessage.sendToTarget();
733        }
734    }
735
736
737    /** "connecting" means "has never been ACTIVE" for both incoming
738     *  and outgoing calls
739     */
740    private boolean
741    isConnectingInOrOut() {
742        return mParent == null || mParent == mOwner.mRingingCall
743            || mParent.mState == CdmaCall.State.DIALING
744            || mParent.mState == CdmaCall.State.ALERTING;
745    }
746
747    private CdmaCall
748    parentFromDCState (DriverCall.State state) {
749        switch (state) {
750            case ACTIVE:
751            case DIALING:
752            case ALERTING:
753                return mOwner.mForegroundCall;
754            //break;
755
756            case HOLDING:
757                return mOwner.mBackgroundCall;
758            //break;
759
760            case INCOMING:
761            case WAITING:
762                return mOwner.mRingingCall;
763            //break;
764
765            default:
766                throw new RuntimeException("illegal call state: " + state);
767        }
768    }
769
770    /**
771     * Set post dial state and acquire wake lock while switching to "started" or "wait"
772     * state, the wake lock will be released if state switches out of "started" or "wait"
773     * state or after WAKE_LOCK_TIMEOUT_MILLIS.
774     * @param s new PostDialState
775     */
776    private void setPostDialState(PostDialState s) {
777        if (s == PostDialState.STARTED ||
778                s == PostDialState.PAUSE) {
779            synchronized (mPartialWakeLock) {
780                if (mPartialWakeLock.isHeld()) {
781                    mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
782                } else {
783                    acquireWakeLock();
784                }
785                Message msg = mHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
786                mHandler.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS);
787            }
788        } else {
789            mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
790            releaseWakeLock();
791        }
792        mPostDialState = s;
793    }
794
795    private void createWakeLock(Context context) {
796        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
797        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
798    }
799
800    private void acquireWakeLock() {
801        log("acquireWakeLock");
802        mPartialWakeLock.acquire();
803    }
804
805    private void releaseWakeLock() {
806        synchronized (mPartialWakeLock) {
807            if (mPartialWakeLock.isHeld()) {
808                log("releaseWakeLock");
809                mPartialWakeLock.release();
810            }
811        }
812    }
813
814    private static boolean isPause(char c) {
815        return c == PhoneNumberUtils.PAUSE;
816    }
817
818    private static boolean isWait(char c) {
819        return c == PhoneNumberUtils.WAIT;
820    }
821
822    // This function is to find the next PAUSE character index if
823    // multiple pauses in a row. Otherwise it finds the next non PAUSE or
824    // non WAIT character index.
825    private static int
826    findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex) {
827        boolean wMatched = isWait(phoneNumber.charAt(currIndex));
828        int index = currIndex + 1;
829        int length = phoneNumber.length();
830        while (index < length) {
831            char cNext = phoneNumber.charAt(index);
832            // if there is any W inside P/W sequence,mark it
833            if (isWait(cNext)) {
834                wMatched = true;
835            }
836            // if any characters other than P/W chars after P/W sequence
837            // we break out the loop and append the correct
838            if (!isWait(cNext) && !isPause(cNext)) {
839                break;
840            }
841            index++;
842        }
843
844        // It means the PAUSE character(s) is in the middle of dial string
845        // and it needs to be handled one by one.
846        if ((index < length) && (index > (currIndex + 1))  &&
847            ((wMatched == false) && isPause(phoneNumber.charAt(currIndex)))) {
848            return (currIndex + 1);
849        }
850        return index;
851    }
852
853    // This function returns either PAUSE or WAIT character to append.
854    // It is based on the next non PAUSE/WAIT character in the phoneNumber and the
855    // index for the current PAUSE/WAIT character
856    private static char
857    findPOrWCharToAppend(String phoneNumber, int currPwIndex, int nextNonPwCharIndex) {
858        char c = phoneNumber.charAt(currPwIndex);
859        char ret;
860
861        // Append the PW char
862        ret = (isPause(c)) ? PhoneNumberUtils.PAUSE : PhoneNumberUtils.WAIT;
863
864        // If the nextNonPwCharIndex is greater than currPwIndex + 1,
865        // it means the PW sequence contains not only P characters.
866        // Since for the sequence that only contains P character,
867        // the P character is handled one by one, the nextNonPwCharIndex
868        // equals to currPwIndex + 1.
869        // In this case, skip P, append W.
870        if (nextNonPwCharIndex > (currPwIndex + 1)) {
871            ret = PhoneNumberUtils.WAIT;
872        }
873        return ret;
874    }
875
876    /**
877     * format original dial string
878     * 1) convert international dialing prefix "+" to
879     *    string specified per region
880     *
881     * 2) handle corner cases for PAUSE/WAIT dialing:
882     *
883     *    If PAUSE/WAIT sequence at the end, ignore them.
884     *
885     *    If consecutive PAUSE/WAIT sequence in the middle of the string,
886     *    and if there is any WAIT in PAUSE/WAIT sequence, treat them like WAIT.
887     */
888    public static String formatDialString(String phoneNumber) {
889        /**
890         * TODO(cleanup): This function should move to PhoneNumberUtils, and
891         * tests should be added.
892         */
893
894        if (phoneNumber == null) {
895            return null;
896        }
897        int length = phoneNumber.length();
898        StringBuilder ret = new StringBuilder();
899        char c;
900        int currIndex = 0;
901
902        while (currIndex < length) {
903            c = phoneNumber.charAt(currIndex);
904            if (isPause(c) || isWait(c)) {
905                if (currIndex < length - 1) {
906                    // if PW not at the end
907                    int nextIndex = findNextPCharOrNonPOrNonWCharIndex(phoneNumber, currIndex);
908                    // If there is non PW char following PW sequence
909                    if (nextIndex < length) {
910                        char pC = findPOrWCharToAppend(phoneNumber, currIndex, nextIndex);
911                        ret.append(pC);
912                        // If PW char sequence has more than 2 PW characters,
913                        // skip to the last PW character since the sequence already be
914                        // converted to WAIT character
915                        if (nextIndex > (currIndex + 1)) {
916                            currIndex = nextIndex - 1;
917                        }
918                    } else if (nextIndex == length) {
919                        // It means PW characters at the end, ignore
920                        currIndex = length - 1;
921                    }
922                }
923            } else {
924                ret.append(c);
925            }
926            currIndex++;
927        }
928        return PhoneNumberUtils.cdmaCheckAndProcessPlusCode(ret.toString());
929    }
930
931    private void log(String msg) {
932        Rlog.d(LOG_TAG, "[CDMAConn] " + msg);
933    }
934
935    @Override
936    public int getNumberPresentation() {
937        return mNumberPresentation;
938    }
939
940    @Override
941    public UUSInfo getUUSInfo() {
942        // UUS information not supported in CDMA
943        return null;
944    }
945}
946