Add more CTS tests around Telecomm flows

Add additional Telecomm CTS tests which verify both failed calls as
well as basic RemoteConnection behavior.

Bug: 16431051
Bug: 16844815
Change-Id: I16e4699347bf128cfd8ea27feae751d0121399db
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 5f6dd0d..17071e3 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1159,7 +1159,41 @@
             <meta-data android:name="test_required_features" android:value="android.hardware.telephony"/>
         </activity>
 
-        <service android:name=".telecomm.CancelCallTestActivity$CancellingConnectionService"
+        <service android:name=".telecomm.CancelCallTestActivity$ConnectionService"
+                 android:permission="android.permission.BIND_CONNECTION_SERVICE">
+            <intent-filter>
+                <action android:name="android.telecomm.ConnectionService" />
+            </intent-filter>
+        </service>
+
+        <activity android:name=".telecomm.FailedCallTestActivity"
+                  android:label="@string/telecomm_failed_call_title">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_telecomm" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.telecomm.TelecommTestActivity" />
+        </activity>
+
+        <service android:name=".telecomm.FailedCallTestActivity$ConnectionService"
+                 android:permission="android.permission.BIND_CONNECTION_SERVICE">
+            <intent-filter>
+                <action android:name="android.telecomm.ConnectionService" />
+            </intent-filter>
+        </service>
+
+        <activity android:name=".telecomm.BasicCallTestActivity"
+                  android:label="@string/telecomm_basic_call_title">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_telecomm" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.telecomm.TelecommTestActivity" />
+        </activity>
+
+        <service android:name=".telecomm.BasicCallTestActivity$ConnectionService"
                  android:permission="android.permission.BIND_CONNECTION_SERVICE">
             <intent-filter>
                 <action android:name="android.telecomm.ConnectionService" />
diff --git a/apps/CtsVerifier/res/layout/telecomm_test_activity.xml b/apps/CtsVerifier/res/layout/telecomm_test_activity.xml
index e5cebe1..c885f8a 100644
--- a/apps/CtsVerifier/res/layout/telecomm_test_activity.xml
+++ b/apps/CtsVerifier/res/layout/telecomm_test_activity.xml
@@ -22,6 +22,11 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/telecomm_open_settings" />
+    <EditText android:id="@+id/phone_number"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:text="@string/telecomm_default_phone_number"
+              android:hint="@string/telecomm_phone_number_hint" />
     <Button android:id="@+id/simulate_call"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 4882946..b4097a3 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1128,10 +1128,22 @@
     </string>
     <string name="telecomm_open_settings">Open settings</string>
     <string name="telecomm_simulate_call">Place call</string>
+    <string name="telecomm_default_phone_number">555-263-7643</string>
+    <string name="telecomm_phone_number_hint">Phone number to call</string>
     <string name="telecomm_cancel_call_title">Canceled Call Test</string>
     <string name="telecomm_cancel_call_info">This tests to make sure that canceled calls are handled correctly.
         Press the \"Open settings\" button and make sure that \"Call Cancel Manager\" is the default Wi-Fi calling account.
         Then press the \"Place call\" button; the test will pass (or fail) automatically.
     </string>
+    <string name="telecomm_failed_call_title">Failed Call Test</string>
+    <string name="telecomm_failed_call_info">This tests to make sure that failed calls are handled correctly.
+        Press the \"Open settings\" button and make sure that \"Call Failed Manager\" is the default Wi-Fi calling account.
+        Then press the \"Place call\" button; the test will pass (or fail) automatically.
+    </string>
+    <string name="telecomm_basic_call_title">Basic Call Test</string>
+    <string name="telecomm_basic_call_info">This tests to make sure that basic calls are handled correctly.
+        Press the \"Open settings\" button and make sure that \"Basic Call Manager\" is the default Wi-Fi calling account.
+        Then press the \"Place call\" button; the test will pass (or fail) automatically.
+    </string>
 
 </resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecomm/BasicCallTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecomm/BasicCallTestActivity.java
