FileDocCategorySizeDatePackage
CdmaConnection.javaAPI DocAndroid 5.1 API31101Thu Mar 12 22:22:54 GMT 2015com.android.internal.telephony.cdma

CdmaConnection

public class CdmaConnection extends Connection
{@hide}

Fields Summary
static final String
LOG_TAG
private static final boolean
VDBG
CdmaCallTracker
mOwner
CdmaCall
mParent
String
mPostDialString
boolean
mDisconnected
int
mIndex
long
mDisconnectTime
int
mNextPostDialChar
int
mCause
PostDialState
mPostDialState
int
mPreciseCause
android.os.Handler
mHandler
private PowerManager.WakeLock
mPartialWakeLock
static final int
EVENT_DTMF_DONE
static final int
EVENT_PAUSE_DONE
static final int
EVENT_NEXT_POST_DIAL
static final int
EVENT_WAKE_LOCK_TIMEOUT
static final int
WAKE_LOCK_TIMEOUT_MILLIS
static final int
PAUSE_DELAY_MILLIS
Constructors Summary
CdmaConnection(android.content.Context context, DriverCall dc, CdmaCallTracker ct, int index)
This is probably an MT call that we first saw in a CLCC response

        createWakeLock(context);
        acquireWakeLock();

        mOwner = ct;
        mHandler = new MyHandler(mOwner.getLooper());

        mAddress = dc.number;

        mIsIncoming = dc.isMT;
        mCreateTime = System.currentTimeMillis();
        mCnapName = dc.name;
        mCnapNamePresentation = dc.namePresentation;
        mNumberPresentation = dc.numberPresentation;

        mIndex = index;

        mParent = parentFromDCState (dc.state);
        mParent.attach(this, dc);
    
CdmaConnection(android.content.Context context, String dialString, CdmaCallTracker ct, CdmaCall parent)
This is an MO call/three way call, created when dialing

        createWakeLock(context);
        acquireWakeLock();

        mOwner = ct;
        mHandler = new MyHandler(mOwner.getLooper());

        mDialString = dialString;
        Rlog.d(LOG_TAG, "[CDMAConn] CdmaConnection: dialString=" + dialString);
        dialString = formatDialString(dialString);
        Rlog.d(LOG_TAG, "[CDMAConn] CdmaConnection:formated dialString=" + dialString);

        mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
        mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString);

        mIndex = -1;

        mIsIncoming = false;
        mCnapName = null;
        mCnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
        mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
        mCreateTime = System.currentTimeMillis();

        if (parent != null) {
            mParent = parent;

            //for the three way call case, not change parent state
            if (parent.mState == CdmaCall.State.ACTIVE) {
                parent.attachFake(this, CdmaCall.State.ACTIVE);
            } else {
                parent.attachFake(this, CdmaCall.State.DIALING);
            }
        }
    
CdmaConnection(android.content.Context context, CdmaCallWaitingNotification cw, CdmaCallTracker ct, CdmaCall parent)
This is a Call waiting call

        createWakeLock(context);
        acquireWakeLock();

        mOwner = ct;
        mHandler = new MyHandler(mOwner.getLooper());
        mAddress = cw.number;
        mNumberPresentation = cw.numberPresentation;
        mCnapName = cw.name;
        mCnapNamePresentation = cw.namePresentation;
        mIndex = -1;
        mIsIncoming = true;
        mCreateTime = System.currentTimeMillis();
        mConnectTime = 0;
        mParent = parent;
        parent.attachFake(this, CdmaCall.State.WAITING);
    
Methods Summary
private voidacquireWakeLock()

        log("acquireWakeLock");
        mPartialWakeLock.acquire();
    
public voidcancelPostDial()

        setPostDialState(PostDialState.CANCELLED);
    
booleancompareTo(DriverCall c)

        // On mobile originated (MO) calls, the phone number may have changed
        // due to a SIM Toolkit call control modification.
        //
        // We assume we know when MO calls are created (since we created them)
        // and therefore don't need to compare the phone number anyway.
        if (! (mIsIncoming || c.isMT)) return true;

        // ... but we can compare phone numbers on MT calls, and we have
        // no control over when they begin, so we might as well

        String cAddress = PhoneNumberUtils.stringFromStringAndTOA(c.number, c.TOA);
        return mIsIncoming == c.isMT && equalsHandlesNulls(mAddress, cAddress);
    
