Refactor some code in Telecom CTS tests

Add ability to add incoming calls in tests
Add some null checks and cleanup code to decrease flakiness

Bug: 20303674

Change-Id: I598250d13f31d2c0516fd27e446ec973ba7f07fd
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index feca18d..0488eb4 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -24,6 +24,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.telecom.Call;
+import android.telecom.CallAudioState;
 import android.telecom.Connection;
 import android.telecom.ConnectionRequest;
 import android.telecom.InCallService;
@@ -114,12 +115,41 @@
                     ConnectionRequest request) {
                 this.lock.release();
             }
+
+            @Override
+            public void onCreateIncomingConnection(MockConnection connection,
+                    ConnectionRequest request) {
+                this.lock.release();
+            }
         };
 
         MockConnectionService.setCallbacks(mConnectionCallbacks);
     }
 
     /**
+     * Puts Telecom in a state where there is an incoming call provided by the
+     * {@link MockConnectionService} which can be tested.
+     */
+    void addAndVerifyNewIncomingCall(Uri incomingHandle, Bundle extras) {
+        if (extras == null) {
+            extras = new Bundle();
+        }
+        extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_HANDLE, incomingHandle);
+        mTelecomManager.addNewIncomingCall(TEST_PHONE_ACCOUNT_HANDLE, extras);
+
+        try {
+            if (!mInCallCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
+                fail("No call added to InCallService.");
+            }
+        } catch (InterruptedException e) {
+            Log.i(TAG, "Test interrupted!");
+        }
+
+        assertEquals("InCallService should contain 1 call after adding a call.", 1,
+                mInCallCallbacks.getService().getCallCount());
+    }
+
+    /**
      *  Puts Telecom in a state where there is an active call provided by the
      *  {@link MockConnectionService} which can be tested.
      */
@@ -144,10 +174,9 @@
 
         assertEquals("InCallService should contain 1 call after adding a call.", 1,
                 mInCallCallbacks.getService().getCallCount());
-        assertTrue("TelecomManager should be in a call", mTelecomManager.isInCall());
     }
 
-    void verifyConnectionService() {
+    void verifyConnectionForOutgoingCall() {
         try {
             if (!mConnectionCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
                 fail("No outgoing call connection requested by Telecom");
@@ -170,6 +199,48 @@
         assertEquals(Connection.STATE_ACTIVE, connection.getState());
     }
 
+    void verifyConnectionForIncomingCall() {
+        try {
+            if (!mConnectionCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
+                fail("No incoming call connection requested by Telecom");
+            }
+        } catch (InterruptedException e) {
+            Log.i(TAG, "Test interrupted!");
+        }
+
+        assertNotNull("Telecom should bind to and create ConnectionService",
+                mConnectionCallbacks.getService());
+        assertNull("Telecom should not create outgoing connection for outgoing call",
+                mConnectionCallbacks.outgoingConnection);
+        assertNotNull("Telecom should create incoming connection for outgoing call",
+                mConnectionCallbacks.incomingConnection);
+
+        final MockConnection connection = mConnectionCallbacks.incomingConnection;
+        connection.setRinging();
+        assertEquals(Connection.STATE_RINGING, connection.getState());
+    }
+
+    /**
+     * Disconnect the created test call, verify that Telecom has cleared all calls and has
+     * unbound from the {@link ConnectionService}.
+     */
+    void cleanupAndVerifyUnbind() {
+        if (mInCallCallbacks != null && mInCallCallbacks.getService() != null) {
+            mInCallCallbacks.prepareForUnbind();
+
+            mInCallCallbacks.getService().disconnectLastCall();
+            assertNumCalls(mInCallCallbacks.getService(), 0);
+
+            try {
+                if (!mInCallCallbacks.unbindLock.tryAcquire(3, TimeUnit.SECONDS)) {
+                    fail("Telecom did not unbind from InCallService after all calls removed.");
+                }
+            } catch (InterruptedException e) {
+                Log.i(TAG, "Test interrupted!");
+            }
+        }
+    }
+
     /**
      * Place a new outgoing call via the {@link MockConnectionService}
      */
@@ -186,7 +257,7 @@
      * calls if multiple calls to the same number are placed within a short period of time which
      * can cause certain tests to fail.
      */
-    private Uri getTestNumber() {
+    Uri getTestNumber() {
         return Uri.fromParts("tel", String.valueOf(sCounter++), null);
     }
 
@@ -216,7 +287,8 @@
 
                     @Override
                     public Object actual() {
-                        return incallService.getCallAudioState().isMuted();
+                        final CallAudioState state = incallService.getCallAudioState();
+                        return state == null ? null : state.isMuted();
                     }
                 },
                 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
@@ -234,7 +306,8 @@
 
                     @Override
                     public Object actual() {
-                        return connection.getCallAudioState().isMuted();
+                        final CallAudioState state = connection.getCallAudioState();
+                        return state == null ? null : state.isMuted();
                     }
                 },
                 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
@@ -252,7 +325,8 @@
 
                     @Override
                     public Object actual() {
-                        return incallService.getCallAudioState().getRoute();
+                        final CallAudioState state = incallService.getCallAudioState();
+                        return state == null ? null : state.getRoute();
                     }
                 },
                 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
@@ -270,7 +344,8 @@
 
                     @Override
                     public Object actual() {
-                        return connection.getCallAudioState().getRoute();
+                        final CallAudioState state = connection.getCallAudioState();
+                        return state == null ? null : state.getRoute();
                     }
                 },
                 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
