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