Support conference calling. (2/4)

Bug: 15006702
Change-Id: I2764ea242f783ba478c9eae86618dd33e9fc792a
diff --git a/src/com/android/telecomm/CallServiceWrapper.java b/src/com/android/telecomm/CallServiceWrapper.java
index 6165804..4462cff 100644
--- a/src/com/android/telecomm/CallServiceWrapper.java
+++ b/src/com/android/telecomm/CallServiceWrapper.java
@@ -35,11 +35,11 @@
 import com.android.internal.telecomm.ICallServiceProvider;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
 
 import org.apache.http.conn.ClientConnectionRequest;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -50,6 +50,7 @@
  * and instead should use this class to invoke methods of {@link ICallService}.
  */
 final class CallServiceWrapper extends ServiceBinder<ICallService> {
+    private static final String TAG = CallServiceWrapper.class.getSimpleName();
 
     private final class Adapter extends ICallServiceAdapter.Stub {
         private static final int MSG_NOTIFY_INCOMING_CALL = 1;
@@ -62,7 +63,10 @@
         private static final int MSG_SET_ON_HOLD = 8;
         private static final int MSG_SET_REQUESTING_RINGBACK = 9;
         private static final int MSG_ON_POST_DIAL_WAIT = 10;
-        private static final int MSG_HANDOFF_CALL = 11;
+        private static final int MSG_CAN_CONFERENCE = 11;
+        private static final int MSG_SET_IS_CONFERENCED = 12;
+        private static final int MSG_ADD_CONFERENCE_CALL = 13;
+        private static final int MSG_HANDOFF_CALL = 14;
 
         private final Handler mHandler = new Handler() {
             @Override
@@ -79,8 +83,7 @@
                             mIncomingCallsManager.handleSuccessfulIncomingCall(call, callInfo);
                         } else {
                             Log.w(this, "notifyIncomingCall, unknown incoming call: %s, id: %s",
-                                    call,
-                                    clientCallInfo.getId());
+                                    call, clientCallInfo.getId());
                         }
                         break;
                     case MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL: {
@@ -176,7 +179,7 @@
                         }
                         break;
                     }
-                    case MSG_ON_POST_DIAL_WAIT:
+                    case MSG_ON_POST_DIAL_WAIT: {
                         SomeArgs args = (SomeArgs) msg.obj;
                         try {
                             call = mCallIdMapper.getCall(args.arg1);
@@ -189,6 +192,8 @@
                         } finally {
                             args.recycle();
                         }
+                        break;
+                    }
                     case MSG_HANDOFF_CALL:
                         call = mCallIdMapper.getCall(msg.obj);
                         if (call != null) {
@@ -197,6 +202,63 @@
                             Log.w(this, "handoffCall, unknown call id: %s", msg.obj);
                         }
                         break;
+                    case MSG_CAN_CONFERENCE: {
+                        call = mCallIdMapper.getCall(msg.obj);
+                        if (call != null) {
+                            call.setIsConferenceCapable(msg.arg1 == 1);
+                        } else {
+                            Log.w(CallServiceWrapper.this, "canConference, unknown call id: %s",
+                                    msg.obj);
+                        }
+                        break;
+                    }
+                    case MSG_SET_IS_CONFERENCED: {
+                        SomeArgs args = (SomeArgs) msg.obj;
+                        try {
+                            Call childCall = mCallIdMapper.getCall(args.arg1);
+                            if (childCall != null) {
+                                String conferenceCallId = (String) args.arg2;
+                                Log.d(this, "setIsConferenced %s, %s", childCall, conferenceCallId);
+
+                                if (conferenceCallId == null) {
+                                    childCall.setParentCall(null);
+                                } else {
+                                    Call conferenceCall = mCallIdMapper.getCall(conferenceCallId);
+                                    if (conferenceCall != null &&
+                                            !mPendingConferenceCalls.contains(conferenceCall)) {
+                                        childCall.setParentCall(conferenceCall);
+                                    } else {
+                                        Log.w(this, "setIsConferenced, unknown conference id %s",
+                                                conferenceCallId);
+                                    }
+                                }
+                            } else {
+                                Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1);
+                            }
+                        } finally {
+                            args.recycle();
+                        }
+                        break;
+                    }
+                    case MSG_ADD_CONFERENCE_CALL: {
+                        SomeArgs args = (SomeArgs) msg.obj;
+                        try {
+                            String callId = (String) args.arg1;
+                            Log.d(this, "addConferenceCall attempt %s, %s",
+                                    callId, mPendingConferenceCalls);
+
+                            Call conferenceCall = mCallIdMapper.getCall(callId);
+                            if (mPendingConferenceCalls.remove(conferenceCall)) {
+                                Log.v(this, "confirming conf call %s", conferenceCall);
+                                conferenceCall.confirmConference();
+                            } else {
+                                Log.w(this, "addConference, unknown call id: %s", callId);
+                            }
+                        } finally {
+                            args.recycle();
+                        }
+                        break;
+                    }
                 }
             }
         };
