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*/ boolean
443    onDisconnect(DisconnectCause cause) {
444        boolean changed = false;
445
446        mCause = cause;
447
448        if (!mDisconnected) {
449            doDisconnect();
450            if (VDBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
451
452            mOwner.mPhone.notifyDisconnect(this);
453
454            if (mParent != null) {
455                changed = mParent.connectionDisconnected(this);
456            }
457        }
458        releaseWakeLock();
459        return changed;
460    }
461
462    /** Called when the call waiting connection has been hung up */
463    /*package*/ void
464    onLocalDisconnect() {
465        if (!mDisconnected) {
466            doDisconnect();
467            if (VDBG) Rlog.d(LOG_TAG, "onLoalDisconnect" );
468
469            if (mParent != null) {
470                mParent.detach(this);
471            }
472        }
473        releaseWakeLock();
474    }
475
476    // Returns true if state has changed, false if nothing changed
477    /*package*/ boolean
478    update (DriverCall dc) {
479        CdmaCall newParent;
480        boolean changed = false;
481        boolean wasConnectingInOrOut = isConnectingInOrOut();
482        boolean wasHolding = (getState() == CdmaCall.State.HOLDING);
483
484        newParent = parentFromDCState(dc.state);
485
486        if (Phone.DEBUG_PHONE) log("parent= " +mParent +", newParent= " + newParent);
487
488        if (!equalsHandlesNulls(mAddress, dc.number)) {
489            if (Phone.DEBUG_PHONE) log("update: phone # changed!");
490            mAddress = dc.number;
491            changed = true;
492        }
493
494        // A null cnapName should be the same as ""
495        if (TextUtils.isEmpty(dc.name)) {
496            if (!TextUtils.isEmpty(mCnapName)) {
497                changed = true;
498                mCnapName = "";
499            }
500        } else if (!dc.name.equals(mCnapName)) {
501            changed = true;
502            mCnapName = dc.name;
503        }
504
505        if (Phone.DEBUG_PHONE) log("--dssds----"+mCnapName);
506        mCnapNamePresentation = dc.namePresentation;
507        mNumberPresentation = dc.numberPresentation;
508
509        if (newParent != mParent) {
510            if (mParent != null) {
511                mParent.detach(this);
512            }
513            newParent.attach(this, dc);
514            mParent = newParent;
515            changed = true;
516        } else {
517            boolean parentStateChange;
518            parentStateChange = mParent.update (this, dc);
519            changed = changed || parentStateChange;
520        }
521
522        /** Some state-transition events */
523
524        if (Phone.DEBUG_PHONE) log(
525                "Update, wasConnectingInOrOut=" + wasConnectingInOrOut +
526                ", wasHolding=" + wasHolding +
527                ", isConnectingInOrOut=" + isConnectingInOrOut() +
528                ", changed=" + changed);
529
530
531        if (wasConnectingInOrOut && !isConnectingInOrOut()) {
532            onConnectedInOrOut();
533        }
534
535        if (changed && !wasHolding && (getState() == CdmaCall.State.HOLDING)) {
536            // We've transitioned into HOLDING
537            onStartedHolding();
538        }
539
540        return changed;
541    }
542
543    /**
544     * Called when this Connection is in the foregroundCall
545     * when a dial is initiated.
546     * We know we're ACTIVE, and we know we're going to end up
547     * HOLDING in the backgroundCall
548     */
549    void
550    fakeHoldBeforeDial() {
551        if (mParent != null) {
552            mParent.detach(this);
553        }
554
555        mParent = mOwner.mBackgroundCall;
556        mParent.attachFake(this, CdmaCall.State.HOLDING);
557
558        onStartedHolding();
559    }
560
561    /*package*/ int
562    getCDMAIndex() throws CallStateException {
563        if (mIndex >= 0) {
564            return mIndex + 1;
565        } else {
566            throw new CallStateException ("CDMA connection index not assigned");
567        }
568    }
569
570    /**
571     * An incoming or outgoing call has connected
572     */
573    void
574    onConnectedInOrOut() {
575        mConnectTime = System.currentTimeMillis();
576        mConnectTimeReal = SystemClock.elapsedRealtime();
577        mDuration = 0;
578
579        // bug #678474: incoming call interpreted as missed call, even though
580        // it sounds like the user has picked up the call.
581        if (Phone.DEBUG_PHONE) {
582            log("onConnectedInOrOut: connectTime=" + mConnectTime);
583        }
584
585        if (!mIsIncoming) {
586            // outgoing calls only
587            processNextPostDialChar();
588        } else {
589            // Only release wake lock for incoming calls, for outgoing calls the wake lock
590            // will be released after any pause-dial is completed
591            releaseWakeLock();
592        }
593    }
594
595    private void
596    doDisconnect() {
597       mIndex = -1;
598       mDisconnectTime = System.currentTimeMillis();
599       mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal;
600       mDisconnected = true;
601    }
602
603    /*package*/ void
604    onStartedHolding() {
605        mHoldingStartTime = SystemClock.elapsedRealtime();
606    }
607    /**
608     * Performs the appropriate action for a post-dial char, but does not
609     * notify application. returns false if the character is invalid and
610     * should be ignored
611     */
612    private boolean
613    processPostDialChar(char c) {
614        if (PhoneNumberUtils.is12Key(c)) {
615            mOwner.mCi.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
616        } else if (c == PhoneNumberUtils.PAUSE) {
617            setPostDialState(PostDialState.PAUSE);
618
619            // Upon occurrences of the separator, the UE shall
620            // pause again for 2 seconds before sending any
621            // further DTMF digits.
622            mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_PAUSE_DONE),
623                                            PAUSE_DELAY_MILLIS);
624        } else if (c == PhoneNumberUtils.WAIT) {
625            setPostDialState(PostDialState.WAIT);
626        } else if (c == PhoneNumberUtils.WILD) {
627            setPostDialState(PostDialState.WILD);
628        } else {
629            return false;
630        }
631
632        return true;
633    }
634
635    @Override
636    public String getRemainingPostDialString() {
637        if (mPostDialState == PostDialState.CANCELLED
638                || mPostDialState == PostDialState.COMPLETE
639                || mPostDialString == null
640                || mPostDialString.length() <= mNextPostDialChar) {
641            return "";
642        }
643
644        String subStr = mPostDialString.substring(mNextPostDialChar);
645        if (subStr != null) {
646            int wIndex = subStr.indexOf(PhoneNumberUtils.WAIT);
647            int pIndex = subStr.indexOf(PhoneNumberUtils.PAUSE);
648
649            if (wIndex > 0 && (wIndex < pIndex || pIndex <= 0)) {
650                subStr = subStr.substring(0, wIndex);
651            } else if (pIndex > 0) {
652                subStr = subStr.substring(0, pIndex);
653            }
654        }
655        return subStr;
656    }
657
658    public void updateParent(CdmaCall oldParent, CdmaCall newParent){
659        if (newParent != oldParent) {
660            if (oldParent != null) {
661                oldParent.detach(this);
662            }
663            newParent.attachFake(this, CdmaCall.State.ACTIVE);
664            mParent = newParent;
665        }
666    }
667
668    @Override
669    protected void finalize()
670    {
671        /**
672         * It is understood that This finializer is not guaranteed
673         * to be called and the release lock call is here just in
674         * case there is some path that doesn't call onDisconnect
675         * and or onConnectedInOrOut.
676         */
677        if (mPartialWakeLock.isHeld()) {
678            Rlog.e(LOG_TAG, "[CdmaConn] UNEXPECTED; mPartialWakeLock is held when finalizing.");
679        }
680        releaseWakeLock();
681    }
682
683    void processNextPostDialChar() {
684        char c = 0;
685        Registrant postDialHandler;
686
687        if (mPostDialState == PostDialState.CANCELLED) {
688            releaseWakeLock();
689            //Rlog.v("CDMA", "##### processNextPostDialChar: postDialState == CANCELLED, bail");
690            return;
691        }
692
693        if (mPostDialString == null ||
694                mPostDialString.length() <= mNextPostDialChar) {
695            setPostDialState(PostDialState.COMPLETE);
696
697            // We were holding a wake lock until pause-dial was complete, so give it up now
698            releaseWakeLock();
699
700            // notifyMessage.arg1 is 0 on complete
701            c = 0;
702        } else {
703            boolean isValid;
704
705            setPostDialState(PostDialState.STARTED);
706
707            c = mPostDialString.charAt(mNextPostDialChar++);
708
709            isValid = processPostDialChar(c);
710
711            if (!isValid) {
712                // Will call processNextPostDialChar
713                mHandler.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget();
714                // Don't notify application
715                Rlog.e("CDMA", "processNextPostDialChar: c=" + c + " isn't valid!");
716                return;
717            }
718        }
719
720        postDialHandler = mOwner.mPhone.mPostDialHandler;
721
722        Message notifyMessage;
723
724        if (postDialHandler != null &&
725                (notifyMessage = postDialHandler.messageForRegistrant()) != null) {
726            // The AsyncResult.result is the Connection object
727            PostDialState state = mPostDialState;
728            AsyncResult ar = AsyncResult.forMessage(notifyMessage);
729            ar.result = this;
730            ar.userObj = state;
731
732            // arg1 is the character that was/is being processed
733            notifyMessage.arg1 = c;
734
735            notifyMessage.sendToTarget();
736        }
737    }
738
739
740    /** "connecting" means "has never been ACTIVE" for both incoming
741     *  and outgoing calls
742     */
743    private boolean
744    isConnectingInOrOut() {
745        return mParent == null || mParent == mOwner.mRingingCall
746            || mParent.mState == CdmaCall.State.DIALING
747            || mParent.mState == CdmaCall.State.ALERTING;
748    }
749
750    private CdmaCall
751    parentFromDCState (DriverCall.State state) {
752        switch (state) {
753            case ACTIVE:
754            case DIALING:
755            case ALERTING:
756                return mOwner.mForegroundCall;
757            //break;
758
759            case HOLDING:
760                return mOwner.mBackgroundCall;
761            //break;
762
763            case INCOMING:
764            case WAITING:
765                return mOwner.mRingingCall;
766            //break;
767
768            default:
769                throw new RuntimeException("illegal call state: " + state);
770        }
771    }
772
773    /**
774     * Set post dial state and acquire wake lock while switching to "started" or "wait"
775     * state, the wake lock will be released if state switches out of "started" or "wait"
776     * state or after WAKE_LOCK_TIMEOUT_MILLIS.
777     * @param s new PostDialState
778     */
779    private void setPostDialState(PostDialState s) {
780        if (s == PostDialState.STARTED ||
781                s == PostDialState.PAUSE) {
782            synchronized (mPartialWakeLock) {
783                if (mPartialWakeLock.isHeld()) {
784                    mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
785                } else {
786                    acquireWakeLock();
787                }
788                Message msg = mHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
789                mHandler.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS);
790            }
791        } else {
792            mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
793            releaseWakeLock();
794        }
795        mPostDialState = s;
796    }
797
798    private void createWakeLock(Context context) {
799        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
800        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
801    }
802
803    private void acquireWakeLock() {
804        log("acquireWakeLock");
805        mPartialWakeLock.acquire();
806    }
807
808    private void releaseWakeLock() {
809        synchronized (mPartialWakeLock) {
810            if (mPartialWakeLock.isHeld()) {
811                log("releaseWakeLock");
812                mPartialWakeLock.release();
813            }
814        }
815    }
816
817    private static boolean isPause(char c) {
818        return c == PhoneNumberUtils.PAUSE;
819    }
820
821    private static boolean isWait(char c) {
822        return c == PhoneNumberUtils.WAIT;
823    }
824
825    // This function is to find the next PAUSE character index if
826    // multiple pauses in a row. Otherwise it finds the next non PAUSE or
827    // non WAIT character index.
828    private static int
829    findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex) {
830        boolean wMatched = isWait(phoneNumber.charAt(currIndex));
831        int index = currIndex + 1;
832        int length = phoneNumber.length();
833        while (index < length) {
834            char cNext = phoneNumber.charAt(index);
835            // if there is any W inside P/W sequence,mark it
836            if (isWait(cNext)) {
837                wMatched = true;
838            }
839            // if any characters other than P/W chars after P/W sequence
840            // we break out the loop and append the correct
841            if (!isWait(cNext) && !isPause(cNext)) {
842                break;
843            }
844            index++;
845        }
846
847        // It means the PAUSE character(s) is in the middle of dial string
848        // and it needs to be handled one by one.
849        if ((index < length) && (index > (currIndex + 1))  &&
850            ((wMatched == false) && isPause(phoneNumber.charAt(currIndex)))) {
851            return (currIndex + 1);
852        }
853        return index;
854    }
855
856    // This function returns either PAUSE or WAIT character to append.
857    // It is based on the next non PAUSE/WAIT character in the phoneNumber and the
858    // index for the current PAUSE/WAIT character
859    private static char
860    findPOrWCharToAppend(String phoneNumber, int currPwIndex, int nextNonPwCharIndex) {
861        char c = phoneNumber.charAt(currPwIndex);
862        char ret;
863
864        // Append the PW char
865        ret = (isPause(c)) ? PhoneNumberUtils.PAUSE : PhoneNumberUtils.WAIT;
866
867        // If the nextNonPwCharIndex is greater than currPwIndex + 1,
868        // it means the PW sequence contains not only P characters.
869        // Since for the sequence that only contains P character,
870        // the P character is handled one by one, the nextNonPwCharIndex
871        // equals to currPwIndex + 1.
872        // In this case, skip P, append W.
873        if (nextNonPwCharIndex > (currPwIndex + 1)) {
874            ret = PhoneNumberUtils.WAIT;
875        }
876        return ret;
877    }
878
879    /**
880     * format original dial string
881     * 1) convert international dialing prefix "+" to
882     *    string specified per region
883     *
884     * 2) handle corner cases for PAUSE/WAIT dialing:
885     *
886     *    If PAUSE/WAIT sequence at the end, ignore them.
887     *
888     *    If consecutive PAUSE/WAIT sequence in the middle of the string,
889     *    and if there is any WAIT in PAUSE/WAIT sequence, treat them like WAIT.
890     */
891    public static String formatDialString(String phoneNumber) {
892        /**
893         * TODO(cleanup): This function should move to PhoneNumberUtils, and
894         * tests should be added.
895         */
896
897        if (phoneNumber == null) {
898            return null;
899        }
900        int length = phoneNumber.length();
901        StringBuilder ret = new StringBuilder();
902        char c;
903        int currIndex = 0;
904
905        while (currIndex < length) {
906            c = phoneNumber.charAt(currIndex);
907            if (isPause(c) || isWait(c)) {
908                if (currIndex < length - 1) {
909                    // if PW not at the end
910                    int nextIndex = findNextPCharOrNonPOrNonWCharIndex(phoneNumber, currIndex);
911                    // If there is non PW char following PW sequence
912                    if (nextIndex < length) {
913                        char pC = findPOrWCharToAppend(phoneNumber, currIndex, nextIndex);
914                        ret.append(pC);
915                        // If PW char sequence has more than 2 PW characters,
916                        // skip to the last PW character since the sequence already be
917                        // converted to WAIT character
918                        if (nextIndex > (currIndex + 1)) {
919                            currIndex = nextIndex - 1;
920                        }
921                    } else if (nextIndex == length) {
922                        // It means PW characters at the end, ignore
923                        currIndex = length - 1;
924                    }
925                }
926            } else {
927                ret.append(c);
928            }
929            currIndex++;
930        }
931        return PhoneNumberUtils.cdmaCheckAndProcessPlusCode(ret.toString());
932    }
933
934    private void log(String msg) {
935        Rlog.d(LOG_TAG, "[CDMAConn] " + msg);
936    }
937
938    @Override
939    public int getNumberPresentation() {
940        return mNumberPresentation;
941    }
942
943    @Override
944    public UUSInfo getUUSInfo() {
945        // UUS information not supported in CDMA
946        return null;
947    }
948}
949