private voidcreateWakeLock(android.content.Context context)

        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
    
intdisconnectCauseFromCode(int causeCode)
Maps RIL call disconnect code to {@link DisconnectCause}.

param
causeCode RIL disconnect code
return
the corresponding value from {@link DisconnectCause}

        /**
         * See 22.001 Annex F.4 for mapping of cause codes
         * to local tones
         */

        switch (causeCode) {
            case CallFailCause.USER_BUSY:
                return DisconnectCause.BUSY;
            case CallFailCause.NO_CIRCUIT_AVAIL:
                return DisconnectCause.CONGESTION;
            case CallFailCause.ACM_LIMIT_EXCEEDED:
                return DisconnectCause.LIMIT_EXCEEDED;
            case CallFailCause.CALL_BARRED:
                return DisconnectCause.CALL_BARRED;
            case CallFailCause.FDN_BLOCKED:
                return DisconnectCause.FDN_BLOCKED;
            case CallFailCause.DIAL_MODIFIED_TO_USSD:
                return DisconnectCause.DIAL_MODIFIED_TO_USSD;
            case CallFailCause.DIAL_MODIFIED_TO_SS:
                return DisconnectCause.DIAL_MODIFIED_TO_SS;
            case CallFailCause.DIAL_MODIFIED_TO_DIAL:
                return DisconnectCause.DIAL_MODIFIED_TO_DIAL;
            case CallFailCause.CDMA_LOCKED_UNTIL_POWER_CYCLE:
                return DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE;
            case CallFailCause.CDMA_DROP:
                return DisconnectCause.CDMA_DROP;
            case CallFailCause.CDMA_INTERCEPT:
                return DisconnectCause.CDMA_INTERCEPT;
            case CallFailCause.CDMA_REORDER:
                return DisconnectCause.CDMA_REORDER;
            case CallFailCause.CDMA_SO_REJECT:
                return DisconnectCause.CDMA_SO_REJECT;
            case CallFailCause.CDMA_RETRY_ORDER:
                return DisconnectCause.CDMA_RETRY_ORDER;
            case CallFailCause.CDMA_ACCESS_FAILURE:
                return DisconnectCause.CDMA_ACCESS_FAILURE;
            case CallFailCause.CDMA_PREEMPTED:
                return DisconnectCause.CDMA_PREEMPTED;
            case CallFailCause.CDMA_NOT_EMERGENCY:
                return DisconnectCause.CDMA_NOT_EMERGENCY;
            case CallFailCause.CDMA_ACCESS_BLOCKED:
                return DisconnectCause.CDMA_ACCESS_BLOCKED;
            case CallFailCause.ERROR_UNSPECIFIED:
            case CallFailCause.NORMAL_CLEARING:
            default:
                CDMAPhone phone = mOwner.mPhone;
                int serviceState = phone.getServiceState().getState();
                UiccCardApplication app = UiccController
                        .getInstance()
                        .getUiccCardApplication(phone.getPhoneId(), UiccController.APP_FAM_3GPP2);
                AppState uiccAppState = (app != null) ? app.getState() : AppState.APPSTATE_UNKNOWN;
                if (serviceState == ServiceState.STATE_POWER_OFF) {
                    return DisconnectCause.POWER_OFF;
                } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE
                        || serviceState == ServiceState.STATE_EMERGENCY_ONLY) {
                    return DisconnectCause.OUT_OF_SERVICE;
                } else if (phone.mCdmaSubscriptionSource ==
                        CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM
                        && uiccAppState != AppState.APPSTATE_READY) {
                    return DisconnectCause.ICC_ERROR;
                } else if (causeCode==CallFailCause.NORMAL_CLEARING) {
                    return DisconnectCause.NORMAL;
                } else {
                    return DisconnectCause.ERROR_UNSPECIFIED;
                }
        }
    
public voiddispose()

    
private voiddoDisconnect()

        mIndex = -1;
        mDisconnectTime = System.currentTimeMillis();
        mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal;
        mDisconnected = true;
        clearPostDialListeners();
    
static booleanequalsHandlesNulls(java.lang.Object a, java.lang.Object b)

        return (a == null) ? (b == null) : a.equals (b);
    
