Merge "IMS: Explicit Call transfer APIS." am: 9f28d84c96 am: 129308a66f

Change-Id: Ic6e71454ce0399f268f9718da47dcc8f1c1b27fa
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
old mode 100644
new mode 100755
index 9a36d3e..52112b1
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -2306,6 +2306,45 @@
     }
 
     /**
+     * Transfers the call if it is active or held.
+     *
+     * @param number number to be transferred to.
+     * @param isConfirmationRequired whether for blind or assured transfer.
+     */
+    @VisibleForTesting
+    public void transfer(Uri number, boolean isConfirmationRequired) {
+        if (mState == CallState.ACTIVE || mState == CallState.ON_HOLD) {
+            if (mConnectionService != null) {
+                mConnectionService.transfer(this, number, isConfirmationRequired);
+            } else {
+                Log.e(this, new NullPointerException(),
+                        "transfer call failed due to null CS callId=%s", getId());
+            }
+            Log.addEvent(this, LogUtils.Events.REQUEST_TRANSFER, Log.pii(number));
+        }
+    }
+
+    /**
+     * Transfers the call when this call is active and the other call is held.
+     * This is for Consultative call transfer.
+     *
+     * @param otherCall The other {@link Call} to which this call will be transferred.
+     */
+    @VisibleForTesting
+    public void transfer(Call otherCall) {
+        if (mState == CallState.ACTIVE &&
+                (otherCall != null && otherCall.getState() == CallState.ON_HOLD)) {
+            if (mConnectionService != null) {
+                mConnectionService.transfer(this, otherCall);
+            } else {
+                Log.e(this, new NullPointerException(),
+                        "transfer call failed due to null CS callId=%s", getId());
+            }
+            Log.addEvent(this, LogUtils.Events.REQUEST_CONSULTATIVE_TRANSFER, otherCall);
+        }
+    }
+
+    /**
      * Puts the call on hold if it is currently active.
      */
     @VisibleForTesting
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
old mode 100644
new mode 100755
index 64bbe99..495dcb4
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -2381,6 +2381,32 @@
         }
     }
 
