Home | History | Annotate | Download | only in gsm
      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.gsm;
     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.PowerManager;
     24 import android.os.Registrant;
     25 import android.os.SystemClock;
     26 import android.telephony.DisconnectCause;
     27 import android.telephony.Rlog;
     28 import android.telephony.PhoneNumberUtils;
     29 import android.telephony.ServiceState;
     30 import android.text.TextUtils;
     31 
     32 import com.android.internal.telephony.*;
     33 import com.android.internal.telephony.uicc.UiccCardApplication;
     34 import com.android.internal.telephony.uicc.UiccController;
     35 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
     36 
     37 /**
     38  * {@hide}
     39  */
     40 public class GsmConnection extends Connection {
     41     private static final String LOG_TAG = "GsmConnection";
     42     private static final boolean DBG = true;
     43 
     44     //***** Instance Variables
     45 
     46     GsmCallTracker mOwner;
     47     GsmCall mParent;
     48 
     49     String mPostDialString;      // outgoing calls only
     50     boolean mDisconnected;
     51 
     52     int mIndex;          // index in GsmCallTracker.connections[], -1 if unassigned
     53                         // The GSM index is 1 + this
     54 
     55     /*
     56      * These time/timespan values are based on System.currentTimeMillis(),
     57      * i.e., "wall clock" time.
     58      */
     59     long mDisconnectTime;
     60 
     61     int mNextPostDialChar;       // index into postDialString
     62 
     63     int mCause = DisconnectCause.NOT_DISCONNECTED;
     64     PostDialState mPostDialState = PostDialState.NOT_STARTED;
     65     UUSInfo mUusInfo;
     66     int mPreciseCause = 0;
     67 
     68     Connection mOrigConnection;
     69 
     70     Handler mHandler;
     71 
     72     private PowerManager.WakeLock mPartialWakeLock;
     73 
     74     //***** Event Constants
     75     static final int EVENT_DTMF_DONE = 1;
     76     static final int EVENT_PAUSE_DONE = 2;
     77     static final int EVENT_NEXT_POST_DIAL = 3;
     78     static final int EVENT_WAKE_LOCK_TIMEOUT = 4;
     79 
     80     //***** Constants
     81     static final int PAUSE_DELAY_MILLIS = 3 * 1000;
     82     static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000;
     83 
     84     //***** Inner Classes
     85 
     86     class MyHandler extends Handler {
     87         MyHandler(Looper l) {super(l);}
     88 
     89         @Override
     90         public void
     91         handleMessage(Message msg) {
     92 
     93             switch (msg.what) {
     94                 case EVENT_NEXT_POST_DIAL:
     95                 case EVENT_DTMF_DONE:
     96                 case EVENT_PAUSE_DONE:
     97                     processNextPostDialChar();
     98                     break;
     99                 case EVENT_WAKE_LOCK_TIMEOUT:
    100                     releaseWakeLock();
    101                     break;
    102             }
    103         }
    104     }
    105 
    106     //***** Constructors
    107 
    108     /** This is probably an MT call that we first saw in a CLCC response */
    109     /*package*/
    110     GsmConnection (Context context, DriverCall dc, GsmCallTracker ct, int index) {
    111         createWakeLock(context);
    112         acquireWakeLock();
    113 
    114         mOwner = ct;
    115         mHandler = new MyHandler(mOwner.getLooper());
    116 
    117         mAddress = dc.number;
    118 
    119         mIsIncoming = dc.isMT;
    120         mCreateTime = System.currentTimeMillis();
    121         mCnapName = dc.name;
    122         mCnapNamePresentation = dc.namePresentation;
    123         mNumberPresentation = dc.numberPresentation;
    124         mUusInfo = dc.uusInfo;
    125 
    126         mIndex = index;
    127 
    128         mParent = parentFromDCState (dc.state);
    129         mParent.attach(this, dc);
    130     }
    131 
    132     /** This is an MO call, created when dialing */
    133     /*package*/
    134     GsmConnection (Context context, String dialString, GsmCallTracker ct, GsmCall parent) {
    135         createWakeLock(context);
    136         acquireWakeLock();
    137 
    138         mOwner = ct;
    139         mHandler = new MyHandler(mOwner.getLooper());
    140 
    141         mDialString = dialString;
    142 
    143         mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
    144         mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
    145 
    146         mIndex = -1;
    147 
    148         mIsIncoming = false;
    149         mCnapName = null;
    150         mCnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
    151         mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
    152         mCreateTime = System.currentTimeMillis();
    153 
    154         mParent = parent;
    155         parent.attachFake(this, GsmCall.State.DIALING);
    156     }
    157 
    158     public void dispose() {
    159     }
    160 
    161     static boolean
    162     equalsHandlesNulls (Object a, Object b) {
    163         return (a == null) ? (b == null) : a.equals (b);
    164     }
    165 
    166     /*package*/ boolean
    167     compareTo(DriverCall c) {
    168         // On mobile originated (MO) calls, the phone number may have changed
    169         // due to a SIM Toolkit call control modification.
    170         //
    171         // We assume we know when MO calls are created (since we created them)
    172         // and therefore don't need to compare the phone number anyway.
    173         if (! (mIsIncoming || c.isMT)) return true;
    174 
    175         // A new call appearing by SRVCC may have invalid number
    176         //  if IMS service is not tightly coupled with cellular modem stack.
    177         // Thus we prefer the preexisting handover connection instance.
    178         if (mOrigConnection != null) return true;
    179 
    180         // ... but we can compare phone numbers on MT calls, and we have
    181         // no control over when they begin, so we might as well
    182 
    183         String cAddress = PhoneNumberUtils.stringFromStringAndTOA(c.number, c.TOA);
    184         return mIsIncoming == c.isMT && equalsHandlesNulls(mAddress, cAddress);
    185     }
    186 
    187     @Override
    188     public GsmCall getCall() {
    189         return mParent;
    190     }
    191 
    192     @Override
    193     public long getDisconnectTime() {
    194         return mDisconnectTime;
    195     }
    196 
    197     @Override
    198     public long getHoldDurationMillis() {
    199         if (getState() != GsmCall.State.HOLDING) {
    200             // If not holding, return 0
    201             return 0;
    202         } else {
    203             return SystemClock.elapsedRealtime() - mHoldingStartTime;
    204         }
    205     }
    206 
    207     @Override
    208     public int getDisconnectCause() {
    209         return mCause;
    210     }
    211 
    212     @Override
    213     public GsmCall.State getState() {
    214         if (mDisconnected) {
    215             return GsmCall.State.DISCONNECTED;
    216         } else {
    217             return super.getState();
    218         }
    219     }
    220 
    221     @Override
    222     public void hangup() throws CallStateException {
    223         if (!mDisconnected) {
    224             mOwner.hangup(this);
    225         } else {
    226             throw new CallStateException ("disconnected");
    227         }
    228     }
    229 
    230     @Override
    231     public void separate() throws CallStateException {
    232         if (!mDisconnected) {
    233             mOwner.separate(this);
    234         } else {
    235             throw new CallStateException ("disconnected");
    236         }
    237     }
    238 
    239     @Override
    240     public PostDialState getPostDialState() {
    241         return mPostDialState;
    242     }
    243 
    244     @Override
    245     public void proceedAfterWaitChar() {
    246         if (mPostDialState != PostDialState.WAIT) {
    247             Rlog.w(LOG_TAG, "GsmConnection.proceedAfterWaitChar(): Expected "
    248                 + "getPostDialState() to be WAIT but was " + mPostDialState);
    249             return;
    250         }
    251 
    252         setPostDialState(PostDialState.STARTED);
    253 
    254         processNextPostDialChar();
    255     }
    256 
    257     @Override
    258     public void proceedAfterWildChar(String str) {
    259         if (mPostDialState != PostDialState.WILD) {
    260             Rlog.w(LOG_TAG, "GsmConnection.proceedAfterWaitChar(): Expected "
    261                 + "getPostDialState() to be WILD but was " + mPostDialState);
    262             return;
    263         }
    264 
    265         setPostDialState(PostDialState.STARTED);
    266 
    267         // make a new postDialString, with the wild char replacement string
    268         // at the beginning, followed by the remaining postDialString.
    269 
    270         StringBuilder buf = new StringBuilder(str);
    271         buf.append(mPostDialString.substring(mNextPostDialChar));
    272         mPostDialString = buf.toString();
    273         mNextPostDialChar = 0;
    274         if (Phone.DEBUG_PHONE) {
    275             log("proceedAfterWildChar: new postDialString is " +
    276                     mPostDialString);
    277         }
    278 
    279         processNextPostDialChar();
    280     }
    281 
    282     @Override
    283     public void cancelPostDial() {
    284         setPostDialState(PostDialState.CANCELLED);
    285     }
    286 
    287     /**
    288      * Called when this Connection is being hung up locally (eg, user pressed "end")
    289      * Note that at this point, the hangup request has been dispatched to the radio
    290      * but no response has yet been received so update() has not yet been called
    291      */
    292     void
    293     onHangupLocal() {
    294         mCause = DisconnectCause.LOCAL;
    295         mPreciseCause = 0;
    296     }
    297 
    298     /**
    299      * Maps RIL call disconnect code to {@link DisconnectCause}.
    300      * @param causeCode RIL disconnect code
    301      * @return the corresponding value from {@link DisconnectCause}
    302      */
    303     int disconnectCauseFromCode(int causeCode) {
    304         /**
    305          * See 22.001 Annex F.4 for mapping of cause codes
    306          * to local tones
    307          */
    308 
    309         switch (causeCode) {
    310             case CallFailCause.USER_BUSY:
    311                 return DisconnectCause.BUSY;
    312 
    313             case CallFailCause.NO_CIRCUIT_AVAIL:
    314             case CallFailCause.TEMPORARY_FAILURE:
    315             case CallFailCause.SWITCHING_CONGESTION:
    316             case CallFailCause.CHANNEL_NOT_AVAIL:
    317             case CallFailCause.QOS_NOT_AVAIL:
    318             case CallFailCause.BEARER_NOT_AVAIL:
    319                 return DisconnectCause.CONGESTION;
    320 
    321             case CallFailCause.ACM_LIMIT_EXCEEDED:
    322                 return DisconnectCause.LIMIT_EXCEEDED;
    323 
    324             case CallFailCause.CALL_BARRED:
    325                 return DisconnectCause.CALL_BARRED;
    326 
    327             case CallFailCause.FDN_BLOCKED:
    328                 return DisconnectCause.FDN_BLOCKED;
    329 
    330             case CallFailCause.UNOBTAINABLE_NUMBER:
    331                 return DisconnectCause.UNOBTAINABLE_NUMBER;
    332 
    333             case CallFailCause.DIAL_MODIFIED_TO_USSD:
    334                 return DisconnectCause.DIAL_MODIFIED_TO_USSD;
    335 
    336             case CallFailCause.DIAL_MODIFIED_TO_SS:
    337                 return DisconnectCause.DIAL_MODIFIED_TO_SS;
    338 
    339             case CallFailCause.DIAL_MODIFIED_TO_DIAL:
    340                 return DisconnectCause.DIAL_MODIFIED_TO_DIAL;
    341 
    342             case CallFailCause.ERROR_UNSPECIFIED:
    343             case CallFailCause.NORMAL_CLEARING:
    344             default:
    345                 GSMPhone phone = mOwner.mPhone;
    346                 int serviceState = phone.getServiceState().getState();
    347                 UiccCardApplication cardApp = phone.getUiccCardApplication();
    348                 AppState uiccAppState = (cardApp != null) ? cardApp.getState() :
    349                                                             AppState.APPSTATE_UNKNOWN;
    350                 if (serviceState == ServiceState.STATE_POWER_OFF) {
    351                     return DisconnectCause.POWER_OFF;
    352                 } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE
    353                         || serviceState == ServiceState.STATE_EMERGENCY_ONLY ) {
    354                     return DisconnectCause.OUT_OF_SERVICE;
    355                 } else if (uiccAppState != AppState.APPSTATE_READY) {
    356                     return DisconnectCause.ICC_ERROR;
    357                 } else if (causeCode == CallFailCause.ERROR_UNSPECIFIED) {
    358                     if (phone.mSST.mRestrictedState.isCsRestricted()) {
    359                         return DisconnectCause.CS_RESTRICTED;
    360                     } else if (phone.mSST.mRestrictedState.isCsEmergencyRestricted()) {
    361                         return DisconnectCause.CS_RESTRICTED_EMERGENCY;
    362                     } else if (phone.mSST.mRestrictedState.isCsNormalRestricted()) {
    363                         return DisconnectCause.CS_RESTRICTED_NORMAL;
    364                     } else {
    365                         return DisconnectCause.ERROR_UNSPECIFIED;
    366                     }
    367                 } else if (causeCode == CallFailCause.NORMAL_CLEARING) {
    368                     return DisconnectCause.NORMAL;
    369                 } else {
    370                     // If nothing else matches, report unknown call drop reason
    371                     // to app, not NORMAL call end.
    372                     return DisconnectCause.ERROR_UNSPECIFIED;
    373                 }
    374         }
    375     }
    376 
    377     /*package*/ void
    378     onRemoteDisconnect(int causeCode) {
    379         this.mPreciseCause = causeCode;
    380         onDisconnect(disconnectCauseFromCode(causeCode));
    381     }
    382 
    383     /**
    384      * Called when the radio indicates the connection has been disconnected.
    385      * @param cause call disconnect cause; values are defined in {@link DisconnectCause}
    386      */
    387     /*package*/ boolean onDisconnect(int cause) {
    388         boolean changed = false;
    389 
    390         mCause = cause;
    391 
    392         if (!mDisconnected) {
    393             mIndex = -1;
    394 
    395             mDisconnectTime = System.currentTimeMillis();
    396             mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal;
    397             mDisconnected = true;
    398 
    399             if (DBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
    400 
    401             mOwner.mPhone.notifyDisconnect(this);
    402 
    403             if (mParent != null) {
    404                 changed = mParent.connectionDisconnected(this);
    405             }
    406 
    407             mOrigConnection = null;
    408         }
    409         clearPostDialListeners();
    410         releaseWakeLock();
    411         return changed;
    412     }
    413 
    414     // Returns true if state has changed, false if nothing changed
    415     /*package*/ boolean
    416     update (DriverCall dc) {
    417         GsmCall newParent;
    418         boolean changed = false;
    419         boolean wasConnectingInOrOut = isConnectingInOrOut();
    420         boolean wasHolding = (getState() == GsmCall.State.HOLDING);
    421 
    422         newParent = parentFromDCState(dc.state);
    423 
    424         //Ignore dc.number and dc.name in case of a handover connection
    425         if (mOrigConnection != null) {
    426             if (Phone.DEBUG_PHONE) log("update: mOrigConnection is not null");
    427         } else {
    428             log(" mNumberConverted " + mNumberConverted);
    429             if (!equalsHandlesNulls(mAddress, dc.number) && (!mNumberConverted
    430                     || !equalsHandlesNulls(mConvertedNumber, dc.number))) {
    431                 if (Phone.DEBUG_PHONE) log("update: phone # changed!");
    432                 mAddress = dc.number;
    433                 changed = true;
    434             }
    435         }
    436 
    437         // A null cnapName should be the same as ""
    438         if (TextUtils.isEmpty(dc.name)) {
    439             if (!TextUtils.isEmpty(mCnapName)) {
    440                 changed = true;
    441                 mCnapName = "";
    442             }
    443         } else if (!dc.name.equals(mCnapName)) {
    444             changed = true;
    445             mCnapName = dc.name;
    446         }
    447 
    448         if (Phone.DEBUG_PHONE) log("--dssds----"+mCnapName);
    449         mCnapNamePresentation = dc.namePresentation;
    450         mNumberPresentation = dc.numberPresentation;
    451 
    452         if (newParent != mParent) {
    453             if (mParent != null) {
    454                 mParent.detach(this);
    455             }
    456             newParent.attach(this, dc);
    457             mParent = newParent;
    458             changed = true;
    459         } else {
    460             boolean parentStateChange;
    461             parentStateChange = mParent.update (this, dc);
    462             changed = changed || parentStateChange;
    463         }
    464 
    465         /** Some state-transition events */
    466 
    467         if (Phone.DEBUG_PHONE) log(
    468                 "update: parent=" + mParent +
    469                 ", hasNewParent=" + (newParent != mParent) +
    470                 ", wasConnectingInOrOut=" + wasConnectingInOrOut +
    471                 ", wasHolding=" + wasHolding +
    472                 ", isConnectingInOrOut=" + isConnectingInOrOut() +
    473                 ", changed=" + changed);
    474 
    475 
    476         if (wasConnectingInOrOut && !isConnectingInOrOut()) {
    477             onConnectedInOrOut();
    478         }
    479 
    480         if (changed && !wasHolding && (getState() == GsmCall.State.HOLDING)) {
    481             // We've transitioned into HOLDING
    482             onStartedHolding();
    483         }
    484 
    485         return changed;
    486     }
    487 
    488     /**
    489      * Called when this Connection is in the foregroundCall
    490      * when a dial is initiated.
    491      * We know we're ACTIVE, and we know we're going to end up
    492      * HOLDING in the backgroundCall
    493      */
    494     void
    495     fakeHoldBeforeDial() {
    496         if (mParent != null) {
    497             mParent.detach(this);
    498         }
    499 
    500         mParent = mOwner.mBackgroundCall;
    501         mParent.attachFake(this, GsmCall.State.HOLDING);
    502 
    503         onStartedHolding();
    504     }
    505 
    506     /*package*/ int
    507     getGSMIndex() throws CallStateException {
    508         if (mIndex >= 0) {
    509             return mIndex + 1;
    510         } else {
    511             throw new CallStateException ("GSM index not yet assigned");
    512         }
    513     }
    514 
    515     /**
    516      * An incoming or outgoing call has connected
    517      */
    518     void
    519     onConnectedInOrOut() {
    520         mConnectTime = System.currentTimeMillis();
    521         mConnectTimeReal = SystemClock.elapsedRealtime();
    522         mDuration = 0;
    523 
    524         // bug #678474: incoming call interpreted as missed call, even though
    525         // it sounds like the user has picked up the call.
    526         if (Phone.DEBUG_PHONE) {
    527             log("onConnectedInOrOut: connectTime=" + mConnectTime);
    528         }
    529 
    530         if (!mIsIncoming) {
    531             // outgoing calls only
    532             processNextPostDialChar();
    533         }
    534         releaseWakeLock();
    535     }
    536 
    537     /*package*/ void
    538     onStartedHolding() {
    539         mHoldingStartTime = SystemClock.elapsedRealtime();
    540     }
    541     /**
    542      * Performs the appropriate action for a post-dial char, but does not
    543      * notify application. returns false if the character is invalid and
    544      * should be ignored
    545      */
    546     private boolean
    547     processPostDialChar(char c) {
    548         if (PhoneNumberUtils.is12Key(c)) {
    549             mOwner.mCi.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
    550         } else if (c == PhoneNumberUtils.PAUSE) {
    551             // From TS 22.101:
    552             // It continues...
    553             // Upon the called party answering the UE shall send the DTMF digits
    554             // automatically to the network after a delay of 3 seconds( 20 ).
    555             // The digits shall be sent according to the procedures and timing
    556             // specified in 3GPP TS 24.008 [13]. The first occurrence of the
    557             // "DTMF Control Digits Separator" shall be used by the ME to
    558             // distinguish between the addressing digits (i.e. the phone number)
    559             // and the DTMF digits. Upon subsequent occurrences of the
    560             // separator,
    561             // the UE shall pause again for 3 seconds ( 20 ) before sending
    562             // any further DTMF digits.
    563             mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_PAUSE_DONE),
    564                     PAUSE_DELAY_MILLIS);
    565         } else if (c == PhoneNumberUtils.WAIT) {
    566             setPostDialState(PostDialState.WAIT);
    567         } else if (c == PhoneNumberUtils.WILD) {
    568             setPostDialState(PostDialState.WILD);
    569         } else {
    570             return false;
    571         }
    572 
    573         return true;
    574     }
    575 
    576     @Override
    577     public String
    578     getRemainingPostDialString() {
    579         if (mPostDialState == PostDialState.CANCELLED
    580             || mPostDialState == PostDialState.COMPLETE
    581             || mPostDialString == null
    582             || mPostDialString.length() <= mNextPostDialChar
    583         ) {
    584             return "";
    585         }
    586 
    587         return mPostDialString.substring(mNextPostDialChar);
    588     }
    589 
    590     @Override
    591     protected void finalize()
    592     {
    593         /**
    594          * It is understood that This finializer is not guaranteed
    595          * to be called and the release lock call is here just in
    596          * case there is some path that doesn't call onDisconnect
    597          * and or onConnectedInOrOut.
    598          */
    599         if (mPartialWakeLock.isHeld()) {
    600             Rlog.e(LOG_TAG, "[GSMConn] UNEXPECTED; mPartialWakeLock is held when finalizing.");
    601         }
    602         clearPostDialListeners();
    603         releaseWakeLock();
    604     }
    605 
    606     private void
    607     processNextPostDialChar() {
    608         char c = 0;
    609         Registrant postDialHandler;
    610 
    611         if (mPostDialState == PostDialState.CANCELLED) {
    612             //Rlog.v("GSM", "##### processNextPostDialChar: postDialState == CANCELLED, bail");
    613             return;
    614         }
    615 
    616         if (mPostDialString == null ||
    617                 mPostDialString.length() <= mNextPostDialChar) {
    618             setPostDialState(PostDialState.COMPLETE);
    619 
    620             // notifyMessage.arg1 is 0 on complete
    621             c = 0;
    622         } else {
    623             boolean isValid;
    624 
    625             setPostDialState(PostDialState.STARTED);
    626 
    627             c = mPostDialString.charAt(mNextPostDialChar++);
    628 
    629             isValid = processPostDialChar(c);
    630 
    631             if (!isValid) {
    632                 // Will call processNextPostDialChar
    633                 mHandler.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget();
    634                 // Don't notify application
    635                 Rlog.e("GSM", "processNextPostDialChar: c=" + c + " isn't valid!");
    636                 return;
    637             }
    638         }
    639 
    640         notifyPostDialListenersNextChar(c);
    641 
    642         // TODO: remove the following code since the handler no longer executes anything.
    643         postDialHandler = mOwner.mPhone.mPostDialHandler;
    644 
    645         Message notifyMessage;
    646 
    647         if (postDialHandler != null
    648                 && (notifyMessage = postDialHandler.messageForRegistrant()) != null) {
    649             // The AsyncResult.result is the Connection object
    650             PostDialState state = mPostDialState;
    651             AsyncResult ar = AsyncResult.forMessage(notifyMessage);
    652             ar.result = this;
    653             ar.userObj = state;
    654 
    655             // arg1 is the character that was/is being processed
    656             notifyMessage.arg1 = c;
    657 
    658             //Rlog.v("GSM", "##### processNextPostDialChar: send msg to postDialHandler, arg1=" + c);
    659             notifyMessage.sendToTarget();
    660         }
    661     }
    662 
    663 
    664     /** "connecting" means "has never been ACTIVE" for both incoming
    665      *  and outgoing calls
    666      */
    667     private boolean
    668     isConnectingInOrOut() {
    669         return mParent == null || mParent == mOwner.mRingingCall
    670             || mParent.mState == GsmCall.State.DIALING
    671             || mParent.mState == GsmCall.State.ALERTING;
    672     }
    673 
    674     private GsmCall
    675     parentFromDCState (DriverCall.State state) {
    676         switch (state) {
    677             case ACTIVE:
    678             case DIALING:
    679             case ALERTING:
    680                 return mOwner.mForegroundCall;
    681             //break;
    682 
    683             case HOLDING:
    684                 return mOwner.mBackgroundCall;
    685             //break;
    686 
    687             case INCOMING:
    688             case WAITING:
    689                 return mOwner.mRingingCall;
    690             //break;
    691 
    692             default:
    693                 throw new RuntimeException("illegal call state: " + state);
    694         }
    695     }
    696 
    697     /**
    698      * Set post dial state and acquire wake lock while switching to "started"
    699      * state, the wake lock will be released if state switches out of "started"
    700      * state or after WAKE_LOCK_TIMEOUT_MILLIS.
    701      * @param s new PostDialState
    702      */
    703     private void setPostDialState(PostDialState s) {
    704         if (mPostDialState != PostDialState.STARTED
    705                 && s == PostDialState.STARTED) {
    706             acquireWakeLock();
    707             Message msg = mHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
    708             mHandler.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS);
    709         } else if (mPostDialState == PostDialState.STARTED
    710                 && s != PostDialState.STARTED) {
    711             mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
    712             releaseWakeLock();
    713         }
    714         mPostDialState = s;
    715         notifyPostDialListeners();
    716     }
    717 
    718     private void
    719     createWakeLock(Context context) {
    720         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    721         mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
    722     }
    723 
    724     private void
    725     acquireWakeLock() {
    726         log("acquireWakeLock");
    727         mPartialWakeLock.acquire();
    728     }
    729 
    730     private void
    731     releaseWakeLock() {
    732         synchronized(mPartialWakeLock) {
    733             if (mPartialWakeLock.isHeld()) {
    734                 log("releaseWakeLock");
    735                 mPartialWakeLock.release();
    736             }
    737         }
    738     }
    739 
    740     private void log(String msg) {
    741         Rlog.d(LOG_TAG, "[GSMConn] " + msg);
    742     }
    743 
    744     @Override
    745     public int getNumberPresentation() {
    746         return mNumberPresentation;
    747     }
    748 
    749     @Override
    750     public UUSInfo getUUSInfo() {
    751         return mUusInfo;
    752     }
    753 
    754     public int getPreciseDisconnectCause() {
    755         return mPreciseCause;
    756     }
    757 
    758     @Override
    759     public void migrateFrom(Connection c) {
    760         if (c == null) return;
    761 
    762         super.migrateFrom(c);
    763 
    764         this.mUusInfo = c.getUUSInfo();
    765 
    766         this.setUserData(c.getUserData());
    767     }
    768 
    769     @Override
    770     public Connection getOrigConnection() {
    771         return mOrigConnection;
    772     }
    773 
    774     @Override
    775     public boolean isMultiparty() {
    776         if (mOrigConnection != null) {
    777             return mOrigConnection.isMultiparty();
    778         }
    779 
    780         return false;
    781     }
    782 }
    783