Methods Summary |
---|
public void | accept(int callType)Accepts a call.
if (VDBG) {
log("accept ::");
}
accept(callType, new ImsStreamMediaProfile());
|
public void | accept(int callType, ImsStreamMediaProfile profile)Accepts a call.
if (VDBG) {
log("accept :: callType=" + callType + ", profile=" + profile);
}
synchronized(mLockObj) {
if (mSession == null) {
throw new ImsException("No call to answer",
ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
}
try {
mSession.accept(callType, profile);
} catch (Throwable t) {
loge("accept :: ", t);
throw new ImsException("accept()", t, 0);
}
if (mInCall && (mProposedCallProfile != null)) {
if (DBG) {
log("accept :: call profile will be updated");
}
mCallProfile = mProposedCallProfile;
mProposedCallProfile = null;
}
// Other call update received
if (mInCall && (mUpdateRequest == UPDATE_UNSPECIFIED)) {
mUpdateRequest = UPDATE_NONE;
}
}
|
public void | attachSession(com.android.ims.internal.ImsCallSession session)Attaches an incoming call to this call object.
if (DBG) {
log("attachSession :: session=" + session);
}
synchronized(mLockObj) {
mSession = session;
try {
mSession.setListener(createCallSessionListener());
} catch (Throwable t) {
loge("attachSession :: ", t);
throwImsException(t, 0);
}
}
|
public boolean | checkIfRemoteUserIsSame(java.lang.String userId)Checks if the call has a same remote user identity or not.
if (userId == null) {
return false;
}
return userId.equals(mCallProfile.getCallExtra(ImsCallProfile.EXTRA_REMOTE_URI, ""));
|
private void | clear(ImsReasonInfo lastReasonInfo)
mInCall = false;
mHold = false;
mUpdateRequest = UPDATE_NONE;
mLastReasonInfo = lastReasonInfo;
|
private void | clearMergeInfo()Clears the merge peer for this call, ensuring that the peer's connection to this call is also
severed at the same time.
if (VDBG) {
log("clearMergeInfo :: clearing all merge info");
}
// First clear out the merge partner then clear ourselves out.
if (mMergeHost != null) {
mMergeHost.mMergePeer = null;
mMergeHost.mUpdateRequest = UPDATE_NONE;
mMergeHost.mCallSessionMergePending = false;
}
if (mMergePeer != null) {
mMergePeer.mMergeHost = null;
mMergePeer.mUpdateRequest = UPDATE_NONE;
mMergePeer.mCallSessionMergePending = false;
}
mMergeHost = null;
mMergePeer = null;
mUpdateRequest = UPDATE_NONE;
mCallSessionMergePending = false;
|
private void | clearSessionTerminationFlags()
mSessionEndDuringMerge = false;
mSessionEndDuringMergeReasonInfo = null;
|
public void | close()Closes this object. This object is not usable after being closed.
synchronized(mLockObj) {
if (mSession != null) {
mSession.close();
mSession = null;
}
mCallProfile = null;
mProposedCallProfile = null;
mLastReasonInfo = null;
mMediaSession = null;
}
|
public void | conferenceStateUpdated(ImsConferenceState state)Report a new conference state to the current {@link ImsCall} and inform listeners of the
change. Marked as {@code VisibleForTesting} so that the
{@code com.android.internal.telephony.TelephonyTester} class can inject a test conference
event package into a regular ongoing IMS call.
Listener listener;
synchronized(this) {
notifyConferenceStateUpdated(state);
listener = mListener;
}
if (listener != null) {
try {
listener.onCallConferenceStateUpdated(this, state);
} catch (Throwable t) {
loge("callSessionConferenceStateUpdated :: ", t);
}
}
|
private ImsCallSession.Listener | createCallSessionListener()Creates an IMS call session listener.
return new ImsCallSessionListenerProxy();
|
private ImsStreamMediaProfile | createHoldMediaProfile()
ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile();
if (mCallProfile == null) {
return mediaProfile;
}
mediaProfile.mAudioQuality = mCallProfile.mMediaProfile.mAudioQuality;
mediaProfile.mVideoQuality = mCallProfile.mMediaProfile.mVideoQuality;
mediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND;
if (mediaProfile.mVideoQuality != ImsStreamMediaProfile.VIDEO_QUALITY_NONE) {
mediaProfile.mVideoDirection = ImsStreamMediaProfile.DIRECTION_SEND;
}
return mediaProfile;
|
private com.android.ims.ImsCall | createNewCall(com.android.ims.internal.ImsCallSession session, ImsCallProfile profile)
ImsCall call = new ImsCall(mContext, profile);
try {
call.attachSession(session);
} catch (ImsException e) {
if (call != null) {
call.close();
call = null;
}
}
// Do additional operations...
return call;
|
private ImsStreamMediaProfile | createResumeMediaProfile()
ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile();
if (mCallProfile == null) {
return mediaProfile;
}
mediaProfile.mAudioQuality = mCallProfile.mMediaProfile.mAudioQuality;
mediaProfile.mVideoQuality = mCallProfile.mMediaProfile.mVideoQuality;
mediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
if (mediaProfile.mVideoQuality != ImsStreamMediaProfile.VIDEO_QUALITY_NONE) {
mediaProfile.mVideoDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
}
return mediaProfile;
|
private void | enforceConversationMode()
if (mInCall) {
mHold = false;
mUpdateRequest = UPDATE_NONE;
}
|
public boolean | equalsTo(com.android.ims.internal.ICall call)Checks if the call is equal or not.
if (call == null) {
return false;
}
if (call instanceof ImsCall) {
return this.equals(call);
}
return false;
|
public void | extendToConference(java.lang.String[] participants)Extends this call (1-to-1 call) to the conference call
inviting the specified participants to.
if (VDBG) {
log("extendToConference ::");
}
if (isOnHold()) {
if (DBG) {
log("extendToConference :: call is on hold");
}
throw new ImsException("Not in a call to extend a call to conference",
ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
}
synchronized(mLockObj) {
if (mUpdateRequest != UPDATE_NONE) {
if (DBG) {
log("extendToConference :: update is in progress; request=" +
updateRequestToString(mUpdateRequest));
}
throw new ImsException("Call update is in progress",
ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
}
if (mSession == null) {
loge("extendToConference :: ");
throw new ImsException("No call session",
ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
}
mSession.extendToConference(participants);
mUpdateRequest = UPDATE_EXTEND_TO_CONFERENCE;
}
|
public java.lang.String | getCallExtra(java.lang.String name)Gets the specified property of this call.
// Lookup the cache
synchronized(mLockObj) {
// If not found, try to get the property from the remote
if (mSession == null) {
throw new ImsException("No call session",
ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
}
try {
return mSession.getProperty(name);
} catch (Throwable t) {
loge("getCallExtra :: ", t);
throw new ImsException("getCallExtra()", t, 0);
}
}
|
public ImsCallProfile | getCallProfile()Gets the negotiated (local & remote) call profile.
synchronized(mLockObj) {
return mCallProfile;
}
|
public com.android.ims.internal.ImsCallSession | getCallSession()Gets the {@link ImsCallSession} that carries this call.
synchronized(mLockObj) {
return mSession;
}
|
public ImsReasonInfo | getLastReasonInfo()Gets the last reason information when the call is not established, cancelled or terminated.
synchronized(mLockObj) {
return mLastReasonInfo;
}
|
public ImsCallProfile | getLocalCallProfile()Gets the local call profile (local capabilities).
synchronized(mLockObj) {
if (mSession == null) {
throw new ImsException("No call session",
ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
}
try {
return mSession.getLocalCallProfile();
} catch (Throwable t) {
loge("getLocalCallProfile :: ", t);
throw new ImsException("getLocalCallProfile()", t, 0);
}
}
|
public com.android.ims.internal.ImsStreamMediaSession | getMediaSession()Gets the {@link ImsStreamMediaSession} that handles the media operation of this call.
Almost interface APIs are for the VT (Video Telephony).
synchronized(mLockObj) {
return mMediaSession;
}
|
public ImsCallProfile | getProposedCallProfile()Gets the call profile proposed by the local/remote user.
synchronized(mLockObj) {
if (!isInCall()) {
return null;
}
return mProposedCallProfile;
}
|
public ImsCallProfile | getRemoteCallProfile()Gets the remote call profile (remote capabilities).
synchronized(mLockObj) {
if (mSession == null) {
throw new ImsException("No call session",
ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
}
try {
return mSession.getRemoteCallProfile();
} catch (Throwable t) {
loge("getRemoteCallProfile :: ", t);
throw new ImsException("getRemoteCallProfile()", t, 0);
}
}
|
public int | getState()Gets the state of the {@link ImsCallSession} that carries this call.
The value returned must be one of the states in {@link ImsCallSession#State}.
synchronized(mLockObj) {
if (mSession == null) {
return ImsCallSession.State.IDLE;
}
return mSession.getState();
}
|
public boolean | hasPendingUpdate()Checks if the call has a pending update operation.
synchronized(mLockObj) {
return (mUpdateRequest != UPDATE_NONE);
}
|
public void | hold()Puts a call on hold. When succeeds, {@link Listener#onCallHeld} is called.
if (VDBG) {
log("hold :: ImsCall=" + this);
}
if (isOnHold()) {
if (DBG) {
log("hold :: call is already on hold");
}
return;
}
synchronized(mLockObj) {
if (mUpdateRequest != UPDATE_NONE) {
loge("hold :: update is in progress; request=" +
updateRequestToString(mUpdateRequest));
throw new ImsException("Call update is in progress",
ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
}
if (mSession == null) {
loge("hold :: ");
throw new ImsException("No call session",
ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
}
mSession.hold(createHoldMediaProfile());
// FIXME: update the state on the callback?
mHold = true;
mUpdateRequest = UPDATE_HOLD;
}
|
public void | inviteParticipants(java.lang.String[] participants)Requests the conference server to invite an additional participants to the conference.
if (VDBG) {
log("inviteParticipants ::");
}
synchronized(mLockObj) {
if (mSession == null) {
loge("inviteParticipants :: ");
throw new ImsException("No call session",
ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
}
mSession.inviteParticipants(participants);
}
|
private boolean | isCallSessionMergePending()Determines if the call session is pending merge into a conference or not.
return mCallSessionMergePending;
|
public boolean | isInCall()Checks if the call is established.
synchronized(mLockObj) {
return mInCall;
}
|
private boolean | isMergeHost()Determines if the current call is the host of the merge.
return mMergePeer != null && mMergeHost == null;
|
private boolean | isMergePeer()Determines if the current call is the peer of the merge.
return mMergePeer == null && mMergeHost != null;
|
public boolean | isMerged()
return mIsMerged;
|
private boolean | isMerging()Determines if the current call is in the process of merging with another call or conference.
return mMergePeer != null || mMergeHost != null;
|
public boolean | isMultiparty()Determines if the call is a multiparty call.
synchronized(mLockObj) {
if (mSession == null) {
return false;
}
return mSession.isMultiparty();
}
|
public boolean | isMuted()Checks if the call is muted.
synchronized(mLockObj) {
return mMute;
}
|
public boolean | isOnHold()Checks if the call is on hold.
synchronized(mLockObj) {
return mHold;
}
|
public static boolean | isSessionAlive(com.android.ims.internal.ImsCallSession session)
return session != null && session.isAlive();
|
private boolean | isTransientConferenceSession(com.android.ims.internal.ImsCallSession session)This function determines if the ImsCallSession is our actual ImsCallSession or if is
the transient session used in the process of creating a conference. This function should only
be called within callbacks that are not directly related to conference merging but might
potentially still be called on the transient ImsCallSession sent to us from
callSessionMergeStarted() when we don't really care. In those situations, we probably don't
want to take any action so we need to know that we can return early.
if (session != null && session != mSession && session == mTransientConferenceSession) {
return true;
}
return false;
|
private void | log(java.lang.String s)
Rlog.d(TAG, s);
|
private void | loge(java.lang.String s)
Rlog.e(TAG, s);
|
private void | loge(java.lang.String s, java.lang.Throwable t)
Rlog.e(TAG, s, t);
|
private void | logv(java.lang.String s)Logs the specified message, as well as the current instance of {@link ImsCall}.
StringBuilder sb = new StringBuilder();
sb.append(s);
sb.append(" imsCall=");
sb.append(ImsCall.this);
Rlog.v(TAG, sb.toString());
|
private void | merge()Merges the active & hold call.
if (VDBG) {
log("merge :: ImsCall=" + this);
}
synchronized(mLockObj) {
if (mUpdateRequest != UPDATE_NONE) {
loge("merge :: update is in progress; request=" +
updateRequestToString(mUpdateRequest));
throw new ImsException("Call update is in progress",
ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
}
if (mSession == null) {
loge("merge :: no call session");
throw new ImsException("No call session",
ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
}
// if skipHoldBeforeMerge = true, IMS service implementation will
// merge without explicitly holding the call.
if (mHold || (mContext.getResources().getBoolean(
com.android.internal.R.bool.skipHoldBeforeMerge))) {
if (mMergePeer != null && !mMergePeer.isMultiparty() && !isMultiparty()) {
// We only set UPDATE_MERGE when we are adding the first
// calls to the Conference. If there is already a conference
// no special handling is needed. The existing conference
// session will just go active and any other sessions will be terminated
// if needed. There will be no merge failed callback.
// Mark both the host and peer UPDATE_MERGE to ensure both are aware that a
// merge is pending.
mUpdateRequest = UPDATE_MERGE;
mMergePeer.mUpdateRequest = UPDATE_MERGE;
}
mSession.merge();
} else {
// This code basically says, we need to explicitly hold before requesting a merge
// when we get the callback that the hold was successful (or failed), we should
// automatically request a merge.
mSession.hold(createHoldMediaProfile());
mHold = true;
mUpdateRequest = UPDATE_HOLD_MERGE;
}
}
|
public void | merge(com.android.ims.ImsCall bgCall)Merges the active & hold call.
if (VDBG) {
log("merge(1) :: bgImsCall=" + bgCall);
}
if (bgCall == null) {
throw new ImsException("No background call",
ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
}
synchronized(mLockObj) {
// Mark both sessions as pending merge.
this.setCallSessionMergePending(true);
bgCall.setCallSessionMergePending(true);
if ((!isMultiparty() && !bgCall.isMultiparty()) || isMultiparty()) {
// If neither call is multiparty, the current call is the merge host and the bg call
// is the merge peer (ie we're starting a new conference).
// OR
// If this call is multiparty, it is the merge host and the other call is the merge
// peer.
setMergePeer(bgCall);
} else {
// If the bg call is multiparty, it is the merge host.
setMergeHost(bgCall);
}
}
merge();
|
private void | mergeInternal()
if (VDBG) {
log("mergeInternal :: ImsCall=" + this);
}
mSession.merge();
mUpdateRequest = UPDATE_MERGE;
|
private void | notifyConferenceSessionTerminated(ImsReasonInfo reasonInfo)
ImsCall.Listener listener = mListener;
clear(reasonInfo);
if (listener != null) {
try {
listener.onCallTerminated(this, reasonInfo);
} catch (Throwable t) {
loge("notifyConferenceSessionTerminated :: ", t);
}
}
|
private void | notifyConferenceStateUpdated(ImsConferenceState state)
Set<Entry<String, Bundle>> participants = state.mParticipants.entrySet();
if (participants == null) {
return;
}
Iterator<Entry<String, Bundle>> iterator = participants.iterator();
List<ConferenceParticipant> conferenceParticipants = new ArrayList<>(participants.size());
while (iterator.hasNext()) {
Entry<String, Bundle> entry = iterator.next();
String key = entry.getKey();
Bundle confInfo = entry.getValue();
String status = confInfo.getString(ImsConferenceState.STATUS);
String user = confInfo.getString(ImsConferenceState.USER);
String displayName = confInfo.getString(ImsConferenceState.DISPLAY_TEXT);
String endpoint = confInfo.getString(ImsConferenceState.ENDPOINT);
if (DBG) {
log("notifyConferenceStateUpdated :: key=" + key +
", status=" + status +
", user=" + user +
", displayName= " + displayName +
", endpoint=" + endpoint);
}
Uri handle = Uri.parse(user);
Uri endpointUri = Uri.parse(endpoint);
int connectionState = ImsConferenceState.getConnectionStateForStatus(status);
ConferenceParticipant conferenceParticipant = new ConferenceParticipant(handle,
displayName, endpointUri, connectionState);
conferenceParticipants.add(conferenceParticipant);
}
if (!conferenceParticipants.isEmpty() && mListener != null) {
try {
mListener.onConferenceParticipantsStateChanged(this, conferenceParticipants);
} catch (Throwable t) {
loge("notifyConferenceStateUpdated :: ", t);
}
}
|
private void | notifyError(int reason, int statusCode, java.lang.String message)
|
private void | notifySessionTerminatedDuringMerge()Handles the case where the session has ended during a merge by reporting the termination
reason to listeners.
ImsCall.Listener listener;
boolean notifyFailure = false;
ImsReasonInfo notifyFailureReasonInfo = null;
synchronized(ImsCall.this) {
listener = mListener;
if (mSessionEndDuringMerge) {
// Set some local variables that will send out a notification about a
// previously buried termination callback for our primary session now that
// we know that this is not due to the conference call merging successfully.
if (DBG) {
log("notifySessionTerminatedDuringMerge ::reporting terminate during merge");
}
notifyFailure = true;
notifyFailureReasonInfo = mSessionEndDuringMergeReasonInfo;
}
clearSessionTerminationFlags();
}
if (listener != null && notifyFailure) {
try {
processCallTerminated(notifyFailureReasonInfo);
} catch (Throwable t) {
loge("notifySessionTerminatedDuringMerge :: ", t);
}
}
|
private void | processCallTerminated(ImsReasonInfo reasonInfo)Perform all cleanup and notification around the termination of a session.
Note that there are 2 distinct modes of operation. The first is when
we receive a session termination on the primary session when we are
in the processing of merging. The second is when we are not merging anything
and the call is terminated.
if (VDBG) {
String reasonString = reasonInfo != null ? reasonInfo.toString() : "null";
log("processCallTerminated :: ImsCall=" + this + " reason=" + reasonString);
}
ImsCall.Listener listener = null;
synchronized(ImsCall.this) {
// If we are in the midst of establishing a conference, we will bury the termination
// until the merge has completed. If necessary we can surface the termination at this
// point.
if (isCallSessionMergePending()) {
// Since we are in the process of a merge, this trigger means something
// else because it is probably due to the merge happening vs. the
// session is really terminated. Let's flag this and revisit if
// the merge() ends up failing because we will need to take action on the
// mSession in that case since the termination was not due to the merge
// succeeding.
if (DBG) {
log("processCallTerminated :: burying termination during ongoing merge.");
}
mSessionEndDuringMerge = true;
mSessionEndDuringMergeReasonInfo = reasonInfo;
return;
}
// If we are terminating the conference call, notify using conference listeners.
if (isMultiparty()) {
notifyConferenceSessionTerminated(reasonInfo);
return;
} else {
listener = mListener;
clear(reasonInfo);
}
}
if (listener != null) {
try {
listener.onCallTerminated(ImsCall.this, reasonInfo);
} catch (Throwable t) {
loge("processCallTerminated :: ", t);
}
}
|
private void | processMergeComplete()We have detected that a initial conference call has been fully configured. The internal
state of both {@code ImsCall} objects need to be cleaned up to reflect the new state.
This function should only be called in the context of the merge host to simplify logic
if (VDBG) {
log("processMergeComplete :: ImsCall=" + this);
}
// The logic simplifies if we can assume that this function is only called on
// the merge host.
if (!isMergeHost()) {
loge("processMergeComplete :: We are not the merge host!");
return;
}
ImsCall.Listener listener;
boolean swapRequired = false;
synchronized(ImsCall.this) {
ImsCall finalHostCall = this;
ImsCall finalPeerCall = mMergePeer;
if (isMultiparty()) {
// The only clean up that we need to do for a merge into an existing conference
// is to deal with the disconnect of the peer if it was successfully added to
// the conference.
setIsMerged(false);
if (!isSessionAlive(mMergePeer.mSession)) {
// If the peer is dead, let's not play a disconnect sound for it when we
// unbury the termination callback.
mMergePeer.setIsMerged(true);
} else {
mMergePeer.setIsMerged(false);
}
} else {
// If we are here, we are not trying to merge a new call into an existing
// conference. That means that there is a transient session on the merge
// host that represents the future conference once all the parties
// have been added to it. So make sure that it exists or else something
// very wrong is going on.
if (mTransientConferenceSession == null) {
loge("processMergeComplete :: No transient session!");
return;
}
if (mMergePeer == null) {
loge("processMergeComplete :: No merge peer!");
return;
}
// Since we are the host, we have the transient session attached to us. Let's detach
// it and figure out where we need to set it for the final conference configuration.
ImsCallSession transientConferenceSession = mTransientConferenceSession;
mTransientConferenceSession = null;
// Clear the listener for this transient session, we'll create a new listener
// when it is attached to the final ImsCall that it should live on.
transientConferenceSession.setListener(null);
// Determine which call the transient session should be moved to. If the current
// call session is still alive and the merge peer's session is not, we have a
// situation where the current call failed to merge into the conference but the
// merge peer did merge in to the conference. In this type of scenario the current
// call will continue as a single party call, yet the background call will become
// the conference.
if (isSessionAlive(mSession) && !isSessionAlive(mMergePeer.getCallSession())) {
// I'm the host but we are moving the transient session to the peer since its
// session was disconnected and my session is still alive. This signifies that
// their session was properly added to the conference but mine was not because
// it is probably in the held state as opposed to part of the final conference.
// In this case, we need to set isMerged to false on both calls so the
// disconnect sound is called when either call disconnects.
// Note that this case is only valid if this is an initial conference being
// brought up.
finalHostCall = mMergePeer;
finalPeerCall = this;
swapRequired = true;
setIsMerged(false);
mMergePeer.setIsMerged(false);
if (VDBG) {
log("processMergeComplete :: transient will transfer to merge peer");
}
} else if (!isSessionAlive(mSession) && isSessionAlive(mMergePeer.getCallSession())) {
// The transient session stays with us and the disconnect sound should be played
// when the merge peer eventually disconnects since it was not actually added to
// the conference and is probably sitting in the held state.
finalHostCall = this;
finalPeerCall = mMergePeer;
swapRequired = false;
setIsMerged(false);
mMergePeer.setIsMerged(false); // Play the disconnect sound
if (VDBG) {
log("processMergeComplete :: transient will stay with the merge host");
}
} else {
// The transient session stays with us and the disconnect sound should not be
// played when we ripple up the disconnect for the merge peer because it was
// only disconnected to be added to the conference.
finalHostCall = this;
finalPeerCall = mMergePeer;
swapRequired = false;
setIsMerged(false);
mMergePeer.setIsMerged(true);
if (VDBG) {
log("processMergeComplete :: transient will stay with us (I'm the host).");
}
}
if (VDBG) {
log("processMergeComplete :: call=" + finalHostCall + " is the final host");
}
// Add the transient session to the ImsCall that ended up being the host for the
// conference.
finalHostCall.setTransientSessionAsPrimary(transientConferenceSession);
}
listener = finalHostCall.mListener;
// Clear all the merge related flags.
clearMergeInfo();
// For the final peer...let's bubble up any possible disconnects that we had
// during the merge process
finalPeerCall.notifySessionTerminatedDuringMerge();
// For the final host, let's just bury the disconnects that we my have received
// during the merge process since we are now the host of the conference call.
finalHostCall.clearSessionTerminationFlags();
}
if (listener != null) {
try {
listener.onCallMerged(ImsCall.this, swapRequired);
} catch (Throwable t) {
loge("processMergeComplete :: ", t);
}
}
return;
|
private void | processMergeFailed(ImsReasonInfo reasonInfo)We received a callback from ImsCallSession that a merge failed. Clean up all
internal state to represent this state change. The calling function is a callback
and should have been called on the session that was in the foreground
when merge() was originally called. It is assumed that this function will be called
on the merge host.
if (VDBG) {
log("processMergeFailed :: this=" + this + "reason=" + reasonInfo);
}
ImsCall.Listener listener;
synchronized(ImsCall.this) {
// The logic simplifies if we can assume that this function is only called on
// the merge host.
if (!isMergeHost()) {
loge("processMergeFailed :: We are not the merge host!");
return;
}
if (mMergePeer == null) {
loge("processMergeFailed :: No merge peer!");
return;
}
if (!isMultiparty()) {
if (mTransientConferenceSession == null) {
loge("processMergeFailed :: No transient session!");
return;
}
// Clean up any work that we performed on the transient session.
mTransientConferenceSession.setListener(null);
mTransientConferenceSession = null;
}
// Ensure the calls being conferenced into the conference has isMerged = false.
setIsMerged(false);
mMergePeer.setIsMerged(false);
listener = mListener;
// Ensure any terminations are surfaced from this session.
notifySessionTerminatedDuringMerge();
mMergePeer.notifySessionTerminatedDuringMerge();
// Clear all the various flags around coordinating this merge.
clearMergeInfo();
}
if (listener != null) {
try {
listener.onCallMergeFailed(ImsCall.this, reasonInfo);
} catch (Throwable t) {
loge("processMergeFailed :: ", t);
}
}
return;
|
public void | reject(int reason)Rejects a call.
if (VDBG) {
log("reject :: reason=" + reason);
}
synchronized(mLockObj) {
if (mSession != null) {
mSession.reject(reason);
}
if (mInCall && (mProposedCallProfile != null)) {
if (DBG) {
log("reject :: call profile is not updated; destroy it...");
}
mProposedCallProfile = null;
}
// Other call update received
if (mInCall && (mUpdateRequest == UPDATE_UNSPECIFIED)) {
mUpdateRequest = UPDATE_NONE;
}
}
|
public void | removeParticipants(java.lang.String[] participants)Requests the conference server to remove the specified participants from the conference.
if (DBG) {
log("removeParticipants ::");
}
synchronized(mLockObj) {
if (mSession == null) {
loge("removeParticipants :: ");
throw new ImsException("No call session",
ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
}
mSession.removeParticipants(participants);
}
|
public void | resume()Continues a call that's on hold. When succeeds, {@link Listener#onCallResumed} is called.
if (VDBG) {
log("resume :: ImsCall=" + this);
}
if (!isOnHold()) {
if (DBG) {
log("resume :: call is in conversation");
}
return;
}
synchronized(mLockObj) {
if (mUpdateRequest != UPDATE_NONE) {
loge("resume :: update is in progress; request=" +
updateRequestToString(mUpdateRequest));
throw new ImsException("Call update is in progress",
ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
}
if (mSession == null) {
loge("resume :: ");
throw new ImsException("No call session",
ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
}
// mHold is set to false in confirmation callback that the
// ImsCall was resumed.
mUpdateRequest = UPDATE_RESUME;
mSession.resume(createResumeMediaProfile());
}
|
public void | sendDtmf(char c, android.os.Message result)Sends a DTMF code. According to RFC 2833,
event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
and event flash to 16. Currently, event flash is not supported.
if (VDBG) {
log("sendDtmf :: code=" + c);
}
synchronized(mLockObj) {
if (mSession != null) {
mSession.sendDtmf(c, result);
}
}
|
public void | sendUssd(java.lang.String ussdMessage)Sends an USSD message.
if (VDBG) {
log("sendUssd :: ussdMessage=" + ussdMessage);
}
synchronized(mLockObj) {
if (mSession == null) {
loge("sendUssd :: ");
throw new ImsException("No call session",
ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
}
mSession.sendUssd(ussdMessage);
}
|
private void | setCallSessionMergePending(boolean callSessionMergePending)Sets flag indicating whether the call session is pending merge into a conference or not.
mCallSessionMergePending = callSessionMergePending;
|
public void | setIsMerged(boolean isMerged)Marks whether an IMS call is merged. This should be set {@code true} when the call merges
into a conference.
mIsMerged = isMerged;
|
public void | setListener(com.android.ims.ImsCall$Listener listener)Sets the listener to listen to the IMS call events.
The method calls {@link #setListener setListener(listener, false)}.
setListener(listener, false);
|
public void | setListener(com.android.ims.ImsCall$Listener listener, boolean callbackImmediately)Sets the listener to listen to the IMS call events.
A {@link ImsCall} can only hold one listener at a time. Subsequent calls
to this method override the previous listener.
boolean inCall;
boolean onHold;
int state;
ImsReasonInfo lastReasonInfo;
synchronized(mLockObj) {
mListener = listener;
if ((listener == null) || !callbackImmediately) {
return;
}
inCall = mInCall;
onHold = mHold;
state = getState();
lastReasonInfo = mLastReasonInfo;
}
try {
if (lastReasonInfo != null) {
listener.onCallError(this, lastReasonInfo);
} else if (inCall) {
if (onHold) {
listener.onCallHeld(this);
} else {
listener.onCallStarted(this);
}
} else {
switch (state) {
case ImsCallSession.State.ESTABLISHING:
listener.onCallProgressing(this);
break;
case ImsCallSession.State.TERMINATED:
listener.onCallTerminated(this, lastReasonInfo);
break;
default:
// Ignore it. There is no action in the other state.
break;
}
}
} catch (Throwable t) {
loge("setListener()", t);
}
|
public void | setMergeHost(com.android.ims.ImsCall mergeHost)Sets the merge hody for the current call. The merge host is the foreground call this call
will be merged into. On the merge host, sets the merge peer to be this call.
mMergeHost = mergeHost;
mMergePeer = null;
mergeHost.mMergeHost = null;
mergeHost.mMergePeer = ImsCall.this;
|
private void | setMergePeer(com.android.ims.ImsCall mergePeer)Sets the merge peer for the current call. The merge peer is the background call that will be
merged into this call. On the merge peer, sets the merge host to be this call.
mMergePeer = mergePeer;
mMergeHost = null;
mergePeer.mMergeHost = ImsCall.this;
mergePeer.mMergePeer = null;
|
public void | setMute(boolean muted)Mutes or unmutes the mic for the active call.
synchronized(mLockObj) {
if (mMute != muted) {
mMute = muted;
try {
mSession.setMute(muted);
} catch (Throwable t) {
loge("setMute :: ", t);
throwImsException(t, 0);
}
}
}
|
private void | setTransientSessionAsPrimary(com.android.ims.internal.ImsCallSession transientSession)
synchronized (ImsCall.this) {
mSession.setListener(null);
mSession = transientSession;
mSession.setListener(createCallSessionListener());
}
|
private boolean | shouldProcessConferenceResult()Determines if there is a conference merge in process. If there is a merge in process,
determines if both the merge host and peer sessions have completed the merge process. This
means that we have received terminate or hold signals for the sessions, indicating that they
are no longer in the process of being merged into the conference.
The sessions are considered to have merged if: both calls still have merge peer/host
relationships configured, both sessions are not waiting to be merged into the conference,
and the transient conference session is alive in the case of an initial conference.
boolean areMergeTriggersDone = false;
synchronized (ImsCall.this) {
// if there is a merge going on, then the merge host/peer relationships should have been
// set up. This works for both the initial conference or merging a call into an
// existing conference.
if (!isMergeHost() && !isMergePeer()) {
if (VDBG) {
log("shouldProcessConferenceResult :: no merge in progress");
}
return false;
}
// There is a merge in progress, so check the sessions to ensure:
// 1. Both calls have completed being merged (or failing to merge) into the conference.
// 2. The transient conference session is alive.
if (isMergeHost()) {
if (VDBG) {
log("shouldProcessConferenceResult :: We are a merge host=" + this);
log("shouldProcessConferenceResult :: Here is the merge peer=" + mMergePeer);
}
areMergeTriggersDone = !isCallSessionMergePending() &&
!mMergePeer.isCallSessionMergePending();
if (!isMultiparty()) {
// Only check the transient session when there is no existing conference
areMergeTriggersDone &= isSessionAlive(mTransientConferenceSession);
}
} else if (isMergePeer()) {
if (VDBG) {
log("shouldProcessConferenceResult :: We are a merge peer=" + this);
log("shouldProcessConferenceResult :: Here is the merge host=" + mMergeHost);
}
areMergeTriggersDone = !isCallSessionMergePending() &&
!mMergeHost.isCallSessionMergePending();
if (!mMergeHost.isMultiparty()) {
// Only check the transient session when there is no existing conference
areMergeTriggersDone &= isSessionAlive(mMergeHost.mTransientConferenceSession);
} else {
// This else block is a special case for Verizon to handle these steps
// 1. Establish a conference call.
// 2. Add a new call (conference in in BG)
// 3. Swap (conference active on FG)
// 4. Merge
// What happens here is that the BG call gets a terminated callback
// because it was added to the conference. I've seen where
// the FG gets no callback at all because its already active.
// So if we continue to wait for it to set its isCallSessionMerging
// flag to false...we'll be waiting forever.
areMergeTriggersDone = !isCallSessionMergePending();
}
} else {
// Realistically this shouldn't happen, but best to be safe.
loge("shouldProcessConferenceResult : merge in progress but call is neither" +
"host nor peer.");
}
if (VDBG) {
log("shouldProcessConferenceResult :: returning:" +
(areMergeTriggersDone ? "true" : "false"));
}
}
return areMergeTriggersDone;
|
public void | start(com.android.ims.internal.ImsCallSession session, java.lang.String callee)Initiates an IMS call with the call profile which is provided
when creating a {@link ImsCall}.
if (DBG) {
log("start(1) :: session=" + session + ", callee=" + callee);
}
synchronized(mLockObj) {
mSession = session;
try {
session.setListener(createCallSessionListener());
session.start(callee, mCallProfile);
} catch (Throwable t) {
loge("start(1) :: ", t);
throw new ImsException("start(1)", t, 0);
}
}
|
public void | start(com.android.ims.internal.ImsCallSession session, java.lang.String[] participants)Initiates an IMS conferenca call with the call profile which is provided
when creating a {@link ImsCall}.
if (DBG) {
log("start(n) :: session=" + session + ", callee=" + participants);
}
synchronized(mLockObj) {
mSession = session;
try {
session.setListener(createCallSessionListener());
session.start(participants, mCallProfile);
} catch (Throwable t) {
loge("start(n) :: ", t);
throw new ImsException("start(n)", t, 0);
}
}
|
public void | startDtmf(char c)Start a DTMF code. According to RFC 2833,
event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
and event flash to 16. Currently, event flash is not supported.
if (DBG) {
log("startDtmf :: session=" + mSession + ", code=" + c);
}
synchronized(mLockObj) {
if (mSession != null) {
mSession.startDtmf(c);
}
}
|
public void | stopDtmf()Stop a DTMF code.
if (DBG) {
log("stopDtmf :: session=" + mSession);
}
synchronized(mLockObj) {
if (mSession != null) {
mSession.stopDtmf();
}
}
|
public void | terminate(int reason)Terminates an IMS call.
if (VDBG) {
log("terminate :: ImsCall=" + this +" reason=" + reason);
}
synchronized(mLockObj) {
mHold = false;
mInCall = false;
if (mSession != null) {
// TODO: Fix the fact that user invoked call terminations during
// the process of establishing a conference call needs to be handled
// as a special case.
// Currently, any terminations (both invoked by the user or
// by the network results in a callSessionTerminated() callback
// from the network. When establishing a conference call we bury
// these callbacks until we get closure on all participants of the
// conference. In some situations, we will throw away the callback
// (when the underlying session of the host of the new conference
// is terminated) or will will unbury it when the conference has been
// established, like when the peer of the new conference goes away
// after the conference has been created. The UI relies on the callback
// to reflect the fact that the call is gone.
// So if a user decides to terminated a call while it is merging, it
// could take a long time to reflect in the UI due to the conference
// processing but we should probably cancel that and just terminate
// the call immediately and clean up. This is not a huge issue right
// now because we have not seen instances where establishing a
// conference takes a long time (more than a second or two).
mSession.terminate(reason);
}
}
|
private void | throwImsException(java.lang.Throwable t, int code)
if (t instanceof ImsException) {
throw (ImsException) t;
} else {
throw new ImsException(String.valueOf(code), t, code);
}
|
public java.lang.String | toString()Provides a string representation of the {@link ImsCall}. Primarily intended for use in log
statements.
StringBuilder sb = new StringBuilder();
sb.append("[ImsCall objId:");
sb.append(System.identityHashCode(this));
sb.append(" onHold:");
sb.append(isOnHold() ? "Y" : "N");
sb.append(" mute:");
sb.append(isMuted() ? "Y" : "N");
sb.append(" updateRequest:");
sb.append(updateRequestToString(mUpdateRequest));
sb.append(" merging:");
sb.append(isMerging() ? "Y" : "N");
if (isMerging()) {
if (isMergePeer()) {
sb.append("P");
} else {
sb.append("H");
}
}
sb.append(" merge action pending:");
sb.append(isCallSessionMergePending() ? "Y" : "N");
sb.append(" merged:");
sb.append(isMerged() ? "Y" : "N");
sb.append(" multiParty:");
sb.append(isMultiparty() ? "Y" : "N");
sb.append(" buried term:");
sb.append(mSessionEndDuringMerge ? "Y" : "N");
sb.append(" session:");
sb.append(mSession);
sb.append(" transientSession:");
sb.append(mTransientConferenceSession);
sb.append("]");
return sb.toString();
|
private void | tryProcessConferenceResult()This function will determine if there is a pending conference and if
we are ready to finalize processing it.
if (shouldProcessConferenceResult()) {
if (isMergeHost()) {
processMergeComplete();
} else if (mMergeHost != null) {
mMergeHost.processMergeComplete();
} else {
// There must be a merge host at this point.
loge("tryProcessConferenceResult :: No merge host for this conference!");
}
}
|
public void | update(int callType, ImsStreamMediaProfile mediaProfile)Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
if (VDBG) {
log("update ::");
}
if (isOnHold()) {
if (DBG) {
log("update :: call is on hold");
}
throw new ImsException("Not in a call to update call",
ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
}
synchronized(mLockObj) {
if (mUpdateRequest != UPDATE_NONE) {
if (DBG) {
log("update :: update is in progress; request=" +
updateRequestToString(mUpdateRequest));
}
throw new ImsException("Call update is in progress",
ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
}
if (mSession == null) {
loge("update :: ");
throw new ImsException("No call session",
ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED);
}
mSession.update(callType, mediaProfile);
mUpdateRequest = UPDATE_UNSPECIFIED;
}
|
private java.lang.String | updateRequestToString(int updateRequest)Provides a human-readable string representation of an update request.
switch (updateRequest) {
case UPDATE_NONE:
return "NONE";
case UPDATE_HOLD:
return "HOLD";
case UPDATE_HOLD_MERGE:
return "HOLD_MERGE";
case UPDATE_RESUME:
return "RESUME";
case UPDATE_MERGE:
return "MERGE";
case UPDATE_EXTEND_TO_CONFERENCE:
return "EXTEND_TO_CONFERENCE";
case UPDATE_UNSPECIFIED:
return "UNSPECIFIED";
default:
return "UNKNOWN";
}
|