Adding unit test for ImsConferenceController

Changes for testing:
 - modify some Mock/Proxy class for using/testing some final public api
 - modify several parts to inject Proxy for testing

Test: Adding unit test for ImsConferenceControllerTest.java
Change-Id: I966ad707e47b98e4adf0e7e180b0ca6ee2a58a11
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 4e88910..509d158 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -215,7 +215,7 @@
     /**
      * The telephony connection service; used to add new participant connections to Telecom.
      */
-    private TelephonyConnectionService mTelephonyConnectionService;
+    private TelephonyConnectionServiceProxy mTelephonyConnectionService;
 
     /**
      * The connection to the conference server which is hosting the conference.
@@ -232,6 +232,8 @@
      */
     private Uri[] mConferenceHostAddress;
 
+    private TelecomAccountRegistry mTelecomAccountRegistry;
+
     /**
      * The known conference participant connections.  The HashMap is keyed by a Pair containing
      * the handle and endpoint Uris.
@@ -267,11 +269,14 @@
      * @param conferenceHost The telephony connection hosting the conference.
      * @param phoneAccountHandle The phone account handle associated with the conference.
      */
-    public ImsConference(TelephonyConnectionService telephonyConnectionService,
+    public ImsConference(TelecomAccountRegistry telecomAccountRegistry,
+                         TelephonyConnectionServiceProxy telephonyConnectionService,
             TelephonyConnection conferenceHost, PhoneAccountHandle phoneAccountHandle) {
 
         super(phoneAccountHandle);
 
+        mTelecomAccountRegistry = telecomAccountRegistry;
+
         // Specify the connection time of the conference to be the connection time of the original
         // connection.
         long connectTime = conferenceHost.getOriginalConnection().getConnectTime();
@@ -589,8 +594,7 @@
             Phone imsPhone = mConferenceHost.getPhone();
             mConferenceHostPhoneAccountHandle =
                     PhoneUtils.makePstnPhoneAccountHandle(imsPhone.getDefaultPhone());
-            Uri hostAddress = TelecomAccountRegistry.getInstance(mTelephonyConnectionService)
-                    .getAddress(mConferenceHostPhoneAccountHandle);
+            Uri hostAddress = mTelecomAccountRegistry.getAddress(mConferenceHostPhoneAccountHandle);
 
             ArrayList<Uri> hostAddresses = new ArrayList<>();
 
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 678a402..1a5a76c 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -91,7 +91,7 @@
     /**
      * The current {@link ConnectionService}.
      */
-    private final TelephonyConnectionService mConnectionService;
+    private final TelephonyConnectionServiceProxy mConnectionService;
 
     /**
      * List of known {@link TelephonyConnection}s.
@@ -104,13 +104,17 @@
      */
     private final ArrayList<ImsConference> mImsConferences = new ArrayList<>(1);
 
+    private TelecomAccountRegistry mTelecomAccountRegistry;
+
     /**
      * Creates a new instance of the Ims conference controller.
      *
      * @param connectionService The current connection service.
      */
-    public ImsConferenceController(TelephonyConnectionService connectionService) {
+    public ImsConferenceController(TelecomAccountRegistry telecomAccountRegistry,
+                                   TelephonyConnectionServiceProxy connectionService) {
         mConnectionService = connectionService;
+        mTelecomAccountRegistry = telecomAccountRegistry;
     }
 
     /**
@@ -319,7 +323,6 @@
         Iterator<TelephonyConnection> it = mTelephonyConnections.iterator();
         while (it.hasNext()) {
             TelephonyConnection connection = it.next();
-
             if (connection.isImsConnection() && connection.getOriginalConnection() != null &&
                     connection.getOriginalConnection().isMultiparty()) {
 
@@ -365,8 +368,8 @@
                     PhoneUtils.makePstnPhoneAccountHandle(imsPhone.getDefaultPhone());
         }
 
-        ImsConference conference = new ImsConference(mConnectionService, conferenceHostConnection,
-                phoneAccountHandle);
+        ImsConference conference = new ImsConference(mTelecomAccountRegistry, mConnectionService,
+                conferenceHostConnection, phoneAccountHandle);
         conference.setState(conferenceHostConnection.getState());
         conference.addListener(mConferenceListener);
         conference.updateConferenceParticipantsAfterCreation();
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index b5f195f..ccc39f2 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -72,22 +72,43 @@
     private static final Pattern CDMA_ACTIVATION_CODE_REGEX_PATTERN =
             Pattern.compile("\\*228[0-9]{0,2}");
 
-    private final TelephonyConferenceController mTelephonyConferenceController =
-            new TelephonyConferenceController(new TelephonyConnectionServiceProxy() {
-                @Override
-                public Collection<Connection> getAllConnections() {
-                    return TelephonyConnectionService.this.getAllConnections();
-                }
+    private final TelephonyConnectionServiceProxy mTelephonyConnectionServiceProxy =
+            new TelephonyConnectionServiceProxy() {
+        @Override
+        public Collection<Connection> getAllConnections() {
+            return TelephonyConnectionService.this.getAllConnections();
+        }
+        @Override
+        public void addConference(TelephonyConference mTelephonyConference) {
+            TelephonyConnectionService.this.addConference(mTelephonyConference);
+        }
+        @Override
+        public void addConference(ImsConference mImsConference) {
+            TelephonyConnectionService.this.addConference(mImsConference);
+        }
+        @Override
+        public void removeConnection(Connection connection) {
+            TelephonyConnectionService.this.removeConnection(connection);
+        }
+        @Override
+        public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
+                                          Connection connection) {
+            TelephonyConnectionService.this
+                    .addExistingConnection(phoneAccountHandle, connection);
+        }
+        @Override
+        public void addConnectionToConferenceController(TelephonyConnection connection) {
+            TelephonyConnectionService.this.addConnectionToConferenceController(connection);
+        }
+    };
 
-                @Override
-                public void addConference(TelephonyConference mTelephonyConference) {
-                    TelephonyConnectionService.this.addConference(mTelephonyConference);
-                }
-            });
+    private final TelephonyConferenceController mTelephonyConferenceController =
+            new TelephonyConferenceController(mTelephonyConnectionServiceProxy);
     private final CdmaConferenceController mCdmaConferenceController =
             new CdmaConferenceController(this);
     private final ImsConferenceController mImsConferenceController =
-            new ImsConferenceController(this);
+            new ImsConferenceController(TelecomAccountRegistry.getInstance(this),
+                    mTelephonyConnectionServiceProxy);
 
     private ComponentName mExpectedComponentName = null;
     private EmergencyCallHelper mEmergencyCallHelper;
diff --git a/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java b/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java
index 384ce69..ee77b08 100644
--- a/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java
+++ b/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java
@@ -17,6 +17,7 @@
 package com.android.services.telephony;
 
 import android.telecom.Connection;
+import android.telecom.PhoneAccountHandle;
 
 import java.util.Collection;
 
@@ -27,4 +28,9 @@
 public interface TelephonyConnectionServiceProxy {
     Collection<Connection> getAllConnections();
     void addConference(TelephonyConference mTelephonyConference);
+    void addConference(ImsConference mImsConference);
+    void removeConnection(Connection connection);
+    void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
+                               Connection connection);
+    void addConnectionToConferenceController(TelephonyConnection connection);
 }
diff --git a/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java b/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
new file mode 100644
index 0000000..3d88af7
--- /dev/null
+++ b/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.services.telephony;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.times;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.PhoneConstants;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests the functionality in ImsConferenceController.java
+ */
+
+public class ImsConferenceControllerTest {
+
+    @Mock
+    private TelephonyConnectionServiceProxy mMockTelephonyConnectionServiceProxy;
+
+    private TelecomAccountRegistry mTelecomAccountRegistry;
+
+    private MockTelephonyConnection mMockTelephonyConnectionA;
+    private MockTelephonyConnection mMockTelephonyConnectionB;
+
+    private ImsConferenceController mControllerTest;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        mTelecomAccountRegistry = TelecomAccountRegistry.getInstance(null);
+        mMockTelephonyConnectionA = new MockTelephonyConnection();
+        mMockTelephonyConnectionB = new MockTelephonyConnection();
+
+        mControllerTest = new ImsConferenceController(mTelecomAccountRegistry,
+                mMockTelephonyConnectionServiceProxy);
+    }
+
+    /**
+     * Behavior: add telephony connections B and A to conference controller,
+     *           set status for connections, remove one call
+     * Assumption: after performing the behaviors, the status of Connection A is STATE_ACTIVE;
+     *             the status of Connection B is STATE_HOLDING
+     * Expected: Connection A and Connection B are conferenceable with each other;
+     *           Connection B is not conferenceable with Connection A after A is removed;
+     *           addConference for ImsConference is not called
+     */
+    @Test
+    @SmallTest
+    public void testConferenceable() {
+
+        mControllerTest.add(mMockTelephonyConnectionB);
+        mControllerTest.add(mMockTelephonyConnectionA);
+
+        mMockTelephonyConnectionA.setActive();
+        mMockTelephonyConnectionB.setOnHold();
+
+        assertTrue(mMockTelephonyConnectionA.getConferenceables()
+                .contains(mMockTelephonyConnectionB));
+        assertTrue(mMockTelephonyConnectionB.getConferenceables()
+                .contains(mMockTelephonyConnectionA));
+
+        // verify addConference method is never called
+        verify(mMockTelephonyConnectionServiceProxy, never())
+                .addConference(any(ImsConference.class));
+
+        // call A removed
+        mControllerTest.remove(mMockTelephonyConnectionA);
+        assertFalse(mMockTelephonyConnectionB.getConferenceables()
+                .contains(mMockTelephonyConnectionA));
+    }
+
+    /**
+     * Behavior: add telephony connection B and A to conference controller,
+     *           set status for merged connections
+     * Assumption: after performing the behaviors, the status of Connection A is STATE_ACTIVE;
+     *             the status of Connection B is STATE_HOLDING;
+     *             getPhoneType() in the original connection of the telephony connection
+     *             is PhoneConstants.PHONE_TYPE_IMS
+     * Expected: addConference for ImsConference is called twice
+     */
+    @Test
+    @SmallTest
+    public void testMergeMultiPartyCalls() {
+
+        when(mMockTelephonyConnectionA.mMockRadioConnection.getPhoneType())
+                .thenReturn(PhoneConstants.PHONE_TYPE_IMS);
+        when(mMockTelephonyConnectionB.mMockRadioConnection.getPhoneType())
+                .thenReturn(PhoneConstants.PHONE_TYPE_IMS);
+        when(mMockTelephonyConnectionA.mMockRadioConnection.isMultiparty()).thenReturn(true);
+        when(mMockTelephonyConnectionB.mMockRadioConnection.isMultiparty()).thenReturn(true);
+
+        mControllerTest.add(mMockTelephonyConnectionB);
+        mControllerTest.add(mMockTelephonyConnectionA);
+
+        mMockTelephonyConnectionA.setActive();
+        mMockTelephonyConnectionB.setOnHold();
+
+        verify(mMockTelephonyConnectionServiceProxy, times(2))
+                .addConference(any(ImsConference.class));
+
+    }
+}
diff --git a/tests/src/com/android/services/telephony/MockTelephonyConnection.java b/tests/src/com/android/services/telephony/MockTelephonyConnection.java
index e647116..b2d6ed8 100644
--- a/tests/src/com/android/services/telephony/MockTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/MockTelephonyConnection.java
@@ -48,10 +48,9 @@
         super(null, null);
         MockitoAnnotations.initMocks(this);
 
-        //mock radioConnection mPhone mCall
+        // Set up mMockRadioConnection and mMockPhone to contain an active call
         when(mMockRadioConnection.getState()).thenReturn(Call.State.ACTIVE);
         when(mMockRadioConnection.getCall()).thenReturn(mMockCall);
-        //to pass the PhoneAccount
         when(mMockPhone.getRingingCall()).thenReturn(mMockCall);
         when(mMockCall.getState()).thenReturn(Call.State.ACTIVE);
     }
@@ -67,7 +66,7 @@
     }
 
     public TelephonyConnection cloneConnection() {
-        return null;
+        return this;
     }
 
 }