FileDocCategorySizeDatePackage
ImsPhoneConnection.javaAPI DocAndroid 5.1 API23204Thu Mar 12 22:22:54 GMT 2015com.android.internal.telephony.imsphone

ImsPhoneConnection

public class ImsPhoneConnection extends com.android.internal.telephony.Connection
{@hide}

Fields Summary
private static final String
LOG_TAG
private static final boolean
DBG
private ImsPhoneCallTracker
mOwner
private ImsPhoneCall
mParent
private com.android.ims.ImsCall
mImsCall
private String
mPostDialString
private boolean
mDisconnected
private long
mDisconnectTime
private int
mNextPostDialChar
private int
mCause
private PostDialState
mPostDialState
private com.android.internal.telephony.UUSInfo
mUusInfo
private android.os.Handler
mHandler
private PowerManager.WakeLock
mPartialWakeLock
private long
mConferenceConnectTime
private static final int
EVENT_DTMF_DONE
private static final int
EVENT_PAUSE_DONE
private static final int
EVENT_NEXT_POST_DIAL
private static final int
EVENT_WAKE_LOCK_TIMEOUT
private static final int
PAUSE_DELAY_MILLIS
private static final int
WAKE_LOCK_TIMEOUT_MILLIS
Constructors Summary
ImsPhoneConnection(android.content.Context context, com.android.ims.ImsCall imsCall, ImsPhoneCallTracker ct, ImsPhoneCall parent)
This is probably an MT call

        createWakeLock(context);
        acquireWakeLock();

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

        if ((imsCall != null) && (imsCall.getCallProfile() != null)) {
            mAddress = imsCall.getCallProfile().getCallExtra(ImsCallProfile.EXTRA_OI);
            mCnapName = imsCall.getCallProfile().getCallExtra(ImsCallProfile.EXTRA_CNA);
            mNumberPresentation = ImsCallProfile.OIRToPresentation(
                    imsCall.getCallProfile().getCallExtraInt(ImsCallProfile.EXTRA_OIR));
            mCnapNamePresentation = ImsCallProfile.OIRToPresentation(
                    imsCall.getCallProfile().getCallExtraInt(ImsCallProfile.EXTRA_CNAP));

            updateMediaCapabilities(imsCall);
        } else {
            mNumberPresentation = PhoneConstants.PRESENTATION_UNKNOWN;
            mCnapNamePresentation = PhoneConstants.PRESENTATION_UNKNOWN;
        }

        mIsIncoming = true;
        mCreateTime = System.currentTimeMillis();
        mUusInfo = null;

        //mIndex = index;

        mParent = parent;
        mParent.attach(this, ImsPhoneCall.State.INCOMING);
    
ImsPhoneConnection(android.content.Context context, String dialString, ImsPhoneCallTracker ct, ImsPhoneCall parent)
This is an MO call, created when dialing

        createWakeLock(context);
        acquireWakeLock();

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

        mDialString = 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();

        mParent = parent;
        parent.attachFake(this, ImsPhoneCall.State.DIALING);
    
Methods Summary
private voidacquireWakeLock()

        Rlog.d(LOG_TAG, "acquireWakeLock");
        mPartialWakeLock.acquire();
    
public voidcancelPostDial()

        setPostDialState(PostDialState.CANCELLED);
    
voidchangeParent(ImsPhoneCall parent)

        mParent = parent;
    
private voidcreateWakeLock(android.content.Context context)

        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
    
public voiddispose()

    
static booleanequalsHandlesNulls(java.lang.Object a, java.lang.Object b)

        return (a == null) ? (b == null) : a.equals (b);
    
protected voidfinalize()

        releaseWakeLock();
    
private intgetAudioQualityFromCallProfile(com.android.ims.ImsCallProfile localCallProfile, com.android.ims.ImsCallProfile remoteCallProfile)
Determines the {@link ImsPhoneConnection} audio quality based on the local and remote {@link ImsCallProfile}. If indicate a HQ audio call if the local stream profile indicates AMR_WB or EVRC_WB and there is no remote restrict cause.

param
localCallProfile The local call profile.
param
remoteCallProfile The remote call profile.
return
The audio quality.

        if (localCallProfile == null || remoteCallProfile == null
                || localCallProfile.mMediaProfile == null) {
            return AUDIO_QUALITY_STANDARD;
        }

        boolean isHighDef = (localCallProfile.mMediaProfile.mAudioQuality
                        == ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB
                || localCallProfile.mMediaProfile.mAudioQuality
                        == ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB)
                && remoteCallProfile.mRestrictCause == ImsCallProfile.CALL_RESTRICT_CAUSE_NONE;
        return isHighDef ? AUDIO_QUALITY_HIGH_DEFINITION : AUDIO_QUALITY_STANDARD;
    
