diff options
author | Ravi Paluri <quic_rpaluri@quicinc.com> | 2020-02-05 12:35:41 +0530 |
---|---|---|
committer | Ravi Paluri <quic_rpaluri@quicinc.com> | 2020-02-14 11:02:09 +0530 |
commit | f4b38e7ff15bef49e333dfb5a0eb788d65abe1ae (patch) | |
tree | 1a106d233c55f7b875f77c7781b38b0fee7fa09c | |
parent | 3819be4271be3085bc55d5e2665952dcc8a77991 (diff) |
IMS: Add support for IMS Explicit call transfer
Test: Manual
Bug: 62170207
Change-Id: I06a256adb0e1910d40809c91bcdd42c56a142842
13 files changed, 377 insertions, 3 deletions
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 52213d8c4fae..c5fcf67c9be9 100644..100755 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -465,8 +465,27 @@ public final class Call { * @hide */ public static final int CAPABILITY_ADD_PARTICIPANT = 0x02000000; + + /** + * When set for a call, indicates that this {@code Call} can be transferred to another + * number. + * Call supports the blind and assured call transfer feature. + * + * @hide + */ + public static final int CAPABILITY_TRANSFER = 0x04000000; + + /** + * When set for a call, indicates that this {@code Call} can be transferred to another + * ongoing call. + * Call supports the consultative call transfer feature. + * + * @hide + */ + public static final int CAPABILITY_TRANSFER_CONSULTATIVE = 0x08000000; + //****************************************************************************************** - // Next CAPABILITY value: 0x04000000 + // Next CAPABILITY value: 0x10000000 //****************************************************************************************** /** @@ -699,6 +718,12 @@ public final class Call { if (can(capabilities, CAPABILITY_ADD_PARTICIPANT)) { builder.append(" CAPABILITY_ADD_PARTICIPANT"); } + if (can(capabilities, CAPABILITY_TRANSFER)) { + builder.append(" CAPABILITY_TRANSFER"); + } + if (can(capabilities, CAPABILITY_TRANSFER_CONSULTATIVE)) { + builder.append(" CAPABILITY_TRANSFER_CONSULTATIVE"); + } builder.append("]"); return builder.toString(); } @@ -1564,6 +1589,30 @@ public final class Call { } /** + * Instructs this {@code Call} to be transferred to another number. + * + * @param targetNumber The address to which the call will be transferred. + * @param isConfirmationRequired if {@code true} it will initiate ASSURED transfer, + * if {@code false}, it will initiate BLIND transfer. + * + * @hide + */ + public void transfer(@NonNull Uri targetNumber, boolean isConfirmationRequired) { + mInCallAdapter.transferCall(mTelecomCallId, targetNumber, isConfirmationRequired); + } + + /** + * Instructs this {@code Call} to be transferred to another ongoing call. + * This will initiate CONSULTATIVE transfer. + * @param toCall The other ongoing {@code Call} to which this call will be transferred. + * + * @hide + */ + public void transfer(@NonNull android.telecom.Call toCall) { + mInCallAdapter.transferCall(mTelecomCallId, toCall.mTelecomCallId); + } + + /** * Instructs this {@code Call} to disconnect. */ public void disconnect() { diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 3b0ba2548660..3564add65113 100644..100755 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -387,8 +387,25 @@ public abstract class Connection extends Conferenceable { * @hide */ public static final int CAPABILITY_ADD_PARTICIPANT = 0x04000000; + + /** + * Indicates that this {@code Connection} can be transferred to another + * number. + * Connection supports the blind and assured call transfer feature. + * @hide + */ + public static final int CAPABILITY_TRANSFER = 0x08000000; + + /** + * Indicates that this {@code Connection} can be transferred to another + * ongoing {@code Connection}. + * Connection supports the consultative call transfer feature. + * @hide + */ + public static final int CAPABILITY_TRANSFER_CONSULTATIVE = 0x10000000; + //********************************************************************************************** - // Next CAPABILITY value: 0x08000000 + // Next CAPABILITY value: 0x20000000 //********************************************************************************************** /** @@ -967,6 +984,13 @@ public abstract class Connection extends Conferenceable { if ((capabilities & CAPABILITY_ADD_PARTICIPANT) == CAPABILITY_ADD_PARTICIPANT) { builder.append(isLong ? " CAPABILITY_ADD_PARTICIPANT" : " add_participant"); } + if ((capabilities & CAPABILITY_TRANSFER) == CAPABILITY_TRANSFER) { + builder.append(isLong ? " CAPABILITY_TRANSFER" : " sup_trans"); + } + if ((capabilities & CAPABILITY_TRANSFER_CONSULTATIVE) + == CAPABILITY_TRANSFER_CONSULTATIVE) { + builder.append(isLong ? " CAPABILITY_TRANSFER_CONSULTATIVE" : " sup_cTrans"); + } builder.append("]"); return builder.toString(); } @@ -3092,6 +3116,26 @@ public abstract class Connection extends Conferenceable { public void onReject(String replyMessage) {} /** + * Notifies this Connection, a request to transfer to a target number. + * @param number the number to transfer this {@link Connection} to. + * @param isConfirmationRequired when {@code true}, the {@link ConnectionService} + * should wait until the transfer has successfully completed before disconnecting + * the current {@link Connection}. + * When {@code false}, the {@link ConnectionService} should signal the network to + * perform the transfer, but should immediately disconnect the call regardless of + * the outcome of the transfer. + * @hide + */ + public void onTransfer(@NonNull Uri number, boolean isConfirmationRequired) {} + + /** + * Notifies this Connection, a request to transfer to another Connection. + * @param otherConnection the {@link Connection} to transfer this call to. + * @hide + */ + public void onTransfer(@NonNull Connection otherConnection) {} + + /** * Notifies this Connection of a request to silence the ringer. * <p> * The ringer may be silenced by any of the following methods: diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 2aea723cf418..0dca006f37c0 100644..100755 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -128,6 +128,8 @@ public abstract class ConnectionService extends Service { private static final String SESSION_ANSWER = "CS.an"; private static final String SESSION_ANSWER_VIDEO = "CS.anV"; private static final String SESSION_DEFLECT = "CS.def"; + private static final String SESSION_TRANSFER = "CS.trans"; + private static final String SESSION_CONSULTATIVE_TRANSFER = "CS.cTrans"; private static final String SESSION_REJECT = "CS.r"; private static final String SESSION_REJECT_MESSAGE = "CS.rWM"; private static final String SESSION_SILENCE = "CS.s"; @@ -196,6 +198,8 @@ public abstract class ConnectionService extends Service { private static final int MSG_CREATE_CONFERENCE_FAILED = 37; private static final int MSG_REJECT_WITH_REASON = 38; private static final int MSG_ADD_PARTICIPANT = 39; + private static final int MSG_EXPLICIT_CALL_TRANSFER = 40; + private static final int MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE = 41; private static Connection sNullConnection; @@ -481,6 +485,38 @@ public abstract class ConnectionService extends Service { } @Override + public void transfer(@NonNull String callId, @NonNull Uri number, + boolean isConfirmationRequired, Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_TRANSFER); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = number; + args.argi1 = isConfirmationRequired ? 1 : 0; + args.arg3 = Log.createSubsession(); + mHandler.obtainMessage(MSG_EXPLICIT_CALL_TRANSFER, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override + public void consultativeTransfer(@NonNull String callId, @NonNull String otherCallId, + Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_CONSULTATIVE_TRANSFER); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = otherCallId; + args.arg3 = Log.createSubsession(); + mHandler.obtainMessage( + MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override public void silence(String callId, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_SILENCE); try { @@ -1108,6 +1144,30 @@ public abstract class ConnectionService extends Service { } break; } + case MSG_EXPLICIT_CALL_TRANSFER: { + SomeArgs args = (SomeArgs) msg.obj; + Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_TRANSFER); + try { + final boolean isConfirmationRequired = args.argi1 == 1; + transfer((String) args.arg1, (Uri) args.arg2, isConfirmationRequired); + } finally { + args.recycle(); + Log.endSession(); + } + break; + } + case MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE: { + SomeArgs args = (SomeArgs) msg.obj; + Log.continueSession( + (Session) args.arg3, SESSION_HANDLER + SESSION_CONSULTATIVE_TRANSFER); + try { + consultativeTransfer((String) args.arg1, (String) args.arg2); + } finally { + args.recycle(); + Log.endSession(); + } + break; + } case MSG_DISCONNECT: { SomeArgs args = (SomeArgs) msg.obj; Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_DISCONNECT); @@ -2042,6 +2102,18 @@ public abstract class ConnectionService extends Service { findConnectionForAction(callId, "reject").onReject(rejectReason); } + private void transfer(String callId, Uri number, boolean isConfirmationRequired) { + Log.d(this, "transfer %s", callId); + findConnectionForAction(callId, "transfer").onTransfer(number, isConfirmationRequired); + } + + private void consultativeTransfer(String callId, String otherCallId) { + Log.d(this, "consultativeTransfer %s", callId); + Connection connection1 = findConnectionForAction(callId, "consultativeTransfer"); + Connection connection2 = findConnectionForAction(otherCallId, " consultativeTransfer"); + connection1.onTransfer(connection2); + } + private void silence(String callId) { Log.d(this, "silence %s", callId); findConnectionForAction(callId, "silence").onSilence(); diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java index 9d29174059ad..dd6c15311651 100644..100755 --- a/telecomm/java/android/telecom/InCallAdapter.java +++ b/telecomm/java/android/telecom/InCallAdapter.java @@ -16,6 +16,7 @@ package android.telecom; +import android.annotation.NonNull; import android.bluetooth.BluetoothDevice; import android.net.Uri; import android.os.Bundle; @@ -102,6 +103,35 @@ public final class InCallAdapter { } /** + * Instructs Telecom to transfer the specified call. + * + * @param callId The identifier of the call to transfer. + * @param targetNumber The address to transfer to. + * @param isConfirmationRequired if {@code true} it will initiate ASSURED transfer, + * if {@code false}, it will initiate BLIND transfer. + */ + public void transferCall(@NonNull String callId, @NonNull Uri targetNumber, + boolean isConfirmationRequired) { + try { + mAdapter.transferCall(callId, targetNumber, isConfirmationRequired); + } catch (RemoteException e) { + } + } + + /** + * Instructs Telecom to transfer the specified call to another ongoing call. + * + * @param callId The identifier of the call to transfer. + * @param otherCallId The identifier of the other call to which this will be transferred. + */ + public void transferCall(@NonNull String callId, @NonNull String otherCallId) { + try { + mAdapter.consultativeTransfer(callId, otherCallId); + } catch (RemoteException e) { + } + } + + /** * Instructs Telecom to disconnect the specified call. * * @param callId The identifier of the call to disconnect. diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl index a397d77db2f6..fb5417994b57 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl @@ -81,6 +81,11 @@ oneway interface IConnectionService { void rejectWithMessage(String callId, String message, in Session.Info sessionInfo); + void transfer(String callId, in Uri number, boolean isConfirmationRequired, + in Session.Info sessionInfo); + + void consultativeTransfer(String callId, String otherCallId, in Session.Info sessionInfo); + void disconnect(String callId, in Session.Info sessionInfo); void silence(String callId, in Session.Info sessionInfo); diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl index 9beff22ce52e..edf1cf4cdb18 100644..100755 --- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl @@ -36,6 +36,10 @@ oneway interface IInCallAdapter { void rejectCallWithReason(String callId, int rejectReason); + void transferCall(String callId, in Uri targetNumber, boolean isConfirmationRequired); + + void consultativeTransfer(String callId, String otherCallId); + void disconnectCall(String callId); void holdCall(String callId); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 05a29e9524e5..92057079e155 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1936,6 +1936,13 @@ public class CarrierConfigManager { "carrier_allow_deflect_ims_call_bool"; /** + * Flag indicating whether the carrier supports explicit call transfer for an IMS call. + * @hide + */ + public static final String KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL = + "carrier_allow_transfer_ims_call_bool"; + + /** * Flag indicating whether the carrier always wants to play an "on-hold" tone when a call has * been remotely held. * <p> @@ -3420,6 +3427,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_CARRIER_CONFIG_VERSION_STRING, ""); sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true); sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false); + sDefaults.putBoolean(KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL, false); sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false); sDefaults.putBoolean(KEY_AUTO_RETRY_FAILED_WIFI_EMERGENCY_CALL, false); sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true); diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java index 1b583fd29965..80c38cbfc39a 100644..100755 --- a/telephony/java/android/telephony/ims/ImsCallSession.java +++ b/telephony/java/android/telephony/ims/ImsCallSession.java @@ -16,6 +16,8 @@ package android.telephony.ims; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Message; import android.os.RemoteException; import android.telephony.CallQuality; @@ -451,6 +453,21 @@ public class ImsCallSession { } /** + * Received success response for call transfer request. + */ + public void callSessionTransferred(@NonNull ImsCallSession session) { + // no-op + } + + /** + * Received failure response for call transfer request. + */ + public void callSessionTransferFailed(@NonNull ImsCallSession session, + @Nullable ImsReasonInfo reasonInfo) { + // no-op + } + + /** * Called when the IMS service reports a change to the call quality. */ public void callQualityChanged(CallQuality callQuality) { @@ -795,6 +812,41 @@ public class ImsCallSession { } /** + * Transfers an ongoing call. + * + * @param number number to be transferred to. + * @param isConfirmationRequired indicates blind or assured transfer. + */ + public void transfer(@NonNull String number, boolean isConfirmationRequired) { + if (mClosed) { + return; + } + + try { + miSession.transfer(number, isConfirmationRequired); + } catch (RemoteException e) { + } + } + + /** + * Transfers a call to another ongoing call. + * + * @param transferToSession the other ImsCallSession to which this session will be transferred. + */ + public void transfer(@NonNull ImsCallSession transferToSession) { + if (mClosed) { + return; + } + + try { + if (transferToSession != null) { + miSession.consultativeTransfer(transferToSession.getSession()); + } + } catch (RemoteException e) { + } + } + + /** * Terminates a call. * * @see Listener#callSessionTerminated @@ -1410,6 +1462,20 @@ public class ImsCallSession { } } + @Override + public void callSessionTransferred() { + if (mListener != null) { + mListener.callSessionTransferred(ImsCallSession.this); + } + } + + @Override + public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo) { + if (mListener != null) { + mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo); + } + } + /** * Call quality updated */ diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl index cc2ebb9b20bd..36d2067ad016 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl @@ -148,6 +148,12 @@ oneway interface IImsCallSessionListener { void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile); /** + * Notifies the result of transfer request. + */ + void callSessionTransferred(); + void callSessionTransferFailed(in ImsReasonInfo reasonInfo); + + /** * Notifies of a change to the call quality. * @param callQuality then updated call quality */ diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java index 75bd6a7dc648..06aa6428b1b2 100644..100755 --- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java +++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java @@ -16,6 +16,8 @@ package android.telephony.ims.compat.stub; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Message; import android.os.RemoteException; @@ -197,6 +199,29 @@ public class ImsCallSessionImplBase extends IImsCallSession.Stub { } /** + * Transfer an established call to given number, disconnecting the ongoing call + * when the transfer is complete. + * + * @param number number to transfer the call + * @param isConfirmationRequired when {@code true}, then the {@link ImsCallSessionImplBase} + * should wait until the transfer has successfully completed before disconnecting the current + * {@link ImsCallSessionImplBase}. When {@code false}, the {@link ImsCallSessionImplBase} + * should signal the network to perform the transfer, but should immediately disconnect the + * call regardless of the outcome of the transfer. + */ + @Override + public void transfer(@NonNull String number, boolean isConfirmationRequired) { + } + + /** + * Transfer an established call to an existing ongoing session. + * When the transfer is complete, the current call gets disconnected locally. + */ + @Override + public void consultativeTransfer(@NonNull IImsCallSession transferToSession) { + } + + /** * Rejects an incoming call or session update. * * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}. @@ -610,6 +635,17 @@ public class ImsCallSessionImplBase extends IImsCallSession.Stub { } @Override + public void callSessionTransferred() throws RemoteException { + mNewListener.callSessionTransferred(); + } + + @Override + public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo) + throws RemoteException { + mNewListener.callSessionTransferFailed(reasonInfo); + } + + @Override public void callQualityChanged(CallQuality callQuality) throws RemoteException { mNewListener.callQualityChanged(callQuality); } diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java index e8f69ea64a22..73ba0e393e78 100644 --- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java @@ -16,6 +16,7 @@ package android.telephony.ims.stub; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; import android.os.Message; @@ -183,6 +184,18 @@ public class ImsCallSessionImplBase implements AutoCloseable { } @Override + public void transfer(@NonNull String number, boolean isConfirmationRequired) { + ImsCallSessionImplBase.this.transfer(number, isConfirmationRequired); + } + + @Override + public void consultativeTransfer(@NonNull IImsCallSession transferToSession) { + ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase(); + otherSession.setServiceImpl(transferToSession); + ImsCallSessionImplBase.this.transfer(otherSession); + } + + @Override public void terminate(int reason) { ImsCallSessionImplBase.this.terminate(reason); } @@ -423,6 +436,26 @@ public class ImsCallSessionImplBase implements AutoCloseable { } /** + * Transfer an established call to given number + * + * @param number number to transfer the call + * @param isConfirmationRequired if {@code True}, indicates Assured transfer, + * if {@code False} it indicates Blind transfer. + * @hide + */ + public void transfer(@NonNull String number, boolean isConfirmationRequired) { + } + + /** + * Transfer an established call to another call session + * + * @param otherSession The other ImsCallSession to transfer the ongoing session to. + * @hide + */ + public void transfer(@NonNull ImsCallSessionImplBase otherSession) { + } + + /** * Terminates a call. * * @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}. diff --git a/telephony/java/com/android/ims/internal/IImsCallSession.aidl b/telephony/java/com/android/ims/internal/IImsCallSession.aidl index 15234e5c0e92..0466efc2f6c6 100644 --- a/telephony/java/com/android/ims/internal/IImsCallSession.aidl +++ b/telephony/java/com/android/ims/internal/IImsCallSession.aidl @@ -18,7 +18,6 @@ package com.android.ims.internal; import android.os.Message; import android.telephony.ims.aidl.IImsCallSessionListener; - import android.telephony.ims.ImsCallProfile; import android.telephony.ims.ImsStreamMediaProfile; import com.android.ims.internal.IImsVideoCallProvider; @@ -151,6 +150,22 @@ interface IImsCallSession { void reject(int reason); /** + * Transfer an established call to given number + * + * @param number number to transfer the call + * @param isConfirmationRequired if {@code True}, indicates Assured transfer, + * if {@code False} it indicates Blind transfer. + */ + void transfer(String number, boolean isConfirmationRequired); + + /** + * Transfer an established call to another call session + * + * @param transferToSession The other ImsCallSession to transfer the ongoing session to. + */ + void consultativeTransfer(in IImsCallSession transferToSession); + + /** * Terminates a call. * * @see Listener#callSessionTerminated diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl index b33a9f1ad23b..1c62cc48093c 100644 --- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl +++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl @@ -184,6 +184,12 @@ oneway interface IImsCallSessionListener { void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile); /** + * Notifies about the response for call transfer request. + */ + void callSessionTransferred(); + + void callSessionTransferFailed(in ImsReasonInfo reasonInfo); + /** * Notifies of a change to the call quality. * @param callQuality then updated call quality */ |