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