new file mode 100644
index 0000000..6e5fab2
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecomm/BasicCallTestActivity.java
@@ -0,0 +1,151 @@
+package com.android.cts.verifier.telecomm;
+
+import com.android.cts.verifier.R;
+
+import android.net.Uri;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.telecomm.Connection;
+import android.telecomm.ConnectionRequest;
+import android.telecomm.PhoneAccountHandle;
+import android.telecomm.RemoteConnection;
+import android.telecomm.StatusHints;
+import android.util.Log;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests that a basic call with RemoteConnections will go through. When this ConnectionService is
+ * notified of an outgoing call, it will create a Connection which wraps a RemoteConnection. Then,
+ * once the RemoteConnection starts dialing, it will disconnect the call and the test will pass.
+ */
+public class BasicCallTestActivity extends TelecommBaseTestActivity {
+    private static final Semaphore sLock = new Semaphore(0);
+
+    @Override
+    protected int getTestTitleResource() {
+        return R.string.telecomm_basic_call_title;
+    }
+
+    @Override
+    protected int getTestInfoResource() {
+        return R.string.telecomm_basic_call_info;
+    }
+
+    @Override
+    protected Class<? extends android.telecomm.ConnectionService> getConnectionService() {
+        return ConnectionService.class;
+    }
+
+    @Override
+    protected String getConnectionServiceLabel() {
+        return "Basic Call Manager";
+    }
+
+    @Override
+    protected boolean onCallPlacedBackgroundThread() {
+        try {
+            if (!sLock.tryAcquire(5000, TimeUnit.MILLISECONDS)) {
+                return false;
+            }
+
+            // Wait for the listeners to be fired so the call is cleaned up.
+            SystemClock.sleep(1000);
+
+            return !getTelecommManager().isInCall();
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    public static class ConnectionService extends android.telecomm.ConnectionService {
+        @Override
+        public Connection onCreateOutgoingConnection(
+                PhoneAccountHandle connectionManagerPhoneAccount,
+                ConnectionRequest request) {
+            RemoteConnection remoteConnection =
+                    createRemoteOutgoingConnection(connectionManagerPhoneAccount, request);
+            return new ProxyConnection(remoteConnection);
+        }
+    }
+
+    private static class ProxyConnection extends Connection {
+        private final RemoteConnection mRemoteConnection;
+
+        private final RemoteConnection.Listener mListener = new RemoteConnection.Listener() {
+            @Override
+            public void onStateChanged(RemoteConnection connection, int state) {
+                switch (state) {
+                    case Connection.STATE_ACTIVE:
+                        setActive();
+                        break;
+                    case Connection.STATE_DIALING:
+                        setDialing();
+                        break;
+                    case Connection.STATE_DISCONNECTED:
+                        sLock.release();
+                        break;
+                    case Connection.STATE_HOLDING:
+                        setOnHold();
+                        break;
+                    case Connection.STATE_INITIALIZING:
+                        setInitializing();
+                        break;
+                    case Connection.STATE_NEW:
+                        setInitialized();
+                        break;
+                    case Connection.STATE_RINGING:
+                        setRinging();
+                        break;
+                }
+            }
+
+            @Override
+            public void onDisconnected(RemoteConnection connection, int disconnectCauseCode,
+                    String disconnectCauseMessage) {
+                setDisconnected(disconnectCauseCode, disconnectCauseMessage);
+            }
+
+            @Override
+            public void onStatusHintsChanged(RemoteConnection connection, StatusHints statusHints) {
+                setStatusHints(statusHints);
+            }
+
+            @Override
+            public void onHandleChanged(RemoteConnection connection, Uri handle, int presentation) {
+                setHandle(handle, presentation);
+            }
+
+            @Override
+            public void onCallerDisplayNameChanged(RemoteConnection connection,
+                    String callerDisplayName,
+                    int presentation) {
+                setCallerDisplayName(callerDisplayName, presentation);
+            }
+
+            @Override
+            public void onDestroyed(RemoteConnection connection) {
+                destroy();
+            }
+        };
+
+        public ProxyConnection(RemoteConnection connection) {
+            mRemoteConnection = connection;
+            if (connection.getState() == Connection.STATE_DISCONNECTED) {
+                sLock.release();
+            } else {
+                mRemoteConnection.addListener(mListener);
+            }
+        }
+
+        @Override
+        public void onSetState(int state) {
+            if (state == Connection.STATE_DIALING) {
+                // Good enough; let's disconnect this call.
+                mRemoteConnection.disconnect();
+                sLock.release();
+            }
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecomm/CancelCallTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecomm/CancelCallTestActivity.java
index 088cf49..d3fb0bf 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/telecomm/CancelCallTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecomm/CancelCallTestActivity.java
@@ -16,106 +16,62 @@
 
 package com.android.cts.verifier.telecomm;
 
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
 import android.os.SystemClock;
 import android.telecomm.Connection;
 import android.telecomm.ConnectionRequest;
-import android.telecomm.ConnectionService;
-import android.telecomm.PhoneAccount;
 import android.telecomm.PhoneAccountHandle;
-import android.telecomm.TelecommManager;
-import android.view.View;
+
+import com.android.cts.verifier.R;
 
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
-public class CancelCallTestActivity extends PassFailButtons.Activity {
+/**
+ * Tests that a call can be canceled by a ConnectionService (and that the cancelation is respected).
+ * Once the ConnectionService returns a canceled connection, the test verifies that Telecomm does
+ * not have any active calls. If this is the case, the test will pass.
+ */
+public class CancelCallTestActivity extends TelecommBaseTestActivity {
     private static final Semaphore sLock = new Semaphore(0);
 
-    private PhoneAccountHandle mPhoneAccountHandle;
-
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        setResult(RESULT_CANCELED);
-        setContentView(R.layout.telecomm_test_activity);
-        setPassFailButtonClickListeners();
-        setInfoResources(R.string.telecomm_cancel_call_title, R.string.telecomm_cancel_call_info, 0);
-
-        findViewById(R.id.open_settings).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mPhoneAccountHandle = new PhoneAccountHandle(
-                        new ComponentName(CancelCallTestActivity.this,
-                                CancellingConnectionService.class),
-                        getClass().getSimpleName()
-                );
-                PhoneAccount account = new PhoneAccount.Builder(mPhoneAccountHandle,
-                                                                "Call Cancel Manager")
-                        .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
-                        .build();
-
-                getTelecommManager().registerPhoneAccount(account);
-
-                Intent i = new Intent(Intent.ACTION_MAIN)
-                        .setClassName("com.android.telecomm",
-                                "com.android.telecomm.PhoneAccountPreferencesActivity");
-                startActivity(i);
-            }
-        });
-
-        findViewById(R.id.simulate_call).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                new Thread() {
-                    @Override
-                    public void run() {
-                        super.run();
-
-                        Intent call = new Intent(Intent.ACTION_CALL);
-                        call.setData(Uri.parse("tel:5552637643"));
-                        startActivity(call);
-                        try {
-                            if (!sLock.tryAcquire(1000, TimeUnit.MILLISECONDS)) {
-                                setTestResultAndFinish(false);
-                                return;
-                            }
-
-                            // Wait for the listeners to be fired so the call is cleaned up.
-                            SystemClock.sleep(1000);
-
-                            // Make sure that there aren't any ongoing calls.
-                            setTestResultAndFinish(!getTelecommManager().isInCall());
-                        } catch (Exception e) {
-                            setTestResultAndFinish(false);
-                            return;
-                        }
-                    }
-                }.start();
-            }
-        });
-    }
-
-    private TelecommManager getTelecommManager() {
-        return (TelecommManager) getSystemService(TELECOMM_SERVICE);
+    protected int getTestTitleResource() {
+        return R.string.telecomm_cancel_call_title;
     }
 
     @Override
-    public void setTestResultAndFinish(boolean passed) {
-        super.setTestResultAndFinish(passed);
-        if (mPhoneAccountHandle != null) {
-            getTelecommManager().unregisterPhoneAccount(mPhoneAccountHandle);
+    protected int getTestInfoResource() {
+        return R.string.telecomm_cancel_call_info;
+    }
+
+    @Override
+    protected Class<? extends android.telecomm.ConnectionService> getConnectionService() {
+        return ConnectionService.class;
+    }
+
+    @Override
+    protected String getConnectionServiceLabel() {
+        return "Call Cancel Manager";
+    }
+
+    @Override
+    protected boolean onCallPlacedBackgroundThread() {
+        try {
+            if (!sLock.tryAcquire(1000, TimeUnit.MILLISECONDS)) {
+                return false;
+            }
+
+            // Wait for the listeners to be fired so the call is cleaned up.
+            SystemClock.sleep(1000);
+
+            // Make sure that there aren't any ongoing calls.
+            return !getTelecommManager().isInCall();
+        } catch (Exception e) {
+            return false;
         }
     }
 
-    public static class CancellingConnectionService extends ConnectionService {
+    public static class ConnectionService extends android.telecomm.ConnectionService {
         @Override
         public Connection onCreateOutgoingConnection(
                 PhoneAccountHandle connectionManagerPhoneAccount,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecomm/FailedCallTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecomm/FailedCallTestActivity.java
new file mode 100644
index 0000000..86137f6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecomm/FailedCallTestActivity.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 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.cts.verifier.telecomm;
+
+import android.os.SystemClock;
+import android.telecomm.Connection;
+import android.telecomm.ConnectionRequest;
+import android.telecomm.PhoneAccountHandle;
+import android.telephony.DisconnectCause;
+
+import com.android.cts.verifier.R;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests that a connection manager can fail calls and they're handled appropriately. That is, when
+ * a call is failed, it will not go through. The flow here is that the ConnectionService will say
+ * that the call failed because it's busy, and then make sure that there are no active calls. If
+ * this is the case, the test will pass.
+ */
+public class FailedCallTestActivity extends TelecommBaseTestActivity {
+    private static final Semaphore sLock = new Semaphore(0);
+
+    @Override
+    protected int getTestTitleResource() {
+        return R.string.telecomm_failed_call_title;
+    }
+
+    @Override
+    protected int getTestInfoResource() {
+        return R.string.telecomm_failed_call_info;
+    }
+
+    @Override
+    protected Class<? extends android.telecomm.ConnectionService> getConnectionService() {
+        return ConnectionService.class;
+    }
+
+    @Override
+    protected String getConnectionServiceLabel() {
+        return "Call Failed Manager";
+    }
+
+    @Override
+    protected boolean onCallPlacedBackgroundThread() {
+        try {
+            if (!sLock.tryAcquire(1000, TimeUnit.MILLISECONDS)) {
+                return false;
+            }
+
+            // Wait for the listeners to be fired so the call is cleaned up.
+            SystemClock.sleep(1000);
+
+            // Make sure that there aren't any ongoing calls.
+            return !getTelecommManager().isInCall();
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    public static class ConnectionService extends android.telecomm.ConnectionService {
+        @Override
+        public Connection onCreateOutgoingConnection(
+                PhoneAccountHandle connectionManagerPhoneAccount,
+                ConnectionRequest request) {
+            sLock.release();
+            return Connection.createFailedConnection(DisconnectCause.BUSY,
+                    "Test; no need to continue");
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecomm/TelecommBaseTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecomm/TelecommBaseTestActivity.java
new file mode 100644
index 0000000..512e306
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecomm/TelecommBaseTestActivity.java
@@ -0,0 +1,120 @@
+package com.android.cts.verifier.telecomm;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.telecomm.ConnectionService;
+import android.telecomm.PhoneAccount;
+import android.telecomm.PhoneAccountHandle;
+import android.telecomm.TelecommManager;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.Objects;
+
+public abstract class TelecommBaseTestActivity extends PassFailButtons.Activity {
+    protected PhoneAccountHandle mPhoneAccountHandle;
+
+    private Button mOpenSettingsBtn;
+    private Button mPlaceCallBtn;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setResult(RESULT_CANCELED);
+        setContentView(R.layout.telecomm_test_activity);
+        setPassFailButtonClickListeners();
+        setInfoResources(getTestTitleResource(), getTestInfoResource(), 0);
+
+        mOpenSettingsBtn = (Button) findViewById(R.id.open_settings);
+        mOpenSettingsBtn.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mPhoneAccountHandle = new PhoneAccountHandle(
+                        new ComponentName(getApplicationContext(), getConnectionService()),
+                        getClass().getSimpleName()
+                );
+                PhoneAccount account = new PhoneAccount.Builder(mPhoneAccountHandle,
+                                getConnectionServiceLabel())
+                        .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
+                        .build();
+
+                getTelecommManager().registerPhoneAccount(account);
+
+                Intent i = new Intent(TelecommManager.ACTION_CHANGE_PHONE_ACCOUNTS);
+                startActivity(i);
+            }
+        });
+
+        mPlaceCallBtn = (Button) findViewById(R.id.simulate_call);
+        mPlaceCallBtn.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                runTest();
+            }
+        });
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        PhoneAccountHandle defaultConnectionManager = getTelecommManager().getConnectionManager();
+        boolean isDefaultConnectionManager = mPhoneAccountHandle != null &&
+                Objects.equals(mPhoneAccountHandle, defaultConnectionManager);
+        mOpenSettingsBtn.setEnabled(!isDefaultConnectionManager);
+        mPlaceCallBtn.setEnabled(isDefaultConnectionManager);
+    }
+
+    abstract protected int getTestTitleResource();
+
+    abstract protected int getTestInfoResource();
+
+    abstract protected Class<? extends ConnectionService> getConnectionService();
+
+    abstract protected String getConnectionServiceLabel();
+
+    /**
+     * Perform any tests once the call has been placed. This is called from a background thread, so
+     * it is safe to block until the result is known.
+     *
+     * @return True if the test passed.
+     */
+    abstract protected boolean onCallPlacedBackgroundThread();
+
+    protected void runTest() {
+        new Thread() {
+            @Override
+            public void run() {
+                EditText phoneNumberEdit = (EditText) findViewById(R.id.phone_number);
+                String numberText = phoneNumberEdit.getText().toString();
+                Uri number = Uri.fromParts("tel", numberText, null);
+
+                Intent call = new Intent(Intent.ACTION_CALL);
+                call.setData(number);
+                startActivity(call);
+
+                boolean passed = onCallPlacedBackgroundThread();
+                setTestResultAndFinish(passed);
+            }
+        }.start();
+    }
+
+    protected TelecommManager getTelecommManager() {
+        return (TelecommManager) getSystemService(TELECOMM_SERVICE);
+    }
+
+    @Override
+    public void setTestResultAndFinish(boolean passed) {
+        super.setTestResultAndFinish(passed);
+        if (mPhoneAccountHandle != null) {
+            getTelecommManager().unregisterPhoneAccount(mPhoneAccountHandle);
+        }
+    }
+}