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