Capability and UI support for merge/swap/addcall

Change-Id: I0d2a9b855028ba2e82e4ffd627389000e3cc1891
diff --git a/common/src/com/android/services/telephony/common/Call.java b/common/src/com/android/services/telephony/common/Call.java
index 0b4abb7..088a06d 100644
--- a/common/src/com/android/services/telephony/common/Call.java
+++ b/common/src/com/android/services/telephony/common/Call.java
@@ -47,11 +47,15 @@
 
     /**
      * Defines a set of capabilities that a call can have as a bit mask.
+     * TODO: Should some of these be capabilities of the Phone instead of the call?
      */
     public static class Capabilities {
-        public static final int HOLD = 0x00000001;    /* has ability to hold the call */
+        public static final int HOLD        = 0x00000001;    /* has ability to hold the call */
+        public static final int MERGE_CALLS = 0x00000002;    /* has ability to merge calls */
+        public static final int SWAP_CALLS  = 0x00000004;    /* swap with a background call */
+        public static final int ADD_CALL    = 0x00000008;    /* add another call to this one */
 
-        public static final int ALL = HOLD;
+        public static final int ALL = HOLD | MERGE_CALLS | SWAP_CALLS | ADD_CALL;
     }
 
     /**
@@ -271,6 +275,8 @@
         buffer.append(STATE_MAP.get(mState));
         buffer.append(", disconnect_cause: ");
         buffer.append(getDisconnectCause().toString());
+        buffer.append(", capabilities: ");
+        buffer.append(Integer.toHexString(getCapabilities()));
         return buffer.toString();
     }
 }
diff --git a/common/src/com/android/services/telephony/common/ICallCommandService.aidl b/common/src/com/android/services/telephony/common/ICallCommandService.aidl
index defeeef..5969575 100644
--- a/common/src/com/android/services/telephony/common/ICallCommandService.aidl
+++ b/common/src/com/android/services/telephony/common/ICallCommandService.aidl
@@ -45,6 +45,23 @@
     void hold(int callId, boolean hold);
 
     /**
+     * Merge foreground and background calls.
+     */
+    void merge();
+
+    /**
+     * Swap foreground and background calls.
+     */
+    void swap();
+
+    /**
+     * Add another call.
+     * TODO(klp): Should this go through the service at all?
+     *            It could just as easily call dialer directly.
+     */
+    void addCall();
+
+    /**
      * Mute the phone.
      */
     void mute(boolean onOff);
diff --git a/src/com/android/phone/CallCommandService.java b/src/com/android/phone/CallCommandService.java
index e5adb52..01b4dd8 100644
--- a/src/com/android/phone/CallCommandService.java
+++ b/src/com/android/phone/CallCommandService.java
@@ -16,11 +16,14 @@
 
 package com.android.phone;
 
+import android.bluetooth.IBluetoothHeadsetPhone;
 import android.content.Context;
+import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.util.Log;
 
 import com.android.internal.telephony.CallManager;
+import com.android.internal.telephony.PhoneConstants;
 import com.android.phone.CallModeler.CallResult;
 import com.android.services.telephony.common.AudioMode;
 import com.android.services.telephony.common.Call;
@@ -129,10 +132,52 @@
     }
 
     @Override
+    public void merge() {
+        if (PhoneUtils.okToMergeCalls(mCallManager)) {
+            PhoneUtils.mergeCalls(mCallManager);
+        }
+    }
+
+    @Override
+    public void addCall() {
+        // start new call checks okToAddCall() already
+        PhoneUtils.startNewCall(mCallManager);
+    }
+
+
+    @Override
+    public void swap() {
+        if (!PhoneUtils.okToSwapCalls(mCallManager)) {
+            // TODO: throw an error instead?
+            return;
+        }
+
+        // Swap the fg and bg calls.
+        // In the future we may provides some way for user to choose among
+        // multiple background calls, for now, always act on the first background calll.
+        PhoneUtils.switchHoldingAndActive(mCallManager.getFirstActiveBgCall());
+
+        final PhoneGlobals mApp = PhoneGlobals.getInstance();
+
+        // If we have a valid BluetoothPhoneService then since CDMA network or
+        // Telephony FW does not send us information on which caller got swapped
+        // we need to update the second call active state in BluetoothPhoneService internally
+        if (mCallManager.getBgPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
+            final IBluetoothHeadsetPhone btPhone = mApp.getBluetoothPhoneService();
+            if (btPhone != null) {
+                try {
+                    btPhone.cdmaSwapSecondCallState();
+                } catch (RemoteException e) {
+                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                }
+            }
+        }
+    }
+
+    @Override
     public void mute(boolean onOff) {
         try {
-            //PhoneUtils.setMute(onOff);
-            mAudioRouter.setAudioMode(onOff ? AudioMode.BLUETOOTH : AudioMode.EARPIECE);
+            PhoneUtils.setMute(onOff);
         } catch (Exception e) {
             Log.e(TAG, "Error during mute().", e);
         }
diff --git a/src/com/android/phone/CallModeler.java b/src/com/android/phone/CallModeler.java
index dcd11f7..154b155 100644
--- a/src/com/android/phone/CallModeler.java
+++ b/src/com/android/phone/CallModeler.java
@@ -29,6 +29,7 @@
 
 import com.android.internal.telephony.CallManager;
 import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyCapabilities;
 import com.android.services.telephony.common.Call;
@@ -264,7 +265,10 @@
             changed = true;
         }
 
