From: Anthony Lee Date: Mon, 3 Nov 2014 23:13:47 +0000 (-0800) Subject: Complete implementation of ImsCallSession merge() X-Git-Url: https://nv-tegra.nvidia.com/r/gitweb?p=android%2Fplatform%2Fframeworks%2Fopt%2Fnet%2Fims.git;a=commitdiff_plain;h=b8799fe85b9387999f03ce7def6ace59903984a4 Complete implementation of ImsCallSession merge() This CL implements the rest of the logic around the various callbacks that are issued from the ImsCallSession to implement the merging of two calls into a conference. Bug: 18056632 Change-Id: I183a3b2c49d4192e14813333339440d746febdbf --- diff --git a/src/java/com/android/ims/ImsCall.java b/src/java/com/android/ims/ImsCall.java index 9f58f0a..3c4484f 100644 --- a/src/java/com/android/ims/ImsCall.java +++ b/src/java/com/android/ims/ImsCall.java @@ -410,7 +410,19 @@ public class ImsCall implements ICall { // Media session to control media (audio/video) operations for an IMS call private ImsStreamMediaSession mMediaSession = null; + // The temporary ImsCallSession that could represent the merged call once + // we receive notification that the merge was successful. private ImsCallSession mTransientConferenceSession = null; + // While a merge is progressing, we bury any session termination requests + // made on the original ImsCallSession until we have closure on the merge request + // If the request ultimately fails, we need to act on the termination request + // that we buried temporarily. We do this because we feel that timing issues could + // cause the termination request to occur just because the merge is succeeding. + private boolean mSessionEndDuringMerge = false; + // Just like mSessionEndDuringMerge, we need to keep track of the reason why the + // termination request was made on the original session in case we need to act + // on it in the case of a merge failure. + private ImsReasonInfo mSessionEndDuringMergeReasonInfo = null; /** * Create an IMS call object. @@ -1078,7 +1090,17 @@ public class ImsCall implements ICall { if (mHold || (mContext.getResources().getBoolean( com.android.internal.R.bool.skipHoldBeforeMerge))) { mSession.merge(); - mUpdateRequest = UPDATE_MERGE; + + // Check to see if there is an owner to a valid call group. If this is the + // case, then we already have a conference call. + if (mCallGroup != null && mCallGroup.getOwner() == null) { + // 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. + mUpdateRequest = UPDATE_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 @@ -1614,6 +1636,59 @@ public class ImsCall implements ICall { } } + /** + * 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. + * + * @param reasonInfo The reason for the session termination + */ + private void processCallTerminated(ImsReasonInfo reasonInfo) { + if (DBG) { + String sessionString = mSession != null ? mSession.toString() : "null"; + String transientSessionString = mTransientConferenceSession != null ? + mTransientConferenceSession.toString() : "null"; + String reasonString = reasonInfo != null ? reasonInfo.toString() : "null"; + log("processCallTerminated :: session=" + sessionString + " transientSession=" + + transientSessionString + " reason=" + reasonString); + } + + ImsCall.Listener listener = null; + + synchronized(ImsCall.this) { + if (mUpdateRequest == UPDATE_MERGE) { + // 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 (mCallGroup != null) { + notifyConferenceSessionTerminated(reasonInfo); + } else { + listener = mListener; + clear(reasonInfo); + } + } + + if (listener != null) { + try { + listener.onCallTerminated(ImsCall.this, reasonInfo); + } catch (Throwable t) { + loge("callSessionTerminated :: ", t); + } + } + } /** * This function determines if the ImsCallSession is our actual ImsCallSession or if is @@ -1635,31 +1710,49 @@ public class ImsCall implements ICall { /** * We received a callback from ImsCallSession that a merge was complete. Clean up all - * internal state to represent this state change. + * internal state to represent this state change. This function will be called when + * the transient conference session goes active or we get an explicit merge complete + * callback on the transient session. * - * @param session The {@link ImsCallSession} that is the host of this conference. */ - private void processMergeComplete(ImsCallSession session) { - // If mTransientConferenceSession is set, this means that this is a newly created - // conference. Otherwise, we are adding to an existing conference. + private void processMergeComplete() { + if (DBG) { + String sessionString = mSession != null ? mSession.toString() : "null"; + String transientSessionString = mTransientConferenceSession != null ? + mTransientConferenceSession.toString() : "null"; + log("processMergeComplete :: session=" + sessionString + " transientSession=" + + transientSessionString); + } + ImsCall.Listener listener; synchronized(ImsCall.this) { listener = mListener; - // TODO: See if we can use the session param to do some further checks here. if (mTransientConferenceSession != null) { - // We need to replace our ImsCallSession with this new one after adjusting - // the listeners on the session. + // Swap out the underlying sessions after shutting down the existing session. mSession.setListener(null); - mTransientConferenceSession.setListener(createCallSessionListener()); mSession = mTransientConferenceSession; - } + // We need to set ourselves as the owner of the call group to indicate that + // a conference call is in progress. + mCallGroup.setOwner(ImsCall.this); + listener = mListener; + } else { + // This is an interesting state that needs to be logged since we + // should only be going through this workflow for new conference calls + // and not merges into existing conferences (which a null transient + // session would imply) + log("processMergeComplete :: ERROR no transient session"); + } + // Clear some flags. If the merge eventually worked, we can safely + // ignore the call terminated message for the old session since we closed it already. + mSessionEndDuringMerge = false; + mSessionEndDuringMergeReasonInfo = null; mUpdateRequest = UPDATE_NONE; } if (listener != null) { try { listener.onCallMerged(ImsCall.this); } catch (Throwable t) { - loge("callSessionMergeStarted :: ", t); + loge("processMergeComplete :: ", t); } } @@ -1670,28 +1763,59 @@ public class ImsCall implements ICall { * We received a callback from ImsCallSession that a merge failed. Clean up all * internal state to represent this state change. * - * @param session The {@link ImsCallSession} that is the host of this conference. * @param reasonInfo The {@link ImsReasonInfo} why the merge failed. */ - private void processMergeFailed(ImsCallSession session, ImsReasonInfo reasonInfo) { - // If mTransientConferenceSession is set, this means that this is a newly created - // conference. Otherwise, we are adding to an existing conference. + private void processMergeFailed(ImsReasonInfo reasonInfo) { + if (DBG) { + String sessionString = mSession != null ? mSession.toString() : "null"; + String transientSessionString = mTransientConferenceSession != null ? + mTransientConferenceSession.toString() : "null"; + String reasonString = reasonInfo != null ? reasonInfo.toString() : "null"; + log("processMergeFailed :: session=" + sessionString + " transientSession=" + + transientSessionString + " reason=" + reasonString); + } + ImsCall.Listener listener; + boolean notifyFailure = false; + ImsReasonInfo notifyFailureReasonInfo = null; + synchronized(ImsCall.this) { listener = mListener; - // TODO: See if we can use the session param to do some further checks here. if (mTransientConferenceSession != null) { // Clean up any work that we performed on the transient session. mTransientConferenceSession.setListener(null); mTransientConferenceSession = null; - } + 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 succesfully. + if (DBG) { + log("processMergeFailed :: following up on a terminate during the merge"); + } + notifyFailure = true; + notifyFailureReasonInfo = mSessionEndDuringMergeReasonInfo; + } + } else { + // This is an interesting state that needs to be logged since we + // should only be going through this workflow for new conference calls + // and not merges into existing conferences (which a null transient + // session would imply) + log("processMergeFailed - ERROR no transient session"); + } + mSessionEndDuringMerge = false; + mSessionEndDuringMergeReasonInfo = null; mUpdateRequest = UPDATE_NONE; } if (listener != null) { try { + // TODO: are both of these callbacks necessary? listener.onCallMergeFailed(ImsCall.this, reasonInfo); + if (notifyFailure) { + processCallTerminated(notifyFailureReasonInfo); + } } catch (Throwable t) { - loge("callSessionMergeFailed :: ", t); + loge("processMergeFailed :: ", t); } } return; @@ -1724,7 +1848,8 @@ public class ImsCall implements ICall { @Override public void callSessionProgressing(ImsCallSession session, ImsStreamMediaProfile profile) { if (isTransientConferenceSession(session)) { - log("callSessionProgressing :: not supported for conference session=" + session); + log("callSessionProgressing :: not supported for transient conference session=" + + session); return; } @@ -1750,15 +1875,20 @@ public class ImsCall implements ICall { @Override public void callSessionStarted(ImsCallSession session, ImsCallProfile profile) { - if (isTransientConferenceSession(session)) { - log("callSessionStarted :: not supported for conference session=" + session); - return; - } - if (DBG) { log("callSessionStarted :: session=" + session + ", profile=" + profile); } + if (isTransientConferenceSession(session)) { + log("callSessionStarted :: transient conference session resumed session=" + + session); + // If we get a resume on the transient session, this means that the merge + // was completed, let's process it are skip the rest of the processing in + // this callback. + processMergeComplete(); + return; + } + ImsCall.Listener listener; synchronized(ImsCall.this) { @@ -1778,7 +1908,8 @@ public class ImsCall implements ICall { @Override public void callSessionStartFailed(ImsCallSession session, ImsReasonInfo reasonInfo) { if (isTransientConferenceSession(session)) { - log("callSessionStartFailed :: not supported for conference session=" + session); + log("callSessionStartFailed :: not supported for transient conference session=" + + session); return; } @@ -1806,64 +1937,22 @@ public class ImsCall implements ICall { @Override public void callSessionTerminated(ImsCallSession session, ImsReasonInfo reasonInfo) { if (isTransientConferenceSession(session)) { - log("callSessionTerminated :: not supported for conference session=" + session); + log("callSessionTerminated :: not supported for transient conference session=" + + session); return; } if (DBG) { - log("callSessionTerminated :: session=" + session + - ", reasonInfo=" + reasonInfo); + log("callSessionTerminated :: session=" + session + ", reasonInfo=" + reasonInfo); } - ImsCall.Listener listener = null; - - synchronized(ImsCall.this) { - // Let's explicitly handle this situation here... - // The ImsCallSession that has just notified that it was terminated could be in the - // state where it is waiting for a merge to happen. If this is the case, let's just - // assume that the merge is going to succeed but the callbacks are being done in the - // wrong order. We would expect callSessionMergeComplete() to be called on the - // transient session first and then this call to the original merge() "host". - // If the timing is off, we could be getting this call first. - if (mUpdateRequest == UPDATE_MERGE) { - if (mTransientConferenceSession != null) { - log("callSessionTerminated() :: called while waiting for a new conference"); - // Let's assume that the merge will complete and the transient session will, - // in fact, be our new session soon enough. - mSession.setListener(null); - mTransientConferenceSession.setListener(createCallSessionListener()); - mSession = mTransientConferenceSession; - // At this point, we'll handle this conference call like it was a merge() - // on an existing conference as opposed to creating a new conference. All - // properly callbacks should be made. - - // TODO: Do we need to handle the case where the new ImsCallSession still - // fails to merge after this? Should be a very unlikely scenario. - return; - } - } - - if (mCallGroup != null) { - notifyConferenceSessionTerminated(reasonInfo); - } else { - listener = mListener; - clear(reasonInfo); - } - } - - if (listener != null) { - try { - listener.onCallTerminated(ImsCall.this, reasonInfo); - } catch (Throwable t) { - loge("callSessionTerminated :: ", t); - } - } + processCallTerminated(reasonInfo); } @Override public void callSessionHeld(ImsCallSession session, ImsCallProfile profile) { if (isTransientConferenceSession(session)) { - log("callSessionHeld :: not supported for conference session=" + session); + log("callSessionHeld :: not supported for transient conference session=" + session); return; } @@ -1901,7 +1990,8 @@ public class ImsCall implements ICall { @Override public void callSessionHoldFailed(ImsCallSession session, ImsReasonInfo reasonInfo) { if (isTransientConferenceSession(session)) { - log("callSessionHoldFailed :: not supported for conference session=" + session); + log("callSessionHoldFailed :: not supported for transient conference session=" + + session); return; } @@ -1943,7 +2033,8 @@ public class ImsCall implements ICall { @Override public void callSessionHoldReceived(ImsCallSession session, ImsCallProfile profile) { if (isTransientConferenceSession(session)) { - log("callSessionHoldReceived :: not supported for conference session=" + session); + log("callSessionHoldReceived :: not supported for transient conference session=" + + session); return; } @@ -1970,7 +2061,8 @@ public class ImsCall implements ICall { @Override public void callSessionResumed(ImsCallSession session, ImsCallProfile profile) { if (isTransientConferenceSession(session)) { - log("callSessionResumed :: not supported for conference session=" + session); + log("callSessionResumed :: not supported for transient conference session=" + + session); return; } @@ -2002,7 +2094,8 @@ public class ImsCall implements ICall { @Override public void callSessionResumeFailed(ImsCallSession session, ImsReasonInfo reasonInfo) { if (isTransientConferenceSession(session)) { - log("callSessionResumeFailed :: not supported for conference session=" + session); + log("callSessionResumeFailed :: not supported for transient conference session=" + + session); return; } @@ -2030,7 +2123,8 @@ public class ImsCall implements ICall { @Override public void callSessionResumeReceived(ImsCallSession session, ImsCallProfile profile) { if (isTransientConferenceSession(session)) { - log("callSessionResumeReceived :: not supported for conference session=" + session); + log("callSessionResumeReceived :: not supported for transient conference session=" + + session); return; } @@ -2059,7 +2153,7 @@ public class ImsCall implements ICall { public void callSessionMergeStarted(ImsCallSession session, ImsCallSession newSession, ImsCallProfile profile) { if (DBG) { - String sessionString = newSession == null ? "null" : newSession.toString(); + String sessionString = session == null ? "null" : session.toString(); String newSessionString = newSession == null ? "null" : newSession.toString(); log("callSessionMergeStarted :: session=" + sessionString + ", newSession=" + newSessionString + ", profile=" + profile); @@ -2101,11 +2195,6 @@ public class ImsCall implements ICall { mTransientConferenceSession.setListener(createCallSessionListener()); } - // STOPSHIP: For now, let's force the complete callback since it is not coming - // from the vendor layer as of when this code was written. - log("callSessionMergeStarted :: forcing a success callback"); - callSessionMergeComplete(mTransientConferenceSession); - return; } @@ -2124,7 +2213,7 @@ public class ImsCall implements ICall { } // Let's let our parent ImsCall now that we received notification that // the merge was completed so we can set up our internal state properly - processMergeComplete(session); + processMergeComplete(); } @Override @@ -2144,13 +2233,14 @@ public class ImsCall implements ICall { } // Let's tell our parent ImsCall that the merge has failed and we need to clean // up any temporary, transient state. - processMergeFailed(session, reasonInfo); + processMergeFailed(reasonInfo); } @Override public void callSessionUpdated(ImsCallSession session, ImsCallProfile profile) { if (isTransientConferenceSession(session)) { - log("callSessionUpdated :: not supported for conference session=" + session); + log("callSessionUpdated :: not supported for transient conference session=" + + session); return; } @@ -2178,7 +2268,8 @@ public class ImsCall implements ICall { @Override public void callSessionUpdateFailed(ImsCallSession session, ImsReasonInfo reasonInfo) { if (isTransientConferenceSession(session)) { - log("callSessionUpdateFailed :: not supported for conference session=" + session); + log("callSessionUpdateFailed :: not supported for transient conference session=" + + session); return; } @@ -2206,7 +2297,8 @@ public class ImsCall implements ICall { @Override public void callSessionUpdateReceived(ImsCallSession session, ImsCallProfile profile) { if (isTransientConferenceSession(session)) { - log("callSessionUpdateReceived :: not supported for conference session=" + session); + log("callSessionUpdateReceived :: not supported for transient conference " + + "session=" + session); return; } @@ -2236,8 +2328,8 @@ public class ImsCall implements ICall { public void callSessionConferenceExtended(ImsCallSession session, ImsCallSession newSession, ImsCallProfile profile) { if (isTransientConferenceSession(session)) { - log("callSessionConferenceExtended :: not supported for conference session=" + - session); + log("callSessionConferenceExtended :: not supported for transient conference " + + "session=" + session); return; } @@ -2273,8 +2365,8 @@ public class ImsCall implements ICall { public void callSessionConferenceExtendFailed(ImsCallSession session, ImsReasonInfo reasonInfo) { if (isTransientConferenceSession(session)) { - log("callSessionConferenceExtendFailed :: not supported for conference session=" + - session); + log("callSessionConferenceExtendFailed :: not supported for transient " + + "conference session=" + session); return; } @@ -2303,8 +2395,8 @@ public class ImsCall implements ICall { public void callSessionConferenceExtendReceived(ImsCallSession session, ImsCallSession newSession, ImsCallProfile profile) { if (isTransientConferenceSession(session)) { - log("callSessionConferenceExtendReceived :: not supported for conference session=" + - session); + log("callSessionConferenceExtendReceived :: not supported for transient " + + "conference session=" + session); return; } @@ -2451,8 +2543,8 @@ public class ImsCall implements ICall { public void callSessionConferenceStateUpdated(ImsCallSession session, ImsConferenceState state) { if (isTransientConferenceSession(session)) { - log("callSessionConferenceStateUpdated :: not supported for conference session=" + - session); + log("callSessionConferenceStateUpdated :: not supported for transient " + + "conference session=" + session); return; } @@ -2468,8 +2560,8 @@ public class ImsCall implements ICall { public void callSessionUssdMessageReceived(ImsCallSession session, int mode, String ussdMessage) { if (isTransientConferenceSession(session)) { - log("callSessionUssdMessageReceived :: not supported for conference session=" + - session); + log("callSessionUssdMessageReceived :: not supported for transient " + + "conference session=" + session); return; }