Add Self-Managed CTS Verifier Test.
Adding CTS to ensure incoming call ui for self-managed calls is shown.
Test: THIS is a test.
Bug: 36098357
Change-Id: Icc6be41a19035444121d98927059e84f2e9d7f95
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index fea3b2b..bf4e30b 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -69,6 +69,9 @@
<!-- Needed by the Audio Quality Verifier to store the sound samples that will be mailed. -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <!-- Needed for Telecom self-managed ConnectionService tests. -->
+ <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
+
<application android:label="@string/app_name"
android:icon="@drawable/icon"
android:backupAgent="VerifierBackupAgent"
@@ -3011,6 +3014,22 @@
<meta-data
android:name="test_required_features"
android:value="android.hardware.telephony"/>
+ </activity>
+
+ <activity
+ android:name=".telecom.SelfManagedIncomingCallTestActivity"
+ android:label="@string/telecom_incoming_self_mgd_test">
+ <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_telecom"/>
+ <meta-data
+ android:name="test_required_features"
+ android:value="android.hardware.telephony"/>
</activity>
<activity
diff --git a/apps/CtsVerifier/res/layout/telecom_self_managed_answer.xml b/apps/CtsVerifier/res/layout/telecom_self_managed_answer.xml
new file mode 100644
index 0000000..27aa55a
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/telecom_self_managed_answer.xml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/telecom_incoming_self_mgd_info"/>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/js_padding"
+ android:layout_marginBottom="@dimen/js_padding">
+
+ <ImageView
+ android:id="@+id/step_1_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/fs_indeterminate"
+ android:layout_marginRight="@dimen/js_padding"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true" />
+ <TextView
+ android:id="@+id/step_1_instructions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/telecom_incoming_self_mgd_step_1"
+ android:textSize="16dp"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@id/step_1_status" />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/step_1_instructions"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_toRightOf="@id/step_1_status"
+ android:id="@+id/telecom_incoming_self_mgd_register_button"
+ android:text="@string/telecom_incoming_self_mgd_register_button"/>
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/js_padding"
+ android:layout_marginBottom="@dimen/js_padding">
+
+ <ImageView
+ android:id="@+id/step_2_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/fs_indeterminate"
+ android:layout_marginRight="@dimen/js_padding"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true" />
+ <TextView
+ android:id="@+id/step_2_instructions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/telecom_incoming_self_mgd_step_2"
+ android:textSize="16dp"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@id/step_2_status" />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/step_2_instructions"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_toRightOf="@id/step_2_status"
+ android:id="@+id/telecom_incoming_self_mgd_show_ui_button"
+ android:text="@string/telecom_incoming_self_mgd_show_ui_button"/>
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/js_padding"
+ android:layout_marginBottom="@dimen/js_padding">
+
+ <ImageView
+ android:id="@+id/step_3_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/fs_indeterminate"
+ android:layout_marginRight="@dimen/js_padding"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true" />
+ <TextView
+ android:id="@+id/step_3_instructions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/telecom_incoming_self_mgd_step_3"
+ android:textSize="16dp"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@id/step_3_status" />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/step_3_instructions"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_toRightOf="@id/step_3_status"
+ android:id="@+id/telecom_incoming_self_mgd_confirm_answer_button"
+ android:text="@string/telecom_incoming_self_mgd_confirm_answer_button"/>
+ </RelativeLayout>
+
+ <include layout="@layout/pass_fail_buttons" />
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index f08bc30..f34bf48 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -3831,4 +3831,22 @@
audio was audible.
</string>
<string name="telecom_incoming_call_confirm_button">Confirm</string>
+ <string name="telecom_incoming_self_mgd_test"> Incoming Self-Managed Connection Test</string>
+ <string name="telecom_incoming_self_mgd_info">
+ This test verifies that incoming calls from a Self-Managed Connection Service will trigger
+ a Telecom-managed incoming call UI when there is already an ongoing call on the device.
+ </string>
+ <string name="telecom_incoming_self_mgd_step_1">
+ Click the button below to register a test self-managed ConnectionService.
+ </string>
+ <string name="telecom_incoming_self_mgd_register_button">Register Self-Managed ConnectionService</string>
+ <string name="telecom_incoming_self_mgd_step_2">
+ Click the button below to test that the system incoming call notification shows. When the
+ notification shows, "answer" the call.
+ </string>
+ <string name="telecom_incoming_self_mgd_show_ui_button">Show System Incoming UI</string>
+ <string name="telecom_incoming_self_mgd_step_3">
+ Click the button below to confirm that the incoming call was answered.
+ </string>
+ <string name="telecom_incoming_self_mgd_confirm_answer_button">Confirm Answer</string>
</resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnection.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnection.java
index c935841..c004b73 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnection.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnection.java
@@ -20,12 +20,16 @@
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaPlayer;
+import android.telecom.CallAudioState;
import android.telecom.Connection;
import android.telecom.DisconnectCause;
import android.telecom.VideoProfile;
import com.android.cts.verifier.R;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* An implementation of the {@link android.telecom.Connection} class used by the
* {@link CtsConnectionService}.
@@ -51,6 +55,7 @@
private final Listener mListener;
private final MediaPlayer mMediaPlayer;
private final Context mContext;
+ private CountDownLatch mWaitForCallAudioStateChanged = new CountDownLatch(1);
public CtsConnection(Context context, boolean isIncomingCall,
Listener listener, boolean hasAudio) {
@@ -131,6 +136,20 @@
}
}
+ public void onCallAudioStateChanged(CallAudioState state) {
+ mWaitForCallAudioStateChanged.countDown();
+ mWaitForCallAudioStateChanged = new CountDownLatch(1);
+
+ }
+
+ public void waitForAudioStateChanged() {
+ try {
+ mWaitForCallAudioStateChanged.await(CtsConnectionService.TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ }
+ }
+
private void setDisconnectedAndDestroy(DisconnectCause cause) {
setDisconnected(cause);
destroy();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnectionService.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnectionService.java
index 528c221..7cfcbf8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnectionService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnectionService.java
@@ -26,35 +26,74 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* CTS Verifier ConnectionService implementation.
*/
public class CtsConnectionService extends ConnectionService {
+ static final int TIMEOUT_MILLIS = 10000;
private CtsConnection.Listener mConnectionListener =
new CtsConnection.Listener() {
@Override
void onDestroyed(CtsConnection connection) {
- mConnections.remove(connection);
+ synchronized (mConnectionsLock) {
+ mConnections.remove(connection);
+ }
}
};
private static CtsConnectionService sConnectionService;
+ private static CountDownLatch sBindingLatch = new CountDownLatch(1);
private List<CtsConnection> mConnections = new ArrayList<>();
+ private Object mConnectionsLock = new Object();
+ private CountDownLatch mConnectionLatch = new CountDownLatch(1);
public static CtsConnectionService getConnectionService() {
return sConnectionService;
}
+ public static CtsConnectionService waitForAndGetConnectionService() {
+ if (sConnectionService == null) {
+ try {
+ sBindingLatch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ }
+ }
+ return sConnectionService;
+ }
+
public CtsConnectionService() throws Exception {
super();
sConnectionService = this;
+ if (sBindingLatch != null) {
+ sBindingLatch.countDown();
+ }
+ sBindingLatch = new CountDownLatch(1);
}
public List<CtsConnection> getConnections() {
- return mConnections;
+ synchronized (mConnectionsLock) {
+ return new ArrayList<CtsConnection>(mConnections);
+ }
+ }
+
+ public CtsConnection waitForAndGetConnection() {
+ try {
+ mConnectionLatch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ }
+ mConnectionLatch = new CountDownLatch(1);
+ synchronized (mConnectionsLock) {
+ if (mConnections.size() > 0) {
+ return mConnections.get(0);
+ } else {
+ return null;
+ }
+ }
}
@Override
@@ -109,7 +148,12 @@
connection.putExtras(moreExtras);
connection.setVideoState(request.getVideoState());
- mConnections.add(connection);
+ synchronized (mConnectionsLock) {
+ mConnections.add(connection);
+ }
+ if (mConnectionLatch != null) {
+ mConnectionLatch.countDown();
+ }
return connection;
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/PhoneAccountUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/PhoneAccountUtils.java
index aa8e961..b559373 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/PhoneAccountUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/PhoneAccountUtils.java
@@ -57,6 +57,19 @@
.setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
.build();
+ public static final String TEST_SELF_MAANGED_PHONE_ACCOUNT2_ID = "selfMgdTest2";
+ public static final String TEST_SELF_MANAGED_PHONE_ACCOUNT2_LABEL = "CTSVerifier2";
+
+ public static final PhoneAccountHandle TEST_SELF_MANAGED_PHONE_ACCOUNT_HANDLE_2 =
+ new PhoneAccountHandle(new ComponentName(
+ PassFailButtons.class.getPackage().getName(),
+ CtsConnectionService.class.getName()), TEST_SELF_MAANGED_PHONE_ACCOUNT2_ID);
+ public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_2 = new PhoneAccount.Builder(
+ TEST_SELF_MANAGED_PHONE_ACCOUNT_HANDLE_2, TEST_SELF_MANAGED_PHONE_ACCOUNT2_LABEL)
+ .setAddress(TEST_SELF_MANAGED_PHONE_ACCOUNT_ADDRESS)
+ .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
+ .build();
+
/**
* Registers the test phone account.
* @param context The context.
@@ -96,6 +109,7 @@
TelecomManager telecomManager = (TelecomManager) context.getSystemService(
Context.TELECOM_SERVICE);
telecomManager.registerPhoneAccount(TEST_SELF_MANAGED_PHONE_ACCOUNT);
+ telecomManager.registerPhoneAccount(TEST_SELF_MANAGED_PHONE_ACCOUNT_2);
}
/**
@@ -114,4 +128,15 @@
Context.TELECOM_SERVICE);
return telecomManager.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
}
+
+ /**
+ * Retrieves the test phone account, or null if not registered.
+ * @param context The context.
+ * @return The Phone Account.
+ */
+ public static PhoneAccount getSelfManagedPhoneAccount2(Context context) {
+ TelecomManager telecomManager = (TelecomManager) context.getSystemService(
+ Context.TELECOM_SERVICE);
+ return telecomManager.getPhoneAccount(TEST_SELF_MANAGED_PHONE_ACCOUNT_HANDLE_2);
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/SelfManagedIncomingCallTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/SelfManagedIncomingCallTestActivity.java
new file mode 100644
index 0000000..530b246
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/SelfManagedIncomingCallTestActivity.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 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.telecom;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.telecom.Connection;
+import android.telecom.PhoneAccount;
+import android.telecom.TelecomManager;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.List;
+
+/**
+ * This test verifies functionality associated with the Self-Managed
+ * {@link android.telecom.ConnectionService} APIs. It ensures that Telecom will show an incoming
+ * call UI when a new incoming self-managed call is added when there is already an ongoing managed
+ * call or when there is an ongoing self-managed call in another app.
+ */
+public class SelfManagedIncomingCallTestActivity extends PassFailButtons.Activity {
+ private Uri TEST_DIAL_NUMBER_1 = Uri.fromParts("tel", "6505551212", null);
+ private Uri TEST_DIAL_NUMBER_2 = Uri.fromParts("tel", "4085551212", null);
+
+ private ImageView mStep1Status;
+ private Button mRegisterPhoneAccount;
+ private ImageView mStep2Status;
+ private Button mShowUi;
+ private ImageView mStep3Status;
+ private Button mConfirm;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ View view = getLayoutInflater().inflate(R.layout.telecom_self_managed_answer, null);
+ setContentView(view);
+ setInfoResources(R.string.telecom_incoming_self_mgd_test,
+ R.string.telecom_incoming_self_mgd_info, -1);
+ setPassFailButtonClickListeners();
+ getPassButton().setEnabled(false);
+
+ mStep1Status = view.findViewById(R.id.step_1_status);
+ mRegisterPhoneAccount = view.findViewById(R.id.telecom_incoming_self_mgd_register_button);
+ mRegisterPhoneAccount.setOnClickListener(v -> {
+ PhoneAccountUtils.registerTestSelfManagedPhoneAccount(this);
+ PhoneAccount account = PhoneAccountUtils.getSelfManagedPhoneAccount(this);
+ PhoneAccount account2 = PhoneAccountUtils.getSelfManagedPhoneAccount2(this);
+ if (account != null &&
+ account.isEnabled() &&
+ account.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED) &&
+ account2 != null &&
+ account2.isEnabled() &&
+ account2.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)) {
+ mRegisterPhoneAccount.setEnabled(false);
+ mShowUi.setEnabled(true);
+ mStep1Status.setImageResource(R.drawable.fs_good);
+ } else {
+ mStep1Status.setImageResource(R.drawable.fs_error);
+ }
+ });
+
+ mStep2Status = view.findViewById(R.id.step_2_status);
+ mShowUi = view.findViewById(R.id.telecom_incoming_self_mgd_show_ui_button);
+ mShowUi.setOnClickListener(v -> {
+ (new AsyncTask<Void, Void, Throwable>() {
+ @Override
+ protected Throwable doInBackground(Void... params) {
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
+ TEST_DIAL_NUMBER_1);
+ TelecomManager telecomManager =
+ (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
+ if (telecomManager == null) {
+ mStep2Status.setImageResource(R.drawable.fs_error);
+ return new Throwable("Could not get telecom service.");
+ }
+ telecomManager.addNewIncomingCall(
+ PhoneAccountUtils.TEST_SELF_MANAGED_PHONE_ACCOUNT_HANDLE, extras);
+
+ CtsConnectionService ctsConnectionService =
+ CtsConnectionService.waitForAndGetConnectionService();
+ if (ctsConnectionService == null) {
+ mStep2Status.setImageResource(R.drawable.fs_error);
+ return new Throwable("Could not get connection service.");
+ }
+
+ CtsConnection connection = ctsConnectionService.waitForAndGetConnection();
+ if (connection == null) {
+ mStep2Status.setImageResource(R.drawable.fs_error);
+ return new Throwable("Could not get connection.");
+ }
+ // Wait until the connection knows its audio state changed; at this point
+ // Telecom knows about the connection and can answer.
+ connection.waitForAudioStateChanged();
+ // Make it active to simulate an answer.
+ connection.setActive();
+
+ // Place the second call. It should trigger the incoming call UX.
+ Bundle extras2 = new Bundle();
+ extras2.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
+ TEST_DIAL_NUMBER_2);
+ telecomManager.addNewIncomingCall(
+ PhoneAccountUtils.TEST_SELF_MANAGED_PHONE_ACCOUNT_HANDLE_2,
+ extras2);
+
+ return null;
+ } catch (Throwable t) {
+ return t;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Throwable t) {
+ if (t == null) {
+ mStep2Status.setImageResource(R.drawable.fs_good);
+ mShowUi.setEnabled(false);
+ mConfirm.setEnabled(true);
+ } else {
+ mStep2Status.setImageResource(R.drawable.fs_error);
+ }
+ }
+ }).execute();
+
+
+ });
+
+ mStep3Status = view.findViewById(R.id.step_2_status);
+ mConfirm = view.findViewById(R.id.telecom_incoming_self_mgd_confirm_answer_button);
+ mConfirm.setOnClickListener(v -> {
+ CtsConnectionService ctsConnectionService = CtsConnectionService.getConnectionService();
+ if (ctsConnectionService == null) {
+ mStep3Status.setImageResource(R.drawable.fs_error);
+ return;
+ }
+ List<CtsConnection> connections = ctsConnectionService.getConnections();
+ if (connections.size() != 1) {
+ mStep3Status.setImageResource(R.drawable.fs_error);
+ return;
+ }
+
+ if (connections.get(0).getState() == Connection.STATE_ACTIVE) {
+ mStep3Status.setImageResource(R.drawable.fs_good);
+ getPassButton().setEnabled(true);
+ } else {
+ mStep3Status.setImageResource(R.drawable.fs_error);
+ }
+ });
+
+ mShowUi.setEnabled(false);
+ mConfirm.setEnabled(false);
+ }
+}