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