+    /**
+     * Instructs Telecom to transfer the specified call. Intended to be invoked by the in-call
+     * app through {@link InCallAdapter} after the user opts to transfer the said call.
+     */
+    @VisibleForTesting
+    public void transferCall(Call call, Uri number, boolean isConfirmationRequired) {
+        if (!mCalls.contains(call)) {
+            Log.i(this, "transferCall - Request to transfer a non-existent call %s", call);
+        } else {
+            call.transfer(number, isConfirmationRequired);
+        }
+    }
+
+    /**
+     * Instructs Telecom to transfer the specified call to another ongoing call.
+     * Intended to be invoked by the in-call app through {@link InCallAdapter} after the user opts
+     * to transfer the said call (consultative transfer).
+     */
+    @VisibleForTesting
+    public void transferCall(Call call, Call otherCall) {
+        if (!mCalls.contains(call) || !mCalls.contains(otherCall)) {
+            Log.i(this, "transferCall - Non-existent call %s or %s", call, otherCall);
+        } else {
+            call.transfer(otherCall);
+        }
+    }
 
     /**
      * Instructs Telecom to play the specified DTMF tone within the specified call.
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
old mode 100644
new mode 100755
index a3b23af..3ae00aa
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -1614,6 +1614,33 @@
         }
     }
 
+    /** @see IConnectionService#transfer(String, Uri , boolean, Session.Info) */
+    void transfer(Call call, Uri number, boolean isConfirmationRequired) {
+        final String callId = mCallIdMapper.getCallId(call);
+        if (callId != null && isServiceValid("transfer")) {
+            try {
+                logOutgoing("transfer %s", callId);
+                mServiceInterface.transfer(callId, number, isConfirmationRequired,
+                        Log.getExternalSession());
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    /** @see IConnectionService#consultativeTransfer(String, String, Session.Info) */
+    void transfer(Call call, Call otherCall) {
+        final String callId = mCallIdMapper.getCallId(call);
+        final String otherCallId = mCallIdMapper.getCallId(otherCall);
+        if (callId != null && otherCallId != null && isServiceValid("consultativeTransfer")) {
+            try {
+                logOutgoing("consultativeTransfer %s", callId);
+                mServiceInterface.consultativeTransfer(callId, otherCallId,
+                        Log.getExternalSession());
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     /** @see IConnectionService#playDtmfTone(String, char, Session.Info) */
     void playDtmfTone(Call call, char digit) {
         final String callId = mCallIdMapper.getCallId(call);
diff --git a/src/com/android/server/telecom/InCallAdapter.java b/src/com/android/server/telecom/InCallAdapter.java
old mode 100644
new mode 100755
index f47f212..d25665f
--- a/src/com/android/server/telecom/InCallAdapter.java
+++ b/src/com/android/server/telecom/InCallAdapter.java
@@ -151,6 +151,54 @@
         }
     }
 
+    public void transferCall(String callId, Uri targetNumber, boolean isConfirmationRequired) {
+        try {
+            Log.startSession(LogUtils.Sessions.ICA_TRANSFER_CALL, mOwnerPackageName);
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    Log.i(this, "transferCall - %s, %s, %b", callId, Log.pii(targetNumber),
+                            isConfirmationRequired);
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        mCallsManager.transferCall(call, targetNumber, isConfirmationRequired);
+                    } else {
+                        Log.w(this, "transferCall, unknown call id: %s", callId);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        } finally {
+            Log.endSession();
+        }
+    }
+
+    @Override
+    public void consultativeTransfer(String callId, String otherCallId) {
+        try {
+            Log.startSession(LogUtils.Sessions.ICA_CONSULTATIVE_TRANSFER, mOwnerPackageName);
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    Log.i(this, "consultativeTransfer - %s, %s", callId, otherCallId);
+                    Call call = mCallIdMapper.getCall(callId);
+                    Call otherCall = mCallIdMapper.getCall(otherCallId);
+                    if (call != null && otherCall != null) {
+                        mCallsManager.transferCall(call, otherCall);
+                    } else {
+                        Log.w(this, "consultativeTransfer, unknown call id: %s or %s",
+                                callId, otherCallId);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        } finally {
+            Log.endSession();
+        }
+    }
+
     @Override
     public void playDtmfTone(String callId, char digit) {
         try {
diff --git a/src/com/android/server/telecom/LogUtils.java b/src/com/android/server/telecom/LogUtils.java
index 0b7a1b5..280b2a8 100644
--- a/src/com/android/server/telecom/LogUtils.java
+++ b/src/com/android/server/telecom/LogUtils.java
@@ -63,6 +63,8 @@
         public static final String ICA_ANSWER_CALL = "ICA.aC";
         public static final String ICA_DEFLECT_CALL = "ICA.defC";
         public static final String ICA_REJECT_CALL = "ICA.rC";
+        public static final String ICA_TRANSFER_CALL = "ICA.tC";
+        public static final String ICA_CONSULTATIVE_TRANSFER = "ICA.cT";
         public static final String ICA_DISCONNECT_CALL = "ICA.dC";
         public static final String ICA_HOLD_CALL = "ICA.hC";
         public static final String ICA_UNHOLD_CALL = "ICA.uC";
@@ -108,6 +110,8 @@
                 "REQUEST_PICKUP_FOR_AUDIO_PROCESSING";
         public static final String REQUEST_DEFLECT = "REQUEST_DEFLECT";
         public static final String REQUEST_REJECT = "REQUEST_REJECT";
+        public static final String REQUEST_TRANSFER = "REQUEST_TRANSFER";
+        public static final String REQUEST_CONSULTATIVE_TRANSFER = "REQUEST_CONSULTATIVE_TRANSFER";
         public static final String START_DTMF = "START_DTMF";
         public static final String STOP_DTMF = "STOP_DTMF";
         public static final String START_RINGER = "START_RINGER";
diff --git a/src/com/android/server/telecom/ParcelableCallUtils.java b/src/com/android/server/telecom/ParcelableCallUtils.java
index 3b77621..aae3e36 100644
--- a/src/com/android/server/telecom/ParcelableCallUtils.java
+++ b/src/com/android/server/telecom/ParcelableCallUtils.java
@@ -515,8 +515,15 @@
 
         Connection.CAPABILITY_SUPPORT_DEFLECT,
         android.telecom.Call.Details.CAPABILITY_SUPPORT_DEFLECT,
+
         Connection.CAPABILITY_ADD_PARTICIPANT,
-        android.telecom.Call.Details.CAPABILITY_ADD_PARTICIPANT
+        android.telecom.Call.Details.CAPABILITY_ADD_PARTICIPANT,
+
+        Connection.CAPABILITY_TRANSFER,
+        android.telecom.Call.Details.CAPABILITY_TRANSFER,
+
+        Connection.CAPABILITY_TRANSFER_CONSULTATIVE,
+        android.telecom.Call.Details.CAPABILITY_TRANSFER_CONSULTATIVE
     };
 
     private static int convertConnectionToCallCapabilities(int connectionCapabilities) {
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
old mode 100644
new mode 100755
index fb04577..c1a3b80
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
@@ -302,6 +302,14 @@
                 throws RemoteException { }
 
         @Override
+        public void transfer(String callId, Uri number, boolean isConfirmationRequired,
+                Session.Info info) throws RemoteException { }
+
+        @Override
+        public void consultativeTransfer(String callId, String otherCallId,
+                Session.Info info) throws RemoteException { }
+
+        @Override
         public void reject(String callId, Session.Info info) throws RemoteException {
             rejectedCallIds.add(callId);
         }