@@ -294,12 +356,29 @@
 
         /** ${inheritDoc} */
         @Override
-        public void setCanConferenceWith(String callId, List<String> conferenceCapableCallIds) {
+        public void setCanConference(String callId, boolean canConference) {
+            Log.d(this, "setCanConference(%s, %b)", callId, canConference);
+            mHandler.obtainMessage(MSG_CAN_CONFERENCE, canConference ? 1 : 0, 0, callId)
+                    .sendToTarget();
         }
 
         /** ${inheritDoc} */
         @Override
-        public void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced) {
+        public void setIsConferenced(String callId, String conferenceCallId) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = callId;
+            args.arg2 = conferenceCallId;
+            mHandler.obtainMessage(MSG_SET_IS_CONFERENCED, args).sendToTarget();
+        }
+
+        /** ${InheritDoc} */
+        @Override
+        public void addConferenceCall(String callId, CallInfo callInfo) {
+            mCallIdMapper.checkValidCallId(callId);
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = callId;
+            args.arg2 = callInfo;
+            mHandler.obtainMessage(MSG_ADD_CONFERENCE_CALL, args).sendToTarget();
         }
 
         @Override
@@ -321,11 +400,13 @@
 
     private final Adapter mAdapter = new Adapter();
     private final CallsManager mCallsManager = CallsManager.getInstance();
-    private final Set<Call> mPendingIncomingCalls = Sets.newHashSet();
+    private final Set<Call> mPendingIncomingCalls = new HashSet<>();
+    private final Set<Call> mPendingConferenceCalls = new HashSet<>();
     private final CallServiceDescriptor mDescriptor;
     private final CallIdMapper mCallIdMapper = new CallIdMapper("CallService");
     private final IncomingCallsManager mIncomingCallsManager;
     private final Map<String, AsyncResultCallback<Boolean>> mPendingOutgoingCalls = new HashMap<>();
+    private final Handler mHandler = new Handler();
 
     private Binder mBinder = new Binder();
     private ICallService mServiceInterface;
@@ -521,7 +602,9 @@
     }
 
     void addCall(Call call) {
-        mCallIdMapper.addCall(call);
+        if (mCallIdMapper.getCallId(call) == null) {
+            mCallIdMapper.addCall(call);
+        }
     }
 
     /**
@@ -553,6 +636,37 @@
         }
     }
 
+    void conference(final Call conferenceCall, Call call) {
+        if (isServiceValid("conference")) {
+            try {
+                conferenceCall.setCallService(this);
+                mPendingConferenceCalls.add(conferenceCall);
+                mHandler.postDelayed(new Runnable() {
+                    @Override public void run() {
+                        if (mPendingConferenceCalls.remove(conferenceCall)) {
+                            conferenceCall.expireConference();
+                            Log.i(this, "Conference call expired: %s", conferenceCall);
+                        }
+                    }
+                }, Timeouts.getConferenceCallExpireMillis());
+
+                mServiceInterface.conference(
+                        mCallIdMapper.getCallId(conferenceCall),
+                        mCallIdMapper.getCallId(call));
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    void splitFromConference(Call call) {
+        if (isServiceValid("splitFromConference")) {
+            try {
+                mServiceInterface.splitFromConference(mCallIdMapper.getCallId(call));
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
     /** {@inheritDoc} */
     @Override
     protected void setServiceInterface(IBinder binder) {
@@ -589,12 +703,12 @@
                 mIncomingCallsManager.handleFailedIncomingCall(call);
             }
 
-      if (!mPendingIncomingCalls.isEmpty()) {
-        Log.wtf(this, "Pending calls did not get cleared.");
-        mPendingIncomingCalls.clear();
-      }
-    }
+            if (!mPendingIncomingCalls.isEmpty()) {
+                Log.wtf(this, "Pending calls did not get cleared.");
+                mPendingIncomingCalls.clear();
+            }
+        }
 
-    mCallIdMapper.clear();
-  }
+        mCallIdMapper.clear();
+    }
 }