-        final int newCapabilities = getCapabilitiesFor(connection);
+        /**
+         * !!! Uses values from connection and call collected above so this part must be last !!!
+         */
+        final int newCapabilities = getCapabilitiesFor(connection, call);
         if (call.getCapabilities() != newCapabilities) {
             call.setCapabilities(newCapabilities);
             changed = true;
@@ -276,14 +280,44 @@
     /**
      * Returns a mask of capabilities for the connection such as merge, hold, etc.
      */
-    private int getCapabilitiesFor(Connection connection) {
+    private int getCapabilitiesFor(Connection connection, Call call) {
+        final boolean callIsActive = (call.getState() == Call.State.ACTIVE);
+        final Phone phone = connection.getCall().getPhone();
+
+        final boolean canHold = TelephonyCapabilities.supportsAnswerAndHold(phone);
+        boolean canAddCall = false;
+        boolean canMergeCall = false;
+        boolean canSwapCall = false;
+
+        // only applies to active calls
+        if (callIsActive) {
+            canAddCall = PhoneUtils.okToAddCall(mCallManager);
+            canMergeCall = PhoneUtils.okToMergeCalls(mCallManager);
+            canSwapCall = PhoneUtils.okToSwapCalls(mCallManager);
+        }
+
+        // special rules section!
+        // CDMA always has Add
+        if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
+            canAddCall = true;
+        } else {
+            // if neither merge nor add is on...then allow add
+            canAddCall |= !(canAddCall || canMergeCall);
+        }
+
         int retval = 0x0;
-
-        final boolean hold = TelephonyCapabilities.supportsAnswerAndHold(connection.getCall().getPhone());
-
-        if (hold) {
+        if (canHold) {
             retval |= Capabilities.HOLD;
         }
+        if (canAddCall) {
+            retval |= Capabilities.ADD_CALL;
+        }
+        if (canMergeCall) {
+            retval |= Capabilities.MERGE_CALLS;
+        }
+        if (canSwapCall) {
+            retval |= Capabilities.SWAP_CALLS;
+        }
 
         return retval;
     }
diff --git a/src/com/android/phone/InCallScreen.java b/src/com/android/phone/InCallScreen.java
index 8f52f29..c672353 100644
--- a/src/com/android/phone/InCallScreen.java
+++ b/src/com/android/phone/InCallScreen.java
@@ -3507,26 +3507,6 @@
         // to lose any previous digits from the current call; see the TODO
         // comment on DTMFTwelvKeyDialer.clearDigits() for more info.)
         mDialer.clearDigits();
-
-        // Swap the fg and bg calls.
-        // In the future we may provides some way for user to choose among
-        // multiple background calls, for now, always act on the first background calll.
-        PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall());
-
-        // If we have a valid BluetoothPhoneService then since CDMA network or
-        // Telephony FW does not send us information on which caller got swapped
-        // we need to update the second call active state in BluetoothPhoneService internally
-        if (mCM.getBgPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
-            IBluetoothHeadsetPhone btPhone = mApp.getBluetoothPhoneService();
-            if (btPhone != null) {
-                try {
-                    btPhone.cdmaSwapSecondCallState();
-                } catch (RemoteException e) {
-                    Log.e(LOG_TAG, Log.getStackTraceString(new Throwable()));
-                }
-            }
-        }
-
     }
 
     /**