voidfakeHoldBeforeDial()
Called when this Connection is in the foregroundCall when a dial is initiated. We know we're ACTIVE, and we know we're going to end up HOLDING in the backgroundCall

        if (mParent != null) {
            mParent.detach(this);
        }

        mParent = mOwner.mBackgroundCall;
        mParent.attachFake(this, CdmaCall.State.HOLDING);

        onStartedHolding();
    
protected voidfinalize()

        /**
         * It is understood that This finializer is not guaranteed
         * to be called and the release lock call is here just in
         * case there is some path that doesn't call onDisconnect
         * and or onConnectedInOrOut.
         */
        if (mPartialWakeLock.isHeld()) {
            Rlog.e(LOG_TAG, "[CdmaConn] UNEXPECTED; mPartialWakeLock is held when finalizing.");
        }
        releaseWakeLock();
    
private static intfindNextPCharOrNonPOrNonWCharIndex(java.lang.String phoneNumber, int currIndex)

        boolean wMatched = isWait(phoneNumber.charAt(currIndex));
        int index = currIndex + 1;
        int length = phoneNumber.length();
        while (index < length) {
            char cNext = phoneNumber.charAt(index);
            // if there is any W inside P/W sequence,mark it
            if (isWait(cNext)) {
                wMatched = true;
            }
            // if any characters other than P/W chars after P/W sequence
            // we break out the loop and append the correct
            if (!isWait(cNext) && !isPause(cNext)) {
                break;
            }
            index++;
        }

        // It means the PAUSE character(s) is in the middle of dial string
        // and it needs to be handled one by one.
        if ((index < length) && (index > (currIndex + 1))  &&
            ((wMatched == false) && isPause(phoneNumber.charAt(currIndex)))) {
            return (currIndex + 1);
        }
        return index;
    
private static charfindPOrWCharToAppend(java.lang.String phoneNumber, int currPwIndex, int nextNonPwCharIndex)

        char c = phoneNumber.charAt(currPwIndex);
        char ret;

        // Append the PW char
        ret = (isPause(c)) ? PhoneNumberUtils.PAUSE : PhoneNumberUtils.WAIT;

        // If the nextNonPwCharIndex is greater than currPwIndex + 1,
        // it means the PW sequence contains not only P characters.
        // Since for the sequence that only contains P character,
        // the P character is handled one by one, the nextNonPwCharIndex
        // equals to currPwIndex + 1.
        // In this case, skip P, append W.
        if (nextNonPwCharIndex > (currPwIndex + 1)) {
            ret = PhoneNumberUtils.WAIT;
        }
        return ret;
    
public static java.lang.StringformatDialString(java.lang.String phoneNumber)
format original dial string 1) convert international dialing prefix "+" to string specified per region 2) handle corner cases for PAUSE/WAIT dialing: If PAUSE/WAIT sequence at the end, ignore them. If consecutive PAUSE/WAIT sequence in the middle of the string, and if there is any WAIT in PAUSE/WAIT sequence, treat them like WAIT.

        /**
         * TODO(cleanup): This function should move to PhoneNumberUtils, and
         * tests should be added.
         */

        if (phoneNumber == null) {
            return null;
        }
        int length = phoneNumber.length();
        StringBuilder ret = new StringBuilder();
        char c;
        int currIndex = 0;

        while (currIndex < length) {
            c = phoneNumber.charAt(currIndex);
            if (isPause(c) || isWait(c)) {
                if (currIndex < length - 1) {
                    // if PW not at the end
                    int nextIndex = findNextPCharOrNonPOrNonWCharIndex(phoneNumber, currIndex);
                    // If there is non PW char following PW sequence
                    if (nextIndex < length) {
                        char pC = findPOrWCharToAppend(phoneNumber, currIndex, nextIndex);
                        ret.append(pC);
                        // If PW char sequence has more than 2 PW characters,
                        // skip to the last PW character since the sequence already be
                        // converted to WAIT character
                        if (nextIndex > (currIndex + 1)) {
                            currIndex = nextIndex - 1;
                        }
                    } else if (nextIndex == length) {
                        // It means PW characters at the end, ignore
                        currIndex = length - 1;
                    }
                }
            } else {
                ret.append(c);
            }
            currIndex++;
        }
        return PhoneNumberUtils.cdmaCheckAndProcessPlusCode(ret.toString());
    
