Call Transfers and Call Forwarding¶
This article describes the call transfer and user initiated call forwarding features of the mobile SDK.
Overview¶
The SDK supports the following types of call redirection:
- Blind transfer (sometimes referred to as unattended transfer or cold transfer) - the party initiating the transfer (the "Transferor") sends the call directly to the third party (the "Target") without waiting to establish a session with the Target first.
- Attended transfer (sometimes referred to as consultation transfer) - party initiating the transfer (the "Transferor") speaks with the third party (the "Target") before completing the transfer from the original call party (the "Transferee").
- Call forwarding - the party initiating the forwarding sends the call directly to the third party while the original call is still ringing.
There are two ways to perform and manage redirection:
- Direct usage of libSoftphone APIs - suitable for simple call redirection scenarios or for scenarios where the developer wants to have full control over the redirection process.
- CallRedirectionManager - a higher level API that simplifies the call redirection management.
The direct APIs¶
The Swift SoftphoneBridge and Java Instance offer the following methods for call redirection:
transfer
Performs a blind transfer of an Established call to the specified target.
attendedTransfer
Performs an attended transfer of an Established call to another Established call.
forward
Forwards an IncomingTrying, IncomingRinging or IncomingIgnored call to the specified target.
acceptOfferedTransfer
Accepts an offered transfer.
rejectOfferedTransfer
Rejects an offered transfer.
The following callbacks are exposed in Swift SoftphoneObserverProxy and Java Listeners classes are available to monitor the progress of the call redirection:
onTransferResult
Reports the result of blind and attended transfers.
onTransferOffered
Informs about an incoming transfer offer. The offer can be accepted or rejected via acceptOfferedTransfer and rejectOfferedTransfer.
A successful call forward is reported via transition of call into IncomingForwarded state which is reported in onCallStateChanged callback.
Using CallRedirectionManager¶
The CallRedirectionManager is a higher level API that simplifies the call redirection management. It is designed in order to lead the developer through the call redirection process and to provide a simple way to implement the call redirection UI by providing a set of unified functions, callbacks and unified state.
The CallRedirectionManager is a singleton object which is accessible both in Swift and Java.
The CallRedirectionManager instance can be retrieved in Swift as follows:
And in Java:
CallRedirectionManager state¶
CallRedirectionManager tracks the redirection state and provides a set of functions to manage the redirection. The redirection state is represented by the RedirectState enum.
The state set description follows:
- Inactive - no redirection is in progress
- SourceAssigned - the redirect source call is assigned, the GUI should direct the user to select the target call by navigating to the dialpad or contact list
- TargetAssigned - the target call is assigned and attended transfer can be performed; this state only occurs in case of attended transfer
- InProgress - both redirect source and target have been assigned. In case of transfers, the REFER will be sent. In case of call forwarding, a 302 response will be sent as response to the INVITE. At this point it is not possible to change the source nor the target. It is not possible to cancel the redirection nor change the redirection type.
- Succeeded - the redirection succeeded
- Failed - the redirection failed
- Cancelled - the redirection was cancelled, e.g. by user, or by some unexpected event, such as one of the calls was disconnected.
The redirection is tracked from the moment the source call is assigned until the source or target call objects are closed. This means the GUI code can retrieve the state, type, source and target call objects even after the redirect has finished or has been cancelled.
The state can be tracked by registering a listener, here's a Swift example:
class SomeClassName: CallRedirectionStateChangeDelegate
{
func addRedirectionDelegate()
{
CallRedirectionManager.instance().addStateChangeDelegate(self)
}
func redirectStateChanged(data: StateChangeData)
{
if data.type.isAttendedTransfer() && data.newState.isTargetAssigned()
{
print("Ready to complete attended transfer")
}
else if data.newState.isInProgress()
{
print("Redirection is in progress")
}
else if data.newState.isSucceeded() || data.newState.isFailed() || data.newState.isCancelled()
{
print("Redirection finished")
}
}
}
And in Java:
CallRedirectionManager callRedirectionManager = SDKServices.get(CallRedirectionManager.class);
Disposable redirectionStateDisposable = callRedirectionManager.registerOnStateChangedCallback((type, state) -> {
if (type != RedirectType.AttendedTransfer)
return;
switch (state)
{
case TargetAssigned:
// show the Complete Transfer action
break;
case InProgress:
// show a progress indicator; it is no longer possible to cancel
break;
case Succeeded:
case Failed:
case Cancelled:
// leave transfer mode and update the call UI
break;
default:
break;
}
});
You can also retrieve the current active redirection type by invoking getCurrentRedirectFlow and the current state by invoking getState.
API overview¶
The breakdown of the most important redirection manager functions follows:
getCurrentRedirectFlow
Returns the current redirection flow or None if no redirection is currently tracked.
getState
Returns the current redirection state.
getRedirectSource
Returns the source call object.
getRedirectTarget
Returns the target call object (only available in attended transfer).
cancelRedirect
Cancels any tracked redirection flow. Cancel has no effect if redirect state is InProgress.
canInitiateRedirect
Checks if the redirect manager is ready to perform a transfer or forward.
getRedirectCapabilities
Returns the transfer and forward capabilities of the supplied call.
setBlindTransferSource
Sets the source call for blind transfer. The source call must be in Established state.
setAttendedTransferSource
Sets the source call for attended transfer. The source call must be in Established state.
setForwardingSource
Sets the source call for call forwarding. The source call must be in IncomingTrying, IncomingRinging or IncomingIgnored state.
setAttendedTransferTarget
Sets the target call for attended transfer. The target call must be in Established state.
performForwardToTarget
Performs a call forward to the specified target. The forwarding source call must be set setForwardingSource before calling this function and the state must be SourceAssigned.
performBlindTransferToTarget
Performs a blind transfer to the specified target. The blind transfer source call must be set with setBlindTransferSource before calling this function and the state must be SourceAssigned.
performAttendedTransfer
Performs an attended transfer between the source previously set via setAttendedTransferSource and the target previously set via setAttendedTransferTarget. The state must be TargetAssigned.
performAttendedTransferBetween
Performs an attended transfer between the supplied source and target. This can be invoked directly without setting the source and target calls. The state must not be InProgress.
Blind transfer flow¶
The typical flow of blind transfer is as follows:
- A call is established.
- GUI checks the redirection capabilities by calling
getRedirectCapabilitiesfor the current call, and enables the transfer button if blind transfer is available. - The user clicks on the transfer button on the call screen.
- GUI invokes
setBlindTransferSourcefor the current call. - GUI gets notified via its registered
StateChangeCallbackabout a transition toRedirectState::SourceAssigned. - The GUI updates the UI to allow the user to enter the number/contact to transfer to. The user should be able to cancel the transfer via a button, if the cancel button is clicked,
cancelRedirectis called. - The user dials a number or selects a contact to transfer to.
- GUI creates and posts a CallEvent for the call to be transferred to, as if it were a regular outgoing call.
- GUI gets notified via its registered
StateChangeCallbackabout a transition toRedirectState::InProgress. - GUI updates the UI to show that the transfer is in progress, at this point it is no longer possible to cancel the transfer.
- Based on the result of the transfer, GUI gets notified via its registered
StateChangeCallbackabout a transition toRedirectState::SucceededorRedirectState::FailedorRedirectState::Cancelled. - GUI updates its layout to show the result of the transfer.
- The original call is disconnected. The GUI should close the call object. When the call object is closed, the state transitions back to
RedirectState::Inactivestate.
Attended transfer flow overview¶
Always start an attended transfer by calling getRedirectCapabilities for the currently selected established call. Do not decide the flow only from the app's call count; use the AttendedTransferCapability value and the attendedTransferTargets list returned by the manager.
| Capability | Starting situation | GUI flow | Completion API |
|---|---|---|---|
NewCall |
Only the source call is established. | Set the source call, send the user to the dialpad or contact picker, start a normal outgoing consultation call, then complete after the new call is established. | performAttendedTransfer after the manager reaches TargetAssigned. |
Direct |
The source call and exactly one valid target call are already established. | No picker is needed; complete the transfer between the source and the single target. | performAttendedTransferBetween(source, target). |
PickAnotherCall |
The source call and multiple valid target calls are already established. | Set the source call, show only attendedTransferTargets, let the user choose a target, then complete. |
setAttendedTransferTarget(chosenTarget) followed by performAttendedTransfer. |
AttendedTransferCapability can also be NotAvailable, in which case attended transfer should not be offered for the call. This happens, for example, when the source call is already part of a conference of three or more participants. Always drive the GUI from the capability value rather than assuming attended transfer is available whenever multiple calls are established.
Attended transfer flow - multiple established calls¶
The simplest possible scenario of attended transfer starts with two established calls:
- Two calls are established.
- GUI checks the redirection capabilities by invoking
getRedirectCapabilitiesfor the current call, and enables the attended transfer button if attended transfer is available. - The user clicks on the attended transfer button on the call screen.
- The capabilities are queried again via
getRedirectCapabilities, theAttendedTransferCapabilityfield of theRedirectCapabilitiesobject is checked to see if attended transfer is still available. - As two calls are currently established, the
AttendedTransferCapabilityreturns the valueDirect. - The transfer target call is retrieved from
RedirectCapabilities::attendedTransferTargetsarray, and GUI callsperformAttendedTransferBetweenwith the two calls as parameters.
The case with three or more established calls is similar to the case with two calls. The AttendedTransferCapability shows the
type PickAnotherCall (unless the source call already belongs to a conference of three or more participants, in which case it is NotAvailable), and the GUI needs to present the user with a list of calls to choose from. The GUI can use the RedirectCapabilities::attendedTransferTargets array to get the list of calls that can be used as transfer targets.
For picker-based UI flows, the GUI can invoke setAttendedTransferSource for the selected source call, show the user the attendedTransferTargets list, then invoke setAttendedTransferTarget with the selected target and performAttendedTransfer. For a compact implementation, performAttendedTransferBetween with the source and selected target calls is also valid.
Attended transfer flow - one established call¶
The most complex attended transfer flow occurs when only one call is established:
- A call is established.
- GUI checks the redirection capabilities by invoking
getRedirectCapabilitiesfor the current call, and enables the attended transfer button if attended transfer is available. - The user clicks on the attended transfer button on the call screen.
- The capabilities are queried again via
getRedirectCapabilities, theAttendedTransferCapabilityfield of theRedirectCapabilitiesreturnsNewCall. - GUI invokes
setAttendedTransferSourcefor the current call. - GUI gets notified via its registered
StateChangeCallbackabout a transition toRedirectState::SourceAssigned. - The GUI updates the UI to allow the user to enter the number/contact to transfer to. The user should be able to cancel the transfer via a button, if the cancel button is clicked,
cancelRedirectis invoked. - The user dials a number or selects a contact to transfer to.
- GUI creates and posts a CallEvent for the target call, as if it were a regular outgoing call. In other words, start the consultation call the same way the app normally starts an outgoing call.
- Once the call gets into
Call::State::Establishedstate, the redirection manager callssetAttendedTransferTargeton the new call automatically. - GUI gets notified via its registered
StateChangeCallbackabout a transition toRedirectState::TargetAssigned. - GUI should display the new call and should allow the user to switch between the calls, merge them into a conference, or put them on hold etc.
- User clicks on the attended transfer button again.
- GUI invokes
performAttendedTransfer. - GUI gets notified via its registered
StateChangeCallbackabout a transition toRedirectState::InProgress. - Based on the result of the transfer, GUI gets notified about a state transition to
RedirectState::SucceededorRedirectState::FailedorRedirectState::Cancelled - GUI updates its layout according to the result of the forward.
- The source and target calls are disconnected. GUI should close the call object. When the call object is closed, the state transitions back to
RedirectState::Inactivestate.
Transfer confirmation troubleshooting¶
Some PBXs do not send the expected transfer-confirmation NOTIFY after a REFER, even though the transfer itself succeeds. If the app reports transfer failure while SIP logs show the PBX accepted the REFER, check the waitForTransferConfirmation account setting.
When waitForTransferConfirmation is disabled, a 2xx response to REFER is treated as transfer success.
Remote party updates after transfer¶
Some PBXs update the displayed remote party after a transfer by sending an in-dialog SIP UPDATE with a caller identity header, such as P-Asserted-Identity. The SDK can use this information to update the call event's remote user, for example when a blind transfer changes who the local user is now connected to.
This behavior is enabled by default through the enableChangingRemoteContactViaUpdateRequest account setting.
Applications should refresh the visible caller name and number from the CallEvent supplied by onCallStateChanged, even when the reported call state value remains Established. Treat a call-state callback as a signal that call metadata may have changed and re-read the call's remote user before updating the call UI.