public ImsPhoneCallgetCall()

        return mParent;
    
public longgetConferenceConnectTime()

return
The conference connect time.

        return mConferenceConnectTime;
    
public intgetDisconnectCause()

        return mCause;
    
public longgetDisconnectTime()

        return mDisconnectTime;
    
public longgetHoldDurationMillis()

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

        return mHoldingStartTime;
    
com.android.ims.ImsCallgetImsCall()

        return mImsCall;
    
public intgetNumberPresentation()

        return mNumberPresentation;
    
public com.android.internal.telephony.ConnectiongetOrigConnection()

        return null;
    
public java.lang.StringgetOrigDialString()

        return mDialString;
    
public ImsPhoneCallTrackergetOwner()

        return mOwner;
    
public PostDialStategetPostDialState()

        return mPostDialState;
    
public intgetPreciseDisconnectCause()

        return 0;
    
public java.lang.StringgetRemainingPostDialString()

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

        return mPostDialString.substring(mNextPostDialChar);
    
public ImsPhoneCall.StategetState()

        if (mDisconnected) {
            return ImsPhoneCall.State.DISCONNECTED;
        } else {
            return super.getState();
        }
    
public com.android.internal.telephony.UUSInfogetUUSInfo()

        return mUusInfo;
    
public voidhangup()

        if (!mDisconnected) {
            mOwner.hangup(this);
        } else {
            throw new CallStateException ("disconnected");
        }
    
public booleanisMultiparty()

        return mImsCall != null && mImsCall.isMultiparty();
    
voidonConnectedInOrOut()
An incoming or outgoing call has connected

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

        if (Phone.DEBUG_PHONE) {
            Rlog.d(LOG_TAG, "onConnectedInOrOut: connectTime=" + mConnectTime);
        }

        if (!mIsIncoming) {
            // outgoing calls only
            processNextPostDialChar();
        }
        releaseWakeLock();
    
public booleanonDisconnect(int cause)
Called when the connection has been disconnected

        Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
        if (mCause != DisconnectCause.LOCAL) mCause = cause;
        return onDisconnect();
    
booleanonDisconnect()

        boolean changed = false;

        if (!mDisconnected) {
            //mIndex = -1;

            mDisconnectTime = System.currentTimeMillis();
            mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal;
            mDisconnected = true;

            mOwner.mPhone.notifyDisconnect(this);

            if (mParent != null) {
                changed = mParent.connectionDisconnected(this);
            } else {
                Rlog.d(LOG_TAG, "onDisconnect: no parent");
            }
            if (mImsCall != null) mImsCall.close();
            mImsCall = null;
        }
        releaseWakeLock();
        return changed;
    
public voidonDisconnectConferenceParticipant(android.net.Uri endpoint)
Notifies this Connection of a request to disconnect a participant of the conference managed by the connection.

param
endpoint the {@link android.net.Uri} of the participant to disconnect.

        ImsCall imsCall = getImsCall();
        if (imsCall == null) {
            return;
        }
        try {
            imsCall.removeParticipants(new String[]{endpoint.toString()});
        } catch (ImsException e) {
            // No session in place -- no change
            Rlog.e(LOG_TAG, "onDisconnectConferenceParticipant: no session in place. "+
                    "Failed to disconnect endpoint = " + endpoint);
        }
    
voidonHangupLocal()
Called when this Connection is being hung up locally (eg, user pressed "end")

        mCause = DisconnectCause.LOCAL;
    
voidonStartedHolding()

        mHoldingStartTime = SystemClock.elapsedRealtime();
    