intgetCDMAIndex()

        if (mIndex >= 0) {
            return mIndex + 1;
        } else {
            throw new CallStateException ("CDMA connection index not assigned");
        }
    
public CdmaCallgetCall()

        return mParent;
    
public intgetDisconnectCause()

        return mCause;
    
public longgetDisconnectTime()

        return mDisconnectTime;
    
public longgetHoldDurationMillis()

        if (getState() != CdmaCall.State.HOLDING) {
            // If not holding, return 0
            return 0;
        } else {
            return SystemClock.elapsedRealtime() - mHoldingStartTime;
        }
    
public intgetNumberPresentation()

        return mNumberPresentation;
    
public ConnectiongetOrigConnection()

        return null;
    
public java.lang.StringgetOrigDialString()

        return mDialString;
    
public PostDialStategetPostDialState()

        return mPostDialState;
    
public intgetPreciseDisconnectCause()

        return mPreciseCause;
    
public java.lang.StringgetRemainingPostDialString()

        if (mPostDialState == PostDialState.CANCELLED
                || mPostDialState == PostDialState.COMPLETE
                || mPostDialString == null
                || mPostDialString.length() <= mNextPostDialChar) {
            return "";
        }

        String subStr = mPostDialString.substring(mNextPostDialChar);
        if (subStr != null) {
            int wIndex = subStr.indexOf(PhoneNumberUtils.WAIT);
            int pIndex = subStr.indexOf(PhoneNumberUtils.PAUSE);

            if (wIndex > 0 && (wIndex < pIndex || pIndex <= 0)) {
                subStr = subStr.substring(0, wIndex);
            } else if (pIndex > 0) {
                subStr = subStr.substring(0, pIndex);
            }
        }
        return subStr;
    
public CdmaCall.StategetState()

        if (mDisconnected) {
            return CdmaCall.State.DISCONNECTED;
        } else {
            return super.getState();
        }
    
public UUSInfogetUUSInfo()

        // UUS information not supported in CDMA
        return null;
    
public voidhangup()

        if (!mDisconnected) {
            mOwner.hangup(this);
        } else {
            throw new CallStateException ("disconnected");
        }
    
private booleanisConnectingInOrOut()
"connecting" means "has never been ACTIVE" for both incoming and outgoing calls

        return mParent == null || mParent == mOwner.mRingingCall
            || mParent.mState == CdmaCall.State.DIALING
            || mParent.mState == CdmaCall.State.ALERTING;
    
public booleanisMultiparty()

        return false;
    
private static booleanisPause(char c)

        return c == PhoneNumberUtils.PAUSE;
    
private static booleanisWait(char c)

        return c == PhoneNumberUtils.WAIT;
    
private voidlog(java.lang.String msg)

        Rlog.d(LOG_TAG, "[CDMAConn] " + msg);
    
voidonConnectedInOrOut()
An incoming or outgoing call has connected

        mConnectTime = System.currentTimeMillis();
        mConnectTimeReal = SystemClock.elapsedRealtime();
        mDuration = 0;

        // bug #678474: incoming call interpreted as missed call, even though
        // it sounds like the user has picked up the call.
        if (Phone.DEBUG_PHONE) {
            log("onConnectedInOrOut: connectTime=" + mConnectTime);
        }

        if (!mIsIncoming) {
            // outgoing calls only
            processNextPostDialChar();
        } else {
            // Only release wake lock for incoming calls, for outgoing calls the wake lock
            // will be released after any pause-dial is completed
            releaseWakeLock();
        }
    
booleanonDisconnect(int cause)
Called when the radio indicates the connection has been disconnected.

param
cause call disconnect cause; values are defined in {@link DisconnectCause}

        boolean changed = false;

        mCause = cause;

        if (!mDisconnected) {
            doDisconnect();
            if (VDBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);

            mOwner.mPhone.notifyDisconnect(this);

            if (mParent != null) {
                changed = mParent.connectionDisconnected(this);
            }
        }
        releaseWakeLock();
        return changed;
    