@@ -278,6 +353,24 @@
         );
     }
 
+    void assertConnectionState(final Connection connection, final int state) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return state;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return connection.getState();
+                    }
+                },
+                WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Connection should be in state " + state
+        );
+    }
+
     void assertCallState(final Call call, final int state) {
         waitUntilConditionIsTrueOrTimeout(
                 new Condition() {
diff --git a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
index 670281f..c4ec5c4 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
@@ -34,17 +34,14 @@
         super.setUp();
         if (shouldTestTelecom(mContext)) {
             placeAndVerifyCall();
-            verifyConnectionService();
+            verifyConnectionForOutgoingCall();
         }
     }
 
     @Override
     protected void tearDown() throws Exception {
         if (shouldTestTelecom(mContext)) {
-            if (mInCallCallbacks != null && mInCallCallbacks.getService() != null) {
-                mInCallCallbacks.getService().disconnectLastCall();
-                assertNumCalls(mInCallCallbacks.getService(), 0);
-            }
+            cleanupAndVerifyUnbind();
         }
         super.tearDown();
     }
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/MockConnectionService.java
index 2b54f32..cf842b6 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockConnectionService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockConnectionService.java
@@ -16,6 +16,7 @@
 
 package android.telecom.cts;
 
+import android.content.Intent;
 import android.telecom.Connection;
 import android.telecom.ConnectionRequest;
 import android.telecom.ConnectionService;
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
index cecc603..3c48ddd 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
@@ -16,6 +16,7 @@
 
 package android.telecom.cts;
 
+import android.content.Intent;
 import android.telecom.Call;
 import android.telecom.InCallService;
 
@@ -31,6 +32,7 @@
     public static abstract class InCallServiceCallbacks {
         private MockInCallService mService;
         public Semaphore lock = new Semaphore(0);
+        public Semaphore unbindLock = null;
 
         public void onCallAdded(Call call, int numCalls) {};
         public void onCallRemoved(Call call, int numCalls) {};
@@ -43,6 +45,10 @@
         final public void setService(MockInCallService service) {
             mService = service;
         }
+
+        final public void prepareForUnbind() {
+            unbindLock = new Semaphore(0);
+        }
     }
 
     private Call.Callback mCallCallback = new Call.Callback() {
@@ -63,6 +69,14 @@
     }
 
     @Override
+    public boolean onUnbind(Intent intent) {
+        if (getCallbacks() != null && getCallbacks().unbindLock != null) {
+            getCallbacks().unbindLock.release();
+        }
+        return super.onUnbind(intent);
+    }
+
+    @Override
     public void onCallAdded(Call call) {
         if (!mCalls.contains(call)) {
             mCalls.add(call);
diff --git a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
index bc010a8..5895267 100644
--- a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
@@ -16,6 +16,8 @@
 
 package android.telecom.cts;
 
+import static android.telecom.cts.TestUtils.shouldTestTelecom;
+
 import android.os.Bundle;
 import android.telecom.CallAudioState;
 import android.telecom.TelecomManager;
@@ -26,15 +28,9 @@
 public class OutgoingCallTest extends BaseTelecomTestWithMockServices {
 
     @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
-    @Override
     protected void tearDown() throws Exception {
-        if (mInCallCallbacks != null && mInCallCallbacks.getService() != null) {
-            mInCallCallbacks.getService().disconnectLastCall();
-            assertNumCalls(mInCallCallbacks.getService(), 0);
+        if (shouldTestTelecom(mContext)) {
+            cleanupAndVerifyUnbind();
         }
         super.tearDown();
     }
@@ -59,7 +55,7 @@
         final Bundle extras = new Bundle();
         extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true);
         placeAndVerifyCall(extras);
-        verifyConnectionService();
+        verifyConnectionForOutgoingCall();
         assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_SPEAKER);
     }
 
@@ -67,13 +63,13 @@
         final Bundle extras = new Bundle();
         extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
         placeAndVerifyCall(extras);
-        verifyConnectionService();
+        verifyConnectionForOutgoingCall();
         assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
     }
 
     public void testStartCallWithSpeakerphoneNotProvided_SpeakerphoneOffByDefault() {
         placeAndVerifyCall();
-        verifyConnectionService();
+        verifyConnectionForOutgoingCall();
         assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
     }
 }