public voidproceedAfterWaitChar()

        if (mPostDialState != PostDialState.WAIT) {
            Rlog.w(LOG_TAG, "ImsPhoneConnection.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, "ImsPhoneConnection.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) {
            Rlog.d(LOG_TAG, "proceedAfterWildChar: new postDialString is " +
                    mPostDialString);
        }

        processNextPostDialChar();
    
private voidprocessNextPostDialChar()

        char c = 0;
        Registrant postDialHandler;

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

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

            // 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(LOG_TAG, "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;

            //Rlog.v(LOG_TAG, "##### processNextPostDialChar: send msg to postDialHandler, 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.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
        } else if (c == PhoneNumberUtils.PAUSE) {
            // From TS 22.101:
            // It continues...
            // Upon the called party answering the UE shall send the DTMF digits
            // automatically to the network after a delay of 3 seconds( 20 ).
            // The digits shall be sent according to the procedures and timing
            // specified in 3GPP TS 24.008 [13]. The first occurrence of the
            // "DTMF Control Digits Separator" shall be used by the ME to
            // distinguish between the addressing digits (i.e. the phone number)
            // and the DTMF digits. Upon subsequent occurrences of the
            // separator,
            // the UE shall pause again for 3 seconds ( 20 ) 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;
    
voidreleaseWakeLock()

        synchronized(mPartialWakeLock) {
            if (mPartialWakeLock.isHeld()) {
                Rlog.d(LOG_TAG, "releaseWakeLock");
                mPartialWakeLock.release();
            }
        }
    
public voidseparate()

        throw new CallStateException ("not supported");
    
public voidsetConferenceConnectTime(long conferenceConnectTime)
Sets the conference connect time. Used when an {@code ImsConference} is created to out of this phone connection.

param
conferenceConnectTime The conference connect time.

        mConferenceConnectTime = conferenceConnectTime;
    
public voidsetDisconnectCause(int cause)

        mCause = cause;
    
voidsetImsCall(com.android.ims.ImsCall imsCall)

        mImsCall = imsCall;
    
private voidsetPostDialState(PostDialState s)
Set post dial state and acquire wake lock while switching to "started" state, the wake lock will be released if state switches out of "started" state or after WAKE_LOCK_TIMEOUT_MILLIS.

param
s new PostDialState

        if (mPostDialState != PostDialState.STARTED
                && s == PostDialState.STARTED) {
            acquireWakeLock();
            Message msg = mHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
            mHandler.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS);
        } else if (mPostDialState == PostDialState.STARTED
                && s != PostDialState.STARTED) {
            mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
            releaseWakeLock();
        }
        mPostDialState = s;
        notifyPostDialListeners();
    
public java.lang.StringtoString()
Provides a string representation of the {@link ImsPhoneConnection}. Primarily intended for use in log statements.

return
String representation of call.

        StringBuilder sb = new StringBuilder();
        sb.append("[ImsPhoneConnection objId: ");
        sb.append(System.identityHashCode(this));
        sb.append(" address:");
        sb.append(Log.pii(getAddress()));
        sb.append(" ImsCall:");
        if (mImsCall == null) {
            sb.append("null");
        } else {
            sb.append(mImsCall);
        }
        sb.append("]");
        return sb.toString();
    
booleanupdate(com.android.ims.ImsCall imsCall, ImsPhoneCall.State state)

return
{@code true} if the {@link ImsPhoneConnection} or its media capabilities have been changed, and {@code false} otherwise.

        if (state == ImsPhoneCall.State.ACTIVE) {
            if (mParent.getState().isRinging() || mParent.getState().isDialing()) {
                onConnectedInOrOut();
            }

            if (mParent.getState().isRinging() || mParent == mOwner.mBackgroundCall) {
                //mForegroundCall should be IDLE
                //when accepting WAITING call
                //before accept WAITING call,
                //the ACTIVE call should be held ahead
                mParent.detach(this);
                mParent = mOwner.mForegroundCall;
                mParent.attach(this);
            }
        } else if (state == ImsPhoneCall.State.HOLDING) {
            onStartedHolding();
        }

        boolean updateParent = mParent.update(this, imsCall, state);
        boolean updateMediaCapabilities = updateMediaCapabilities(imsCall);
        return updateParent || updateMediaCapabilities;
    
private booleanupdateMediaCapabilities(com.android.ims.ImsCall imsCall)
Check for a change in the video capabilities and audio quality for the {@link ImsCall}, and update the {@link ImsPhoneConnection} with this information.

param
imsCall The call to check for changes in media capabilities.
return
Whether the media capabilities have been changed.

        if (imsCall == null) {
            return false;
        }

        boolean changed = false;

        try {
            // The actual call profile (negotiated between local and peer).
            ImsCallProfile negotiatedCallProfile = imsCall.getCallProfile();
            // The capabilities of the local device.
            ImsCallProfile localCallProfile = imsCall.getLocalCallProfile();
            // The capabilities of the peer device.
            ImsCallProfile remoteCallProfile = imsCall.getRemoteCallProfile();

            if (negotiatedCallProfile != null) {
                int callType = negotiatedCallProfile.mCallType;

                int newVideoState = ImsCallProfile.getVideoStateFromCallType(callType);
                if (getVideoState() != newVideoState) {
                    setVideoState(newVideoState);
                    changed = true;
                }
            }

            if (localCallProfile != null) {
                int callType = localCallProfile.mCallType;

                boolean newLocalVideoCapable = callType == ImsCallProfile.CALL_TYPE_VT;
                if (isLocalVideoCapable() != newLocalVideoCapable) {
                    setLocalVideoCapable(newLocalVideoCapable);
                    changed = true;
                }
            }

            int newAudioQuality =
                    getAudioQualityFromCallProfile(localCallProfile, remoteCallProfile);
            if (getAudioQuality() != newAudioQuality) {
                setAudioQuality(newAudioQuality);
                changed = true;
            }
        } catch (ImsException e) {
            // No session in place -- no change
        }

        return changed;