voidonHangupLocal()
Called when this Connection is being hung up locally (eg, user pressed "end") Note that at this point, the hangup request has been dispatched to the radio but no response has yet been received so update() has not yet been called

        mCause = DisconnectCause.LOCAL;
        mPreciseCause = 0;
    
voidonLocalDisconnect()
Called when the call waiting connection has been hung up

        if (!mDisconnected) {
            doDisconnect();
            if (VDBG) Rlog.d(LOG_TAG, "onLoalDisconnect" );

            if (mParent != null) {
                mParent.detach(this);
            }
        }
        releaseWakeLock();
    
voidonRemoteDisconnect(int causeCode)

        this.mPreciseCause = causeCode;
        onDisconnect(disconnectCauseFromCode(causeCode));
    
voidonStartedHolding()

        mHoldingStartTime = SystemClock.elapsedRealtime();
    
private CdmaCallparentFromDCState(DriverCall.State state)

        switch (state) {
            case ACTIVE:
            case DIALING:
            case ALERTING:
                return mOwner.mForegroundCall;
            //break;

            case HOLDING:
                return mOwner.mBackgroundCall;
            //break;

            case INCOMING:
            case WAITING:
                return mOwner.mRingingCall;
            //break;

            default:
                throw new RuntimeException("illegal call state: " + state);
        }
    
public voidproceedAfterWaitChar()

        if (mPostDialState != PostDialState.WAIT) {
            Rlog.w(LOG_TAG, "CdmaConnection.proceedAfterWaitChar(): Expected "
                + "getPostDialState() to be WAIT but was " + mPostDialState);
            return;
        }

        setPostDialState(PostDialState.STARTED);

        processNextPostDialChar();
    
public voidproceedAfterWildChar(java.lang.String str)

        if (mPostDialState != PostDialState.WILD) {
            Rlog.w(LOG_TAG, "CdmaConnection.proceedAfterWaitChar(): Expected "
                + "getPostDialState() to be WILD but was " + mPostDialState);
            return;
        }

        setPostDialState(PostDialState.STARTED);

        // make a new postDialString, with the wild char replacement string
        // at the beginning, followed by the remaining postDialString.

        StringBuilder buf = new StringBuilder(str);
        buf.append(mPostDialString.substring(mNextPostDialChar));
        mPostDialString = buf.toString();
        mNextPostDialChar = 0;
        if (Phone.DEBUG_PHONE) {
            log("proceedAfterWildChar: new postDialString is " +
                    mPostDialString);
        }

        processNextPostDialChar();
    
voidprocessNextPostDialChar()

        char c = 0;
        Registrant postDialHandler;

        if (mPostDialState == PostDialState.CANCELLED) {
            releaseWakeLock();
            //Rlog.v("CDMA", "##### processNextPostDialChar: postDialState == CANCELLED, bail");
            return;
        }

        if (mPostDialString == null ||
                mPostDialString.length() <= mNextPostDialChar) {
            setPostDialState(PostDialState.COMPLETE);

            // We were holding a wake lock until pause-dial was complete, so give it up now
            releaseWakeLock();

            // notifyMessage.arg1 is 0 on complete
            c = 0;
        } else {
            boolean isValid;

            setPostDialState(PostDialState.STARTED);

            c = mPostDialString.charAt(mNextPostDialChar++);

            isValid = processPostDialChar(c);

            if (!isValid) {
                // Will call processNextPostDialChar
                mHandler.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget();
                // Don't notify application
                Rlog.e("CDMA", "processNextPostDialChar: c=" + c + " isn't valid!");
                return;
            }
        }

        notifyPostDialListenersNextChar(c);

        // TODO: remove the following code since the handler no longer executes anything.
        postDialHandler = mOwner.mPhone.mPostDialHandler;

        Message notifyMessage;

        if (postDialHandler != null &&
                (notifyMessage = postDialHandler.messageForRegistrant()) != null) {
            // The AsyncResult.result is the Connection object
            PostDialState state = mPostDialState;
            AsyncResult ar = AsyncResult.forMessage(notifyMessage);
            ar.result = this;
            ar.userObj = state;

            // arg1 is the character that was/is being processed
            notifyMessage.arg1 = c;

            notifyMessage.sendToTarget();
        }
    
