Home | History | Annotate | Download | only in telephony
      1 /*
      2  * Copyright (C) 2015 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;
     18 import android.content.Context;
     19 import android.os.AsyncResult;
     20 import android.os.Handler;
     21 import android.os.Looper;
     22 import android.os.Message;
     23 import android.os.PersistableBundle;
     24 import android.os.PowerManager;
     25 import android.os.Registrant;
     26 import android.os.SystemClock;
     27 import android.telephony.CarrierConfigManager;
     28 import android.telephony.DisconnectCause;
     29 import android.telephony.PhoneNumberUtils;
     30 import android.telephony.Rlog;
     31 import android.telephony.ServiceState;
     32 import android.text.TextUtils;
     33 
     34 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
     35 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
     36 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
     37 import com.android.internal.telephony.uicc.UiccCardApplication;
     38 
     39 /**
     40  * {@hide}
     41  */
     42 public class GsmCdmaConnection extends Connection {
     43     private static final String LOG_TAG = "GsmCdmaConnection";
     44     private static final boolean DBG = true;
     45     private static final boolean VDBG = false;
     46 
     47     //***** Instance Variables
     48 
     49     GsmCdmaCallTracker mOwner;
     50     GsmCdmaCall mParent;
     51 
     52     boolean mDisconnected;
     53 
     54     int mIndex;          // index in GsmCdmaCallTracker.connections[], -1 if unassigned
     55                         // The GsmCdma index is 1 + this
     56 
     57     /*
     58      * These time/timespan values are based on System.currentTimeMillis(),
     59      * i.e., "wall clock" time.
     60      */
     61     long mDisconnectTime;
     62 
     63     UUSInfo mUusInfo;
     64     int mPreciseCause = 0;
     65     String mVendorCause;
     66 
     67     Connection mOrigConnection;
     68 
     69     Handler mHandler;
     70 
     71     private PowerManager.WakeLock mPartialWakeLock;
     72 
     73     private boolean mIsEmergencyCall = false;
     74 
     75     // The cached delay to be used between DTMF tones fetched from carrier config.
     76     private int mDtmfToneDelay = 0;
     77 
     78     //***** Event Constants
     79     static final int EVENT_DTMF_DONE = 1;
     80     static final int EVENT_PAUSE_DONE = 2;
     81     static final int EVENT_NEXT_POST_DIAL = 3;
     82     static final int EVENT_WAKE_LOCK_TIMEOUT = 4;
     83     static final int EVENT_DTMF_DELAY_DONE = 5;
     84 
     85     //***** Constants
     86     static final int PAUSE_DELAY_MILLIS_GSM = 3 * 1000;
     87     static final int PAUSE_DELAY_MILLIS_CDMA = 2 * 1000;
     88     static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000;
     89 
     90     //***** Inner Classes
     91 
     92     class MyHandler extends Handler {
     93         MyHandler(Looper l) {super(l);}
     94 
     95         @Override
     96         public void
     97         handleMessage(Message msg) {
     98 
     99             switch (msg.what) {
    100                 case EVENT_NEXT_POST_DIAL:
    101                 case EVENT_DTMF_DELAY_DONE:
    102                 case EVENT_PAUSE_DONE:
    103                     processNextPostDialChar();
    104                     break;
    105                 case EVENT_WAKE_LOCK_TIMEOUT:
    106                     releaseWakeLock();
    107                     break;
    108                 case EVENT_DTMF_DONE:
    109                     // We may need to add a delay specified by carrier between DTMF tones that are
    110                     // sent out.
    111                     mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_DTMF_DELAY_DONE),
    112                             mDtmfToneDelay);
    113                     break;
    114             }
    115         }
    116     }
    117 
    118     //***** Constructors
    119 
    120     /** This is probably an MT call that we first saw in a CLCC response or a hand over. */
    121     public GsmCdmaConnection (GsmCdmaPhone phone, DriverCall dc, GsmCdmaCallTracker ct, int index) {
    122         super(phone.getPhoneType());
    123         createWakeLock(phone.getContext());
    124         acquireWakeLock();
    125 
    126         mOwner = ct;
    127         mHandler = new MyHandler(mOwner.getLooper());
    128 
    129         mAddress = dc.number;
    130         mIsEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(phone.getContext(), mAddress);
    131         mIsIncoming = dc.isMT;
    132         mCreateTime = System.currentTimeMillis();
    133         mCnapName = dc.name;
    134         mCnapNamePresentation = dc.namePresentation;
    135         mNumberPresentation = dc.numberPresentation;
    136         mUusInfo = dc.uusInfo;
    137 
    138         mIndex = index;
    139 
    140         mParent = parentFromDCState(dc.state);
    141         mParent.attach(this, dc);
    142 
    143         fetchDtmfToneDelay(phone);
    144 
    145         setAudioQuality(getAudioQualityFromDC(dc.audioQuality));
    146     }
    147 
    148     /** This is an MO call, created when dialing */
    149     public GsmCdmaConnection (GsmCdmaPhone phone, String dialString, GsmCdmaCallTracker ct,
    150                               GsmCdmaCall parent, boolean isEmergencyCall) {
    151         super(phone.getPhoneType());
    152         createWakeLock(phone.getContext());
    153         acquireWakeLock();
    154 
    155         mOwner = ct;
    156         mHandler = new MyHandler(mOwner.getLooper());
    157 
    158         if (isPhoneTypeGsm()) {
    159             mDialString = dialString;
    160         } else {
    161             Rlog.d(LOG_TAG, "[GsmCdmaConn] GsmCdmaConnection: dialString=" +
    162                     maskDialString(dialString));
    163             dialString = formatDialString(dialString);
    164             Rlog.d(LOG_TAG,
    165                     "[GsmCdmaConn] GsmCdmaConnection:formated dialString=" +
    166                             maskDialString(dialString));
    167         }
    168 
    169         mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
    170         mIsEmergencyCall = isEmergencyCall;
    171         mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
    172 
    173         mIndex = -1;
    174 
    175         mIsIncoming = false;
    176         mCnapName = null;
    177         mCnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
    178         mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
    179         mCreateTime = System.currentTimeMillis();
    180 
    181         if (parent != null) {
    182             mParent = parent;
    183             if (isPhoneTypeGsm()) {
    184                 parent.attachFake(this, GsmCdmaCall.State.DIALING);
    185             } else {
    186                 //for the three way call case, not change parent state
    187                 if (parent.mState == GsmCdmaCall.State.ACTIVE) {
    188                     parent.attachFake(this, GsmCdmaCall.State.ACTIVE);
    189                 } else {
    190                     parent.attachFake(this, GsmCdmaCall.State.DIALING);
    191                 }
    192 
    193             }
    194         }
    195 
    196         fetchDtmfToneDelay(phone);
    197     }
    198 
    199     //CDMA
    200     /** This is a Call waiting call*/
    201     public GsmCdmaConnection(Context context, CdmaCallWaitingNotification cw, GsmCdmaCallTracker ct,
    202                              GsmCdmaCall parent) {
    203         super(parent.getPhone().getPhoneType());
    204         createWakeLock(context);
    205         acquireWakeLock();
    206 
    207         mOwner = ct;
    208         mHandler = new MyHandler(mOwner.getLooper());
    209         mAddress = cw.number;
    210         mNumberPresentation = cw.numberPresentation;
    211         mCnapName = cw.name;
    212         mCnapNamePresentation = cw.namePresentation;
    213         mIndex = -1;
    214         mIsIncoming = true;
    215         mCreateTime = System.currentTimeMillis();
    216         mConnectTime = 0;
    217         mParent = parent;
    218         parent.attachFake(this, GsmCdmaCall.State.WAITING);
    219     }
    220 
    221 
    222     public void dispose() {
    223         clearPostDialListeners();
    224         if (mParent != null) {
    225             mParent.detach(this);
    226         }
    227         releaseAllWakeLocks();
    228     }
    229 
    230     static boolean equalsHandlesNulls(Object a, Object b) {
    231         return (a == null) ? (b == null) : a.equals (b);
    232     }
    233 
    234     static boolean
    235     equalsBaseDialString (String a, String b) {
    236         return (a == null) ? (b == null) : (b != null && a.startsWith (b));
    237     }
    238 
    239     //CDMA
    240     /**
    241      * format original dial string
    242      * 1) convert international dialing prefix "+" to
    243      *    string specified per region
    244      *
    245      * 2) handle corner cases for PAUSE/WAIT dialing:
    246      *
    247      *    If PAUSE/WAIT sequence at the end, ignore them.
    248      *
    249      *    If consecutive PAUSE/WAIT sequence in the middle of the string,
    250      *    and if there is any WAIT in PAUSE/WAIT sequence, treat them like WAIT.
    251      */
    252     public static String formatDialString(String phoneNumber) {
    253         /**
    254          * TODO(cleanup): This function should move to PhoneNumberUtils, and
    255          * tests should be added.
    256          */
    257 
    258         if (phoneNumber == null) {
    259             return null;
    260         }
    261         int length = phoneNumber.length();
    262         StringBuilder ret = new StringBuilder();
    263         char c;
    264         int currIndex = 0;
    265 
    266         while (currIndex < length) {
    267             c = phoneNumber.charAt(currIndex);
    268             if (isPause(c) || isWait(c)) {
    269                 if (currIndex < length - 1) {
    270                     // if PW not at the end
    271                     int nextIndex = findNextPCharOrNonPOrNonWCharIndex(phoneNumber, currIndex);
    272                     // If there is non PW char following PW sequence
    273                     if (nextIndex < length) {
    274                         char pC = findPOrWCharToAppend(phoneNumber, currIndex, nextIndex);
    275                         ret.append(pC);
    276                         // If PW char sequence has more than 2 PW characters,
    277                         // skip to the last PW character since the sequence already be
    278                         // converted to WAIT character
    279                         if (nextIndex > (currIndex + 1)) {
    280                             currIndex = nextIndex - 1;
    281                         }
    282                     } else if (nextIndex == length) {
    283                         // It means PW characters at the end, ignore
    284                         currIndex = length - 1;
    285                     }
    286                 }
    287             } else {
    288                 ret.append(c);
    289             }
    290             currIndex++;
    291         }
    292         return PhoneNumberUtils.cdmaCheckAndProcessPlusCode(ret.toString());
    293     }
    294 
    295     /*package*/ boolean
    296     compareTo(DriverCall c) {
    297         // On mobile originated (MO) calls, the phone number may have changed
    298         // due to a SIM Toolkit call control modification.
    299         //
    300         // We assume we know when MO calls are created (since we created them)
    301         // and therefore don't need to compare the phone number anyway.
    302         if (! (mIsIncoming || c.isMT)) return true;
    303 
    304         // A new call appearing by SRVCC may have invalid number
    305         //  if IMS service is not tightly coupled with cellular modem stack.
    306         // Thus we prefer the preexisting handover connection instance.
    307         if (isPhoneTypeGsm() && mOrigConnection != null) return true;
    308 
    309         // ... but we can compare phone numbers on MT calls, and we have
    310         // no control over when they begin, so we might as well
    311 
    312         String cAddress = PhoneNumberUtils.stringFromStringAndTOA(c.number, c.TOA);
    313         return mIsIncoming == c.isMT && equalsHandlesNulls(mAddress, cAddress);
    314     }
    315 
    316     @Override
    317     public String getOrigDialString(){
    318         return mDialString;
    319     }
    320 
    321     @Override
    322     public GsmCdmaCall getCall() {
    323         return mParent;
    324     }
    325 
    326     @Override
    327     public long getDisconnectTime() {
    328         return mDisconnectTime;
    329     }
    330 
    331     @Override
    332     public long getHoldDurationMillis() {
    333         if (getState() != GsmCdmaCall.State.HOLDING) {
    334             // If not holding, return 0
    335             return 0;
    336         } else {
    337             return SystemClock.elapsedRealtime() - mHoldingStartTime;
    338         }
    339     }
    340 
    341     @Override
    342     public GsmCdmaCall.State getState() {
    343         if (mDisconnected) {
    344             return GsmCdmaCall.State.DISCONNECTED;
    345         } else {
    346             return super.getState();
    347         }
    348     }
    349 
    350     @Override
    351     public void hangup() throws CallStateException {
    352         if (!mDisconnected) {
    353             mOwner.hangup(this);
    354         } else {
    355             throw new CallStateException ("disconnected");
    356         }
    357     }
    358 
    359     @Override
    360     public void deflect(String number) throws CallStateException {
    361         // Deflect is not supported.
    362         throw new CallStateException ("deflect is not supported for CS");
    363     }
    364 
    365     @Override
    366     public void separate() throws CallStateException {
    367         if (!mDisconnected) {
    368             mOwner.separate(this);
    369         } else {
    370             throw new CallStateException ("disconnected");
    371         }
    372     }
    373 
    374     @Override
    375     public void proceedAfterWaitChar() {
    376         if (mPostDialState != PostDialState.WAIT) {
    377             Rlog.w(LOG_TAG, "GsmCdmaConnection.proceedAfterWaitChar(): Expected "
    378                     + "getPostDialState() to be WAIT but was " + mPostDialState);
    379             return;
    380         }
    381 
    382         setPostDialState(PostDialState.STARTED);
    383 
    384         processNextPostDialChar();
    385     }
    386 
    387     @Override
    388     public void proceedAfterWildChar(String str) {
    389         if (mPostDialState != PostDialState.WILD) {
    390             Rlog.w(LOG_TAG, "GsmCdmaConnection.proceedAfterWaitChar(): Expected "
    391                 + "getPostDialState() to be WILD but was " + mPostDialState);
    392             return;
    393         }
    394 
    395         setPostDialState(PostDialState.STARTED);
    396 
    397         // make a new postDialString, with the wild char replacement string
    398         // at the beginning, followed by the remaining postDialString.
    399 
    400         StringBuilder buf = new StringBuilder(str);
    401         buf.append(mPostDialString.substring(mNextPostDialChar));
    402         mPostDialString = buf.toString();
    403         mNextPostDialChar = 0;
    404         if (Phone.DEBUG_PHONE) {
    405             log("proceedAfterWildChar: new postDialString is " +
    406                     mPostDialString);
    407         }
    408 
    409         processNextPostDialChar();
    410     }
    411 
    412     @Override
    413     public void cancelPostDial() {
    414         setPostDialState(PostDialState.CANCELLED);
    415     }
    416 
    417     /**
    418      * Called when this Connection is being hung up locally (eg, user pressed "end")
    419      * Note that at this point, the hangup request has been dispatched to the radio
    420      * but no response has yet been received so update() has not yet been called
    421      */
    422     void
    423     onHangupLocal() {
    424         mCause = DisconnectCause.LOCAL;
    425         mPreciseCause = 0;
    426         mVendorCause = null;
    427     }
    428 
    429     /**
    430      * Maps RIL call disconnect code to {@link DisconnectCause}.
    431      * @param causeCode RIL disconnect code
    432      * @return the corresponding value from {@link DisconnectCause}
    433      */
    434     int disconnectCauseFromCode(int causeCode) {
    435         /**
    436          * See 22.001 Annex F.4 for mapping of cause codes
    437          * to local tones
    438          */
    439 
    440         switch (causeCode) {
    441             case CallFailCause.USER_BUSY:
    442                 return DisconnectCause.BUSY;
    443 
    444             case CallFailCause.NO_CIRCUIT_AVAIL:
    445             case CallFailCause.TEMPORARY_FAILURE:
    446             case CallFailCause.SWITCHING_CONGESTION:
    447             case CallFailCause.CHANNEL_NOT_AVAIL:
    448             case CallFailCause.QOS_NOT_AVAIL:
    449             case CallFailCause.BEARER_NOT_AVAIL:
    450                 return DisconnectCause.CONGESTION;
    451 
    452             case CallFailCause.EMERGENCY_TEMP_FAILURE:
    453                 return DisconnectCause.EMERGENCY_TEMP_FAILURE;
    454             case CallFailCause.EMERGENCY_PERM_FAILURE:
    455                 return DisconnectCause.EMERGENCY_PERM_FAILURE;
    456 
    457             case CallFailCause.ACM_LIMIT_EXCEEDED:
    458                 return DisconnectCause.LIMIT_EXCEEDED;
    459 
    460             case CallFailCause.OPERATOR_DETERMINED_BARRING:
    461             case CallFailCause.CALL_BARRED:
    462                 return DisconnectCause.CALL_BARRED;
    463 
    464             case CallFailCause.FDN_BLOCKED:
    465                 return DisconnectCause.FDN_BLOCKED;
    466 
    467             case CallFailCause.IMEI_NOT_ACCEPTED:
    468                 return DisconnectCause.IMEI_NOT_ACCEPTED;
    469 
    470             case CallFailCause.UNOBTAINABLE_NUMBER:
    471                 return DisconnectCause.UNOBTAINABLE_NUMBER;
    472 
    473             case CallFailCause.DIAL_MODIFIED_TO_USSD:
    474                 return DisconnectCause.DIAL_MODIFIED_TO_USSD;
    475 
    476             case CallFailCause.DIAL_MODIFIED_TO_SS:
    477                 return DisconnectCause.DIAL_MODIFIED_TO_SS;
    478 
    479             case CallFailCause.DIAL_MODIFIED_TO_DIAL:
    480                 return DisconnectCause.DIAL_MODIFIED_TO_DIAL;
    481 
    482             case CallFailCause.CDMA_LOCKED_UNTIL_POWER_CYCLE:
    483                 return DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE;
    484 
    485             case CallFailCause.CDMA_DROP:
    486                 return DisconnectCause.CDMA_DROP;
    487 
    488             case CallFailCause.CDMA_INTERCEPT:
    489                 return DisconnectCause.CDMA_INTERCEPT;
    490 
    491             case CallFailCause.CDMA_REORDER:
    492                 return DisconnectCause.CDMA_REORDER;
    493 
    494             case CallFailCause.CDMA_SO_REJECT:
    495                 return DisconnectCause.CDMA_SO_REJECT;
    496 
    497             case CallFailCause.CDMA_RETRY_ORDER:
    498                 return DisconnectCause.CDMA_RETRY_ORDER;
    499 
    500             case CallFailCause.CDMA_ACCESS_FAILURE:
    501                 return DisconnectCause.CDMA_ACCESS_FAILURE;
    502 
    503             case CallFailCause.CDMA_PREEMPTED:
    504                 return DisconnectCause.CDMA_PREEMPTED;
    505 
    506             case CallFailCause.CDMA_NOT_EMERGENCY:
    507                 return DisconnectCause.CDMA_NOT_EMERGENCY;
    508 
    509             case CallFailCause.CDMA_ACCESS_BLOCKED:
    510                 return DisconnectCause.CDMA_ACCESS_BLOCKED;
    511 
    512             case CallFailCause.NORMAL_UNSPECIFIED:
    513                 return DisconnectCause.NORMAL_UNSPECIFIED;
    514 
    515             case CallFailCause.USER_ALERTING_NO_ANSWER:
    516                 return DisconnectCause.TIMED_OUT;
    517 
    518             case CallFailCause.ERROR_UNSPECIFIED:
    519             case CallFailCause.NORMAL_CLEARING:
    520             default:
    521                 GsmCdmaPhone phone = mOwner.getPhone();
    522                 int serviceState = phone.getServiceState().getState();
    523                 UiccCardApplication cardApp = phone.getUiccCardApplication();
    524                 AppState uiccAppState = (cardApp != null) ? cardApp.getState() :
    525                         AppState.APPSTATE_UNKNOWN;
    526                 if (serviceState == ServiceState.STATE_POWER_OFF) {
    527                     return DisconnectCause.POWER_OFF;
    528                 }
    529                 if (!mIsEmergencyCall) {
    530                     // Only send OUT_OF_SERVICE if it is not an emergency call. We can still
    531                     // technically be in STATE_OUT_OF_SERVICE or STATE_EMERGENCY_ONLY during
    532                     // an emergency call and when it ends, we do not want to mistakenly generate
    533                     // an OUT_OF_SERVICE disconnect cause during normal call ending.
    534                     if ((serviceState == ServiceState.STATE_OUT_OF_SERVICE
    535                             || serviceState == ServiceState.STATE_EMERGENCY_ONLY)) {
    536                         return DisconnectCause.OUT_OF_SERVICE;
    537                     }
    538                     // If we are placing an emergency call and the SIM is currently PIN/PUK
    539                     // locked the AppState will always not be equal to APPSTATE_READY.
    540                     if (uiccAppState != AppState.APPSTATE_READY) {
    541                         if (isPhoneTypeGsm()) {
    542                             return DisconnectCause.ICC_ERROR;
    543                         } else { // CDMA
    544                             if (phone.mCdmaSubscriptionSource ==
    545                                     CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM) {
    546                                 return DisconnectCause.ICC_ERROR;
    547                             }
    548                         }
    549                     }
    550                 }
    551                 if (isPhoneTypeGsm()) {
    552                     if (causeCode == CallFailCause.ERROR_UNSPECIFIED) {
    553                         if (phone.mSST.mRestrictedState.isCsRestricted()) {
    554                             return DisconnectCause.CS_RESTRICTED;
    555                         } else if (phone.mSST.mRestrictedState.isCsEmergencyRestricted()) {
    556                             return DisconnectCause.CS_RESTRICTED_EMERGENCY;
    557                         } else if (phone.mSST.mRestrictedState.isCsNormalRestricted()) {
    558                             return DisconnectCause.CS_RESTRICTED_NORMAL;
    559                         }
    560                     }
    561                 }
    562                 if (causeCode == CallFailCause.NORMAL_CLEARING) {
    563                     return DisconnectCause.NORMAL;
    564                 }
    565                 // If nothing else matches, report unknown call drop reason
    566                 // to app, not NORMAL call end.
    567                 return DisconnectCause.ERROR_UNSPECIFIED;
    568         }
    569     }
    570 
    571     /*package*/ void
    572     onRemoteDisconnect(int causeCode, String vendorCause) {
    573         this.mPreciseCause = causeCode;
    574         this.mVendorCause = vendorCause;
    575         onDisconnect(disconnectCauseFromCode(causeCode));
    576     }
    577 
    578     /**
    579      * Called when the radio indicates the connection has been disconnected.
    580      * @param cause call disconnect cause; values are defined in {@link DisconnectCause}
    581      */
    582     @Override
    583     public boolean onDisconnect(int cause) {
    584         boolean changed = false;
    585 
    586         mCause = cause;
    587 
    588         if (!mDisconnected) {
    589             doDisconnect();
    590 
    591             if (DBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
    592 
    593             mOwner.getPhone().notifyDisconnect(this);
    594             notifyDisconnect(cause);
    595 
    596             if (mParent != null) {
    597                 changed = mParent.connectionDisconnected(this);
    598             }
    599 
    600             mOrigConnection = null;
    601         }
    602         clearPostDialListeners();
    603         releaseWakeLock();
    604         return changed;
    605     }
    606 
    607     //CDMA
    608     /** Called when the call waiting connection has been hung up */
    609     /*package*/ void
    610     onLocalDisconnect() {
    611         if (!mDisconnected) {
    612             doDisconnect();
    613             if (VDBG) Rlog.d(LOG_TAG, "onLoalDisconnect" );
    614 
    615             if (mParent != null) {
    616                 mParent.detach(this);
    617             }
    618         }
    619         releaseWakeLock();
    620     }
    621 
    622     // Returns true if state has changed, false if nothing changed
    623     public boolean
    624     update (DriverCall dc) {
    625         GsmCdmaCall newParent;
    626         boolean changed = false;
    627         boolean wasConnectingInOrOut = isConnectingInOrOut();
    628         boolean wasHolding = (getState() == GsmCdmaCall.State.HOLDING);
    629 
    630         newParent = parentFromDCState(dc.state);
    631 
    632         if (Phone.DEBUG_PHONE) log("parent= " +mParent +", newParent= " + newParent);
    633 
    634         //Ignore dc.number and dc.name in case of a handover connection
    635         if (isPhoneTypeGsm() && mOrigConnection != null) {
    636             if (Phone.DEBUG_PHONE) log("update: mOrigConnection is not null");
    637         } else if (isIncoming()) {
    638             if (!equalsBaseDialString(mAddress, dc.number) && (!mNumberConverted
    639                     || !equalsBaseDialString(mConvertedNumber, dc.number))) {
    640                 if (Phone.DEBUG_PHONE) log("update: phone # changed!");
    641                 mAddress = dc.number;
    642                 changed = true;
    643             }
    644         }
    645 
    646         int newAudioQuality = getAudioQualityFromDC(dc.audioQuality);
    647         if (getAudioQuality() != newAudioQuality) {
    648             if (Phone.DEBUG_PHONE) {
    649                 log("update: audioQuality # changed!:  "
    650                         + (newAudioQuality == Connection.AUDIO_QUALITY_HIGH_DEFINITION
    651                         ? "high" : "standard"));
    652             }
    653             setAudioQuality(newAudioQuality);
    654             changed = true;
    655         }
    656 
    657         // A null cnapName should be the same as ""
    658         if (TextUtils.isEmpty(dc.name)) {
    659             if (!TextUtils.isEmpty(mCnapName)) {
    660                 changed = true;
    661                 mCnapName = "";
    662             }
    663         } else if (!dc.name.equals(mCnapName)) {
    664             changed = true;
    665             mCnapName = dc.name;
    666         }
    667 
    668         if (Phone.DEBUG_PHONE) log("--dssds----"+mCnapName);
    669         mCnapNamePresentation = dc.namePresentation;
    670         mNumberPresentation = dc.numberPresentation;
    671 
    672         if (newParent != mParent) {
    673             if (mParent != null) {
    674                 mParent.detach(this);
    675             }
    676             newParent.attach(this, dc);
    677             mParent = newParent;
    678             changed = true;
    679         } else {
    680             boolean parentStateChange;
    681             parentStateChange = mParent.update (this, dc);
    682             changed = changed || parentStateChange;
    683         }
    684 
    685         /** Some state-transition events */
    686 
    687         if (Phone.DEBUG_PHONE) log(
    688                 "update: parent=" + mParent +
    689                 ", hasNewParent=" + (newParent != mParent) +
    690                 ", wasConnectingInOrOut=" + wasConnectingInOrOut +
    691                 ", wasHolding=" + wasHolding +
    692                 ", isConnectingInOrOut=" + isConnectingInOrOut() +
    693                 ", changed=" + changed);
    694 
    695 
    696         if (wasConnectingInOrOut && !isConnectingInOrOut()) {
    697             onConnectedInOrOut();
    698         }
    699 
    700         if (changed && !wasHolding && (getState() == GsmCdmaCall.State.HOLDING)) {
    701             // We've transitioned into HOLDING
    702             onStartedHolding();
    703         }
    704 
    705         return changed;
    706     }
    707 
    708     /**
    709      * Called when this Connection is in the foregroundCall
    710      * when a dial is initiated.
    711      * We know we're ACTIVE, and we know we're going to end up
    712      * HOLDING in the backgroundCall
    713      */
    714     void
    715     fakeHoldBeforeDial() {
    716         if (mParent != null) {
    717             mParent.detach(this);
    718         }
    719 
    720         mParent = mOwner.mBackgroundCall;
    721         mParent.attachFake(this, GsmCdmaCall.State.HOLDING);
    722 
    723         onStartedHolding();
    724     }
    725 
    726     /*package*/ int
    727     getGsmCdmaIndex() throws CallStateException {
    728         if (mIndex >= 0) {
    729             return mIndex + 1;
    730         } else {
    731             throw new CallStateException ("GsmCdma index not yet assigned");
    732         }
    733     }
    734 
    735     /**
    736      * An incoming or outgoing call has connected
    737      */
    738     void
    739     onConnectedInOrOut() {
    740         mConnectTime = System.currentTimeMillis();
    741         mConnectTimeReal = SystemClock.elapsedRealtime();
    742         mDuration = 0;
    743 
    744         // bug #678474: incoming call interpreted as missed call, even though
    745         // it sounds like the user has picked up the call.
    746         if (Phone.DEBUG_PHONE) {
    747             log("onConnectedInOrOut: connectTime=" + mConnectTime);
    748         }
    749 
    750         if (!mIsIncoming) {
    751             // outgoing calls only
    752             processNextPostDialChar();
    753         } else {
    754             // Only release wake lock for incoming calls, for outgoing calls the wake lock
    755             // will be released after any pause-dial is completed
    756             releaseWakeLock();
    757         }
    758     }
    759 
    760     private void
    761     doDisconnect() {
    762         mIndex = -1;
    763         mDisconnectTime = System.currentTimeMillis();
    764         mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal;
    765         mDisconnected = true;
    766         clearPostDialListeners();
    767     }
    768 
    769     /*package*/ void
    770     onStartedHolding() {
    771         mHoldingStartTime = SystemClock.elapsedRealtime();
    772     }
    773 
    774     /**
    775      * Performs the appropriate action for a post-dial char, but does not
    776      * notify application. returns false if the character is invalid and
    777      * should be ignored
    778      */
    779     private boolean
    780     processPostDialChar(char c) {
    781         if (PhoneNumberUtils.is12Key(c)) {
    782             mOwner.mCi.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
    783         } else if (isPause(c)) {
    784             if (!isPhoneTypeGsm()) {
    785                 setPostDialState(PostDialState.PAUSE);
    786             }
    787             // From TS 22.101:
    788             // It continues...
    789             // Upon the called party answering the UE shall send the DTMF digits
    790             // automatically to the network after a delay of 3 seconds( 20 ).
    791             // The digits shall be sent according to the procedures and timing
    792             // specified in 3GPP TS 24.008 [13]. The first occurrence of the
    793             // "DTMF Control Digits Separator" shall be used by the ME to
    794             // distinguish between the addressing digits (i.e. the phone number)
    795             // and the DTMF digits. Upon subsequent occurrences of the
    796             // separator,
    797             // the UE shall pause again for 3 seconds ( 20 ) before sending
    798             // any further DTMF digits.
    799             mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_PAUSE_DONE),
    800                     isPhoneTypeGsm() ? PAUSE_DELAY_MILLIS_GSM: PAUSE_DELAY_MILLIS_CDMA);
    801         } else if (isWait(c)) {
    802             setPostDialState(PostDialState.WAIT);
    803         } else if (isWild(c)) {
    804             setPostDialState(PostDialState.WILD);
    805         } else {
    806             return false;
    807         }
    808 
    809         return true;
    810     }
    811 
    812     @Override
    813     public String
    814     getRemainingPostDialString() {
    815         String subStr = super.getRemainingPostDialString();
    816         if (!isPhoneTypeGsm() && !TextUtils.isEmpty(subStr)) {
    817             int wIndex = subStr.indexOf(PhoneNumberUtils.WAIT);
    818             int pIndex = subStr.indexOf(PhoneNumberUtils.PAUSE);
    819 
    820             if (wIndex > 0 && (wIndex < pIndex || pIndex <= 0)) {
    821                 subStr = subStr.substring(0, wIndex);
    822             } else if (pIndex > 0) {
    823                 subStr = subStr.substring(0, pIndex);
    824             }
    825         }
    826         return subStr;
    827     }
    828 
    829     //CDMA
    830     public void updateParent(GsmCdmaCall oldParent, GsmCdmaCall newParent){
    831         if (newParent != oldParent) {
    832             if (oldParent != null) {
    833                 oldParent.detach(this);
    834             }
    835             newParent.attachFake(this, GsmCdmaCall.State.ACTIVE);
    836             mParent = newParent;
    837         }
    838     }
    839 
    840     @Override
    841     protected void finalize()
    842     {
    843         /**
    844          * It is understood that This finalizer is not guaranteed
    845          * to be called and the release lock call is here just in
    846          * case there is some path that doesn't call onDisconnect
    847          * and or onConnectedInOrOut.
    848          */
    849         if (mPartialWakeLock != null && mPartialWakeLock.isHeld()) {
    850             Rlog.e(LOG_TAG, "UNEXPECTED; mPartialWakeLock is held when finalizing.");
    851         }
    852         clearPostDialListeners();
    853         releaseWakeLock();
    854     }
    855 
    856     private void
    857     processNextPostDialChar() {
    858         char c = 0;
    859         Registrant postDialHandler;
    860 
    861         if (mPostDialState == PostDialState.CANCELLED) {
    862             releaseWakeLock();
    863             return;
    864         }
    865 
    866         if (mPostDialString == null ||
    867                 mPostDialString.length() <= mNextPostDialChar) {
    868             setPostDialState(PostDialState.COMPLETE);
    869 
    870             // We were holding a wake lock until pause-dial was complete, so give it up now
    871             releaseWakeLock();
    872 
    873             // notifyMessage.arg1 is 0 on complete
    874             c = 0;
    875         } else {
    876             boolean isValid;
    877 
    878             setPostDialState(PostDialState.STARTED);
    879 
    880             c = mPostDialString.charAt(mNextPostDialChar++);
    881 
    882             isValid = processPostDialChar(c);
    883 
    884             if (!isValid) {
    885                 // Will call processNextPostDialChar
    886                 mHandler.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget();
    887                 // Don't notify application
    888                 Rlog.e(LOG_TAG, "processNextPostDialChar: c=" + c + " isn't valid!");
    889                 return;
    890             }
    891         }
    892 
    893         notifyPostDialListenersNextChar(c);
    894 
    895         // TODO: remove the following code since the handler no longer executes anything.
    896         postDialHandler = mOwner.getPhone().getPostDialHandler();
    897 
    898         Message notifyMessage;
    899 
    900         if (postDialHandler != null
    901                 && (notifyMessage = postDialHandler.messageForRegistrant()) != null) {
    902             // The AsyncResult.result is the Connection object
    903             PostDialState state = mPostDialState;
    904             AsyncResult ar = AsyncResult.forMessage(notifyMessage);
    905             ar.result = this;
    906             ar.userObj = state;
    907 
    908             // arg1 is the character that was/is being processed
    909             notifyMessage.arg1 = c;
    910 
    911             //Rlog.v("GsmCdma", "##### processNextPostDialChar: send msg to postDialHandler, arg1=" + c);
    912             notifyMessage.sendToTarget();
    913         }
    914     }
    915 
    916     /** "connecting" means "has never been ACTIVE" for both incoming
    917      *  and outgoing calls
    918      */
    919     private boolean
    920     isConnectingInOrOut() {
    921         return mParent == null || mParent == mOwner.mRingingCall
    922             || mParent.mState == GsmCdmaCall.State.DIALING
    923             || mParent.mState == GsmCdmaCall.State.ALERTING;
    924     }
    925 
    926     private GsmCdmaCall
    927     parentFromDCState (DriverCall.State state) {
    928         switch (state) {
    929             case ACTIVE:
    930             case DIALING:
    931             case ALERTING:
    932                 return mOwner.mForegroundCall;
    933             //break;
    934 
    935             case HOLDING:
    936                 return mOwner.mBackgroundCall;
    937             //break;
    938 
    939             case INCOMING:
    940             case WAITING:
    941                 return mOwner.mRingingCall;
    942             //break;
    943 
    944             default:
    945                 throw new RuntimeException("illegal call state: " + state);
    946         }
    947     }
    948 
    949     private int getAudioQualityFromDC(int audioQuality) {
    950         switch (audioQuality) {
    951             case DriverCall.AUDIO_QUALITY_AMR_WB:
    952             case DriverCall.AUDIO_QUALITY_EVRC_NW:
    953                 return Connection.AUDIO_QUALITY_HIGH_DEFINITION;
    954             default:
    955                 return Connection.AUDIO_QUALITY_STANDARD;
    956         }
    957     }
    958 
    959     /**
    960      * Set post dial state and acquire wake lock while switching to "started" or "pause"
    961      * state, the wake lock will be released if state switches out of "started" or "pause"
    962      * state or after WAKE_LOCK_TIMEOUT_MILLIS.
    963      * @param s new PostDialState
    964      */
    965     private void setPostDialState(PostDialState s) {
    966         if (s == PostDialState.STARTED ||
    967                 s == PostDialState.PAUSE) {
    968             synchronized (mPartialWakeLock) {
    969                 if (mPartialWakeLock.isHeld()) {
    970                     mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
    971                 } else {
    972                     acquireWakeLock();
    973                 }
    974                 Message msg = mHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
    975                 mHandler.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS);
    976             }
    977         } else {
    978             mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
    979             releaseWakeLock();
    980         }
    981         mPostDialState = s;
    982         notifyPostDialListeners();
    983     }
    984 
    985     private void createWakeLock(Context context) {
    986         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    987         mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
    988     }
    989 
    990     private void acquireWakeLock() {
    991         if (mPartialWakeLock != null) {
    992             synchronized (mPartialWakeLock) {
    993                 log("acquireWakeLock");
    994                 mPartialWakeLock.acquire();
    995             }
    996         }
    997     }
    998 
    999     private void releaseWakeLock() {
   1000         if (mPartialWakeLock != null) {
   1001             synchronized (mPartialWakeLock) {
   1002                 if (mPartialWakeLock.isHeld()) {
   1003                     log("releaseWakeLock");
   1004                     mPartialWakeLock.release();
   1005                 }
   1006             }
   1007         }
   1008     }
   1009 
   1010     private void releaseAllWakeLocks() {
   1011         if (mPartialWakeLock != null) {
   1012             synchronized (mPartialWakeLock) {
   1013                 while (mPartialWakeLock.isHeld()) {
   1014                     mPartialWakeLock.release();
   1015                 }
   1016             }
   1017         }
   1018     }
   1019 
   1020     private static boolean isPause(char c) {
   1021         return c == PhoneNumberUtils.PAUSE;
   1022     }
   1023 
   1024     private static boolean isWait(char c) {
   1025         return c == PhoneNumberUtils.WAIT;
   1026     }
   1027 
   1028     private static boolean isWild(char c) {
   1029         return c == PhoneNumberUtils.WILD;
   1030     }
   1031 
   1032     //CDMA
   1033     // This function is to find the next PAUSE character index if
   1034     // multiple pauses in a row. Otherwise it finds the next non PAUSE or
   1035     // non WAIT character index.
   1036     private static int findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex) {
   1037         boolean wMatched = isWait(phoneNumber.charAt(currIndex));
   1038         int index = currIndex + 1;
   1039         int length = phoneNumber.length();
   1040         while (index < length) {
   1041             char cNext = phoneNumber.charAt(index);
   1042             // if there is any W inside P/W sequence,mark it
   1043             if (isWait(cNext)) {
   1044                 wMatched = true;
   1045             }
   1046             // if any characters other than P/W chars after P/W sequence
   1047             // we break out the loop and append the correct
   1048             if (!isWait(cNext) && !isPause(cNext)) {
   1049                 break;
   1050             }
   1051             index++;
   1052         }
   1053 
   1054         // It means the PAUSE character(s) is in the middle of dial string
   1055         // and it needs to be handled one by one.
   1056         if ((index < length) && (index > (currIndex + 1))  &&
   1057                 ((wMatched == false) && isPause(phoneNumber.charAt(currIndex)))) {
   1058             return (currIndex + 1);
   1059         }
   1060         return index;
   1061     }
   1062 
   1063     // CDMA
   1064     // This function returns either PAUSE or WAIT character to append.
   1065     // It is based on the next non PAUSE/WAIT character in the phoneNumber and the
   1066     // index for the current PAUSE/WAIT character
   1067     private static char findPOrWCharToAppend(String phoneNumber, int currPwIndex,
   1068                                              int nextNonPwCharIndex) {
   1069         char c = phoneNumber.charAt(currPwIndex);
   1070         char ret;
   1071 
   1072         // Append the PW char
   1073         ret = (isPause(c)) ? PhoneNumberUtils.PAUSE : PhoneNumberUtils.WAIT;
   1074 
   1075         // If the nextNonPwCharIndex is greater than currPwIndex + 1,
   1076         // it means the PW sequence contains not only P characters.
   1077         // Since for the sequence that only contains P character,
   1078         // the P character is handled one by one, the nextNonPwCharIndex
   1079         // equals to currPwIndex + 1.
   1080         // In this case, skip P, append W.
   1081         if (nextNonPwCharIndex > (currPwIndex + 1)) {
   1082             ret = PhoneNumberUtils.WAIT;
   1083         }
   1084         return ret;
   1085     }
   1086 
   1087     private String maskDialString(String dialString) {
   1088         if (VDBG) {
   1089             return dialString;
   1090         }
   1091 
   1092         return "<MASKED>";
   1093     }
   1094 
   1095     private void fetchDtmfToneDelay(GsmCdmaPhone phone) {
   1096         CarrierConfigManager configMgr = (CarrierConfigManager)
   1097                 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
   1098         PersistableBundle b = configMgr.getConfigForSubId(phone.getSubId());
   1099         if (b != null) {
   1100             mDtmfToneDelay = b.getInt(phone.getDtmfToneDelayKey());
   1101         }
   1102     }
   1103 
   1104     private boolean isPhoneTypeGsm() {
   1105         return mOwner.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_GSM;
   1106     }
   1107 
   1108     private void log(String msg) {
   1109         Rlog.d(LOG_TAG, "[GsmCdmaConn] " + msg);
   1110     }
   1111 
   1112     @Override
   1113     public int getNumberPresentation() {
   1114         return mNumberPresentation;
   1115     }
   1116 
   1117     @Override
   1118     public UUSInfo getUUSInfo() {
   1119         return mUusInfo;
   1120     }
   1121 
   1122     public int getPreciseDisconnectCause() {
   1123         return mPreciseCause;
   1124     }
   1125 
   1126     @Override
   1127     public String getVendorDisconnectCause() {
   1128         return mVendorCause;
   1129     }
   1130 
   1131     @Override
   1132     public void migrateFrom(Connection c) {
   1133         if (c == null) return;
   1134 
   1135         super.migrateFrom(c);
   1136 
   1137         this.mUusInfo = c.getUUSInfo();
   1138 
   1139         this.setUserData(c.getUserData());
   1140     }
   1141 
   1142     @Override
   1143     public Connection getOrigConnection() {
   1144         return mOrigConnection;
   1145     }
   1146 
   1147     @Override
   1148     public boolean isMultiparty() {
   1149         if (mOrigConnection != null) {
   1150             return mOrigConnection.isMultiparty();
   1151         }
   1152 
   1153         return false;
   1154     }
   1155 }
   1156