private booleanprocessPostDialChar(char c)
Performs the appropriate action for a post-dial char, but does not notify application. returns false if the character is invalid and should be ignored

        if (PhoneNumberUtils.is12Key(c)) {
            mOwner.mCi.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
        } else if (c == PhoneNumberUtils.PAUSE) {
            setPostDialState(PostDialState.PAUSE);

            // Upon occurrences of the separator, the UE shall
            // pause again for 2 seconds before sending any
            // further DTMF digits.
            mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_PAUSE_DONE),
                                            PAUSE_DELAY_MILLIS);
        } else if (c == PhoneNumberUtils.WAIT) {
            setPostDialState(PostDialState.WAIT);
        } else if (c == PhoneNumberUtils.WILD) {
            setPostDialState(PostDialState.WILD);
        } else {
            return false;
        }

        return true;
    
private voidreleaseWakeLock()

        synchronized (mPartialWakeLock) {
            if (mPartialWakeLock.isHeld()) {
                log("releaseWakeLock");
                mPartialWakeLock.release();
            }
        }
    
public voidseparate()

        if (!mDisconnected) {
            mOwner.separate(this);
        } else {
            throw new CallStateException ("disconnected");
        }
    
private voidsetPostDialState(PostDialState s)
Set post dial state and acquire wake lock while switching to "started" or "wait" state, the wake lock will be released if state switches out of "started" or "wait" state or after WAKE_LOCK_TIMEOUT_MILLIS.

param
s new PostDialState

        if (s == PostDialState.STARTED ||
                s == PostDialState.PAUSE) {
            synchronized (mPartialWakeLock) {
                if (mPartialWakeLock.isHeld()) {
                    mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
                } else {
                    acquireWakeLock();
                }
                Message msg = mHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
                mHandler.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS);
            }
        } else {
            mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
            releaseWakeLock();
        }
        mPostDialState = s;
        notifyPostDialListeners();
    
booleanupdate(DriverCall dc)

        CdmaCall newParent;
        boolean changed = false;
        boolean wasConnectingInOrOut = isConnectingInOrOut();
        boolean wasHolding = (getState() == CdmaCall.State.HOLDING);

        newParent = parentFromDCState(dc.state);

        if (Phone.DEBUG_PHONE) log("parent= " +mParent +", newParent= " + newParent);

        log(" mNumberConverted " + mNumberConverted);
        if (!equalsHandlesNulls(mAddress, dc.number) && (!mNumberConverted
                || !equalsHandlesNulls(mConvertedNumber, dc.number)))  {
            if (Phone.DEBUG_PHONE) log("update: phone # changed!");
            mAddress = dc.number;
            changed = true;
        }

        // A null cnapName should be the same as ""
        if (TextUtils.isEmpty(dc.name)) {
            if (!TextUtils.isEmpty(mCnapName)) {
                changed = true;
                mCnapName = "";
            }
        } else if (!dc.name.equals(mCnapName)) {
            changed = true;
            mCnapName = dc.name;
        }

        if (Phone.DEBUG_PHONE) log("--dssds----"+mCnapName);
        mCnapNamePresentation = dc.namePresentation;
        mNumberPresentation = dc.numberPresentation;

        if (newParent != mParent) {
            if (mParent != null) {
                mParent.detach(this);
            }
            newParent.attach(this, dc);
            mParent = newParent;
            changed = true;
        } else {
            boolean parentStateChange;
            parentStateChange = mParent.update (this, dc);
            changed = changed || parentStateChange;
        }

        /** Some state-transition events */

        if (Phone.DEBUG_PHONE) log(
                "Update, wasConnectingInOrOut=" + wasConnectingInOrOut +
                ", wasHolding=" + wasHolding +
                ", isConnectingInOrOut=" + isConnectingInOrOut() +
                ", changed=" + changed);


        if (wasConnectingInOrOut && !isConnectingInOrOut()) {
            onConnectedInOrOut();
        }

        if (changed && !wasHolding && (getState() == CdmaCall.State.HOLDING)) {
            // We've transitioned into HOLDING
            onStartedHolding();
        }

        return changed;
    
public voidupdateParent(CdmaCall oldParent, CdmaCall newParent)

        if (newParent != oldParent) {
            if (oldParent != null) {
                oldParent.detach(this);
            }
            newParent.attachFake(this, CdmaCall.State.ACTIVE);
            mParent = newParent;
        }