Self Managed CS implementation.
Implementation for self-managed connection service APIs.
Add ability for self-managed CS to:
- register a phone account and have it be auto-enabled.
- add ability for self-managed calls, ensuring only self-manage calls
can be added.
Add implementations for new isXCallPermitted APIs.
Filter self-managed calls from InCallServices.
Ensure self managed connection creation doesn't use emergency call CS.
Add ability to set audio route from self-managed connection
Overhaul some of the CallsManager getCallsWithState methods to support
other use cases for self-managed calls (also use steams/filters.. Ooo aah)
Test: Manual
Bug: 34159263
Change-Id: I3131fd48ee5c5aa36c0e88992fa51879af07d495
diff --git a/testapps/AndroidManifest.xml b/testapps/AndroidManifest.xml
index c1eb258..6c7202b 100644
--- a/testapps/AndroidManifest.xml
+++ b/testapps/AndroidManifest.xml
@@ -25,6 +25,7 @@
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE" />
+ <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.REGISTER_CALL_PROVIDER" />
@@ -155,6 +156,24 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
+ </activity>
+
+ <activity android:name="com.android.server.telecom.testapps.SelfManagedCallingActivity"
+ android:label="@string/selfManagedCallingActivityLabel"
+ android:process="com.android.server.telecom.testapps.SelfMangingCallingApp">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
</activity>
+
+ <service android:name="com.android.server.telecom.testapps.SelfManagedConnectionService"
+ android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
+ android:process="com.android.server.telecom.testapps.SelfMangingCallingApp">
+ <intent-filter>
+ <action android:name="android.telecom.ConnectionService" />
+ </intent-filter>
+ </service>
</application>
</manifest>
diff --git a/testapps/res/layout/self_managed_call_list_item.xml b/testapps/res/layout/self_managed_call_list_item.xml
new file mode 100644
index 0000000..f3be4ad
--- /dev/null
+++ b/testapps/res/layout/self_managed_call_list_item.xml
@@ -0,0 +1,71 @@
+<?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="wrap_content">
+ <TextView
+ android:id="@+id/phoneNumber"
+ android:layout_gravity="left"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="25dp"
+ android:text="TextView" />
+ <TextView
+ android:id="@+id/callState"
+ android:layout_gravity="left"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="25dp"
+ android:text="TextView" />
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Active"
+ android:id="@+id/setActiveButton" />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Hold"
+ android:id="@+id/setHeldButton" />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Disconnect"
+ android:id="@+id/disconnectButton" />
+ </LinearLayout>
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Speaker"
+ android:id="@+id/speakerButton" />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Earpiece"
+ android:id="@+id/earpieceButton" />
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/testapps/res/layout/self_managed_sample_main.xml b/testapps/res/layout/self_managed_sample_main.xml
new file mode 100644
index 0000000..f239997
--- /dev/null
+++ b/testapps/res/layout/self_managed_sample_main.xml
@@ -0,0 +1,81 @@
+<?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="fill_parent"
+ android:layout_height="fill_parent">
+ <CheckBox
+ android:id="@+id/checkIfPermittedBeforeCalling"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/checkIfPermittedBeforeCallingButton" />
+ <TableLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TableRow android:layout_span="2">
+ <RadioGroup>
+ <RadioButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Acct 1"
+ android:id="@+id/useAcct1Button"/>
+ <RadioButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Acct 2"
+ android:id="@+id/useAcct2Button"/>
+ </RadioGroup>
+ </TableRow>
+ <TableRow android:layout_span="2">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Place call:" />
+ </TableRow>
+ <TableRow>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Number:" />
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/phoneNumber" />
+ </TableRow>
+ <TableRow>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Outgoing Call"
+ android:id="@+id/placeOutgoingCallButton" />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Incoming Call"
+ android:id="@+id/placeIncomingCallButton" />
+ </TableRow>
+ <TableRow android:layout_span="2">
+ <ListView
+ android:id="@+id/callList"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:listSelector="@null"
+ android:divider="@null" />
+ </TableRow>
+ </TableLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/testapps/res/raw/sample_audio.ogg b/testapps/res/raw/sample_audio.ogg
new file mode 100644
index 0000000..0129b46
--- /dev/null
+++ b/testapps/res/raw/sample_audio.ogg
Binary files differ
diff --git a/testapps/res/raw/sample_audio2.ogg b/testapps/res/raw/sample_audio2.ogg
new file mode 100644
index 0000000..a0b39b4
--- /dev/null
+++ b/testapps/res/raw/sample_audio2.ogg
Binary files differ
diff --git a/testapps/res/values/donottranslate_strings.xml b/testapps/res/values/donottranslate_strings.xml
index 83d1ef3..74a3fd4 100644
--- a/testapps/res/values/donottranslate_strings.xml
+++ b/testapps/res/values/donottranslate_strings.xml
@@ -49,4 +49,17 @@
<string name="UssdUiAppLabel">Test Ussd UI</string>
<string name="placeUssdButton">Send USSD</string>
+
+ <!-- String for button in SelfManagedCallingActivity. -->
+ <string name="checkIfPermittedBeforeCallingButton">Check if calls permitted before calling</string>
+
+ <string name="selfManagedCallingActivityLabel">Self-Managed Sample</string>
+
+ <string name="outgoingCallNotPermitted">Outgoing call not permitted.</string>
+
+ <string name="outgoingCallNotPermittedCS">Outgoing call not permitted (CS Reported).</string>
+
+ <string name="incomingCallNotPermitted">Incoming call not permitted.</string>
+
+ <string name="incomingCallNotPermittedCS">Incoming call not permitted (CS Reported).</string>
</resources>
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallList.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallList.java
new file mode 100644
index 0000000..e0d7442
--- /dev/null
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallList.java
@@ -0,0 +1,133 @@
+/*
+ * 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.server.telecom.testapps;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.telecom.ConnectionRequest;
+import android.telecom.Log;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.util.ArrayMap;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Manages the list of {@link SelfManagedConnection} active in the sample third-party calling app.
+ */
+public class SelfManagedCallList {
+ public abstract static class Listener {
+ public void onCreateIncomingConnectionFailed(ConnectionRequest request) {};
+ public void onCreateOutgoingConnectionFailed(ConnectionRequest request) {};
+ public void onConnectionListChanged() {};
+ }
+
+ public static String SELF_MANAGED_ACCOUNT_1 = "1";
+ public static String SELF_MANAGED_ACCOUNT_2 = "2";
+
+ private static SelfManagedCallList sInstance;
+ private static ComponentName COMPONENT_NAME = new ComponentName(
+ SelfManagedCallList.class.getPackage().getName(),
+ SelfManagedConnectionService.class.getName());
+ private static Uri SELF_MANAGED_ADDRESS_1 = Uri.fromParts(PhoneAccount.SCHEME_TEL, "555-1212",
+ "");
+ private static Uri SELF_MANAGED_ADDRESS_2 = Uri.fromParts(PhoneAccount.SCHEME_SIP,
+ "me@test.org", "");
+ private static Map<String, PhoneAccountHandle> mPhoneAccounts = new ArrayMap();
+
+ public static SelfManagedCallList getInstance() {
+ if (sInstance == null) {
+ sInstance = new SelfManagedCallList();
+ }
+ return sInstance;
+ }
+
+ private Listener mListener;
+
+ private List<SelfManagedConnection> mConnections = new ArrayList<>();
+
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ public void registerPhoneAccounts(Context context) {
+ registerPhoneAccount(context, SELF_MANAGED_ACCOUNT_1, SELF_MANAGED_ADDRESS_1);
+ registerPhoneAccount(context, SELF_MANAGED_ACCOUNT_2, SELF_MANAGED_ADDRESS_2);
+ }
+
+ public void registerPhoneAccount(Context context, String id, Uri address) {
+ PhoneAccountHandle handle = new PhoneAccountHandle(COMPONENT_NAME, id);
+ mPhoneAccounts.put(id, handle);
+ PhoneAccount.Builder builder = PhoneAccount.builder(handle, id)
+ .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
+ .addSupportedUriScheme(PhoneAccount.SCHEME_SIP)
+ .setAddress(address)
+ .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED |
+ PhoneAccount.CAPABILITY_VIDEO_CALLING |
+ PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING);
+
+ TelecomManager.from(context).registerPhoneAccount(builder.build());
+ }
+
+ public PhoneAccountHandle getPhoneAccountHandle(String id) {
+ return mPhoneAccounts.get(id);
+ }
+
+ public void notifyCreateIncomingConnectionFailed(ConnectionRequest request) {
+ if (mListener != null) {
+ mListener.onCreateIncomingConnectionFailed(request);
+ }
+ }
+
+ public void notifyCreateOutgoingConnectionFailed(ConnectionRequest request) {
+ if (mListener != null) {
+ mListener.onCreateOutgoingConnectionFailed(request);
+ }
+ }
+
+ public void addConnection(SelfManagedConnection connection) {
+ Log.i(this, "addConnection %s", connection);
+ mConnections.add(connection);
+ if (mListener != null) {
+ Log.i(this, "addConnection calling onConnectionListChanged %s", connection);
+ mListener.onConnectionListChanged();
+ }
+ }
+
+ public void removeConnection(SelfManagedConnection connection) {
+ Log.i(this, "removeConnection %s", connection);
+ mConnections.remove(connection);
+ if (mListener != null) {
+ Log.i(this, "removeConnection calling onConnectionListChanged %s", connection);
+ mListener.onConnectionListChanged();
+ }
+ }
+
+ public List<SelfManagedConnection> getConnections() {
+ return mConnections;
+ }
+
+ public void notifyCallModified() {
+ if (mListener != null) {
+ mListener.onConnectionListChanged();
+ }
+ }
+}
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallListAdapter.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallListAdapter.java
new file mode 100644
index 0000000..29d3c5f
--- /dev/null
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallListAdapter.java
@@ -0,0 +1,189 @@
+/*
+ * 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.server.telecom.testapps;
+
+import android.telecom.CallAudioState;
+import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccountHandle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class SelfManagedCallListAdapter extends BaseAdapter {
+
+ private static final String TAG = "SelfMgCallListAd";
+ /**
+ * Listener used to handle tap of the "disconnect" button for a connection.
+ */
+ private View.OnClickListener mDisconnectListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ View parent = (View) v.getParent().getParent();
+ SelfManagedConnection connection = (SelfManagedConnection) parent.getTag();
+ connection.setConnectionDisconnected(DisconnectCause.LOCAL);
+ SelfManagedCallList.getInstance().removeConnection(connection);
+ }
+ };
+
+ /**
+ * Listener used to handle tap of the "active" button for a connection.
+ */
+ private View.OnClickListener mActiveListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ View parent = (View) v.getParent().getParent();
+ SelfManagedConnection connection = (SelfManagedConnection) parent.getTag();
+ connection.setConnectionActive();
+ notifyDataSetChanged();
+ }
+ };
+
+ /**
+ * Listener used to handle tap of the "held" button for a connection.
+ */
+ private View.OnClickListener mHeldListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ View parent = (View) v.getParent().getParent();
+ SelfManagedConnection connection = (SelfManagedConnection) parent.getTag();
+ connection.setConnectionHeld();
+ notifyDataSetChanged();
+ }
+ };
+
+ private View.OnClickListener mSpeakerListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ View parent = (View) v.getParent().getParent();
+ SelfManagedConnection connection = (SelfManagedConnection) parent.getTag();
+ connection.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
+ notifyDataSetChanged();
+ }
+ };
+
+ private View.OnClickListener mEarpieceListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ View parent = (View) v.getParent().getParent();
+ SelfManagedConnection connection = (SelfManagedConnection) parent.getTag();
+ connection.setAudioRoute(CallAudioState.ROUTE_EARPIECE);
+ notifyDataSetChanged();
+ }
+ };
+
+ private final LayoutInflater mLayoutInflater;
+
+ private List<SelfManagedConnection> mConnections;
+
+ public SelfManagedCallListAdapter(LayoutInflater layoutInflater,
+ List<SelfManagedConnection> connections) {
+
+ mLayoutInflater = layoutInflater;
+ mConnections = connections;
+ }
+
+ @Override
+ public int getCount() {
+ return mConnections.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mConnections.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View result = convertView == null
+ ? mLayoutInflater.inflate(R.layout.self_managed_call_list_item, parent, false)
+ : convertView;
+ SelfManagedConnection connection = mConnections.get(position);
+ PhoneAccountHandle phoneAccountHandle = connection.getExtras().getParcelable(
+ SelfManagedConnection.EXTRA_PHONE_ACCOUNT_HANDLE);
+ CallAudioState audioState = connection.getCallAudioState();
+ String audioRoute = "?";
+ if (audioState != null) {
+ switch (audioState.getRoute()) {
+ case CallAudioState.ROUTE_BLUETOOTH:
+ audioRoute = "BT";
+ break;
+ case CallAudioState.ROUTE_SPEAKER:
+ audioRoute = "\uD83D\uDD0A";
+ break;
+ case CallAudioState.ROUTE_EARPIECE:
+ audioRoute = "\uD83D\uDC42";
+ break;
+ case CallAudioState.ROUTE_WIRED_HEADSET:
+ audioRoute = "\uD83D\uDD0C";
+ break;
+ default:
+ audioRoute = "?";
+ break;
+ }
+ }
+ String callType;
+ if (connection.isIncomingCall()) {
+ if (connection.isIncomingCallUiShowing()) {
+ callType = "Incoming - Show Incoming UX";
+ } else {
+ callType = "Incoming - DO NOT SHOW Incoming UX";
+ }
+ } else {
+ callType = "Outgoing";
+ }
+ setInfoForRow(result, phoneAccountHandle.getId(), connection.getAddress().toString(),
+ android.telecom.Connection.stateToString(connection.getState()), audioRoute,
+ callType);
+ result.setTag(connection);
+ return result;
+ }
+
+ public void updateConnections() {
+ Log.i(TAG, "updateConnections "+ mConnections.size());
+
+ notifyDataSetChanged();
+ }
+
+ private void setInfoForRow(View view, String accountName, String number,
+ String status, String audioRoute, String callType) {
+
+ TextView numberTextView = (TextView) view.findViewById(R.id.phoneNumber);
+ TextView statusTextView = (TextView) view.findViewById(R.id.callState);
+ View activeButton = view.findViewById(R.id.setActiveButton);
+ activeButton.setOnClickListener(mActiveListener);
+ View disconnectButton = view.findViewById(R.id.disconnectButton);
+ disconnectButton.setOnClickListener(mDisconnectListener);
+ View setHeldButton = view.findViewById(R.id.setHeldButton);
+ setHeldButton.setOnClickListener(mHeldListener);
+ View speakerButton = view.findViewById(R.id.speakerButton);
+ speakerButton.setOnClickListener(mSpeakerListener);
+ View earpieceButton = view.findViewById(R.id.earpieceButton);
+ earpieceButton.setOnClickListener(mEarpieceListener);
+ numberTextView.setText(accountName + " - " + number + " (" + audioRoute + ")");
+ statusTextView.setText(callType + " - Status: " + status);
+ }
+}
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java
new file mode 100644
index 0000000..8b7eae0
--- /dev/null
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java
@@ -0,0 +1,150 @@
+/*
+ * 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.server.telecom.testapps;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.os.Bundle;
+import android.telecom.ConnectionRequest;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.RadioButton;
+import android.widget.Toast;
+
+import com.android.server.telecom.testapps.R;
+
+import java.util.Objects;
+
+/**
+ * Provides a sample third-party calling app UX which implements the self managed connection service
+ * APIs.
+ */
+public class SelfManagedCallingActivity extends Activity {
+ private static final String TAG = "SelfMgCallActivity";
+ private SelfManagedCallList mCallList = SelfManagedCallList.getInstance();
+ private CheckBox mCheckIfPermittedBeforeCalling;
+ private Button mPlaceOutgoingCallButton;
+ private Button mPlaceIncomingCallButton;
+ private RadioButton mUseAcct1Button;
+ private RadioButton mUseAcct2Button;
+ private EditText mNumber;
+ private ListView mListView;
+ private SelfManagedCallListAdapter mListAdapter;
+
+ private SelfManagedCallList.Listener mCallListListener = new SelfManagedCallList.Listener() {
+ @Override
+ public void onCreateIncomingConnectionFailed(ConnectionRequest request) {
+ Log.i(TAG, "onCreateIncomingConnectionFailed " + request);
+ Toast.makeText(SelfManagedCallingActivity.this,
+ R.string.incomingCallNotPermittedCS , Toast.LENGTH_SHORT).show();
+ };
+
+ @Override
+ public void onCreateOutgoingConnectionFailed(ConnectionRequest request) {
+ Log.i(TAG, "onCreateOutgoingConnectionFailed " + request);
+ Toast.makeText(SelfManagedCallingActivity.this,
+ R.string.outgoingCallNotPermittedCS , Toast.LENGTH_SHORT).show();
+ };
+
+ @Override
+ public void onConnectionListChanged() {
+ Log.i(TAG, "onConnectionListChanged");
+ mListAdapter.updateConnections();
+ };
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.self_managed_sample_main);
+ mCheckIfPermittedBeforeCalling = (CheckBox) findViewById(
+ R.id.checkIfPermittedBeforeCalling);
+ mPlaceOutgoingCallButton = (Button) findViewById(R.id.placeOutgoingCallButton);
+ mPlaceOutgoingCallButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ placeOutgoingCall();
+ }
+ });
+ mPlaceIncomingCallButton = (Button) findViewById(R.id.placeIncomingCallButton);
+ mPlaceIncomingCallButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ placeIncomingCall();
+ }
+ });
+ mUseAcct1Button = (RadioButton) findViewById(R.id.useAcct1Button);
+ mUseAcct2Button = (RadioButton) findViewById(R.id.useAcct2Button);
+ mNumber = (EditText) findViewById(R.id.phoneNumber);
+ mListView = (ListView) findViewById(R.id.callList);
+ mCallList.setListener(mCallListListener);
+ mCallList.registerPhoneAccounts(this);
+ mListAdapter = new SelfManagedCallListAdapter(getLayoutInflater(),
+ mCallList.getConnections());
+ mListView.setAdapter(mListAdapter);
+ Log.i(TAG, "onCreate - mCallList id " + Objects.hashCode(mCallList));
+ }
+
+ private PhoneAccountHandle getSelectedPhoneAccountHandle() {
+ if (mUseAcct1Button.isChecked()) {
+ return mCallList.getPhoneAccountHandle(SelfManagedCallList.SELF_MANAGED_ACCOUNT_1);
+ } else if (mUseAcct2Button.isChecked()) {
+ return mCallList.getPhoneAccountHandle(SelfManagedCallList.SELF_MANAGED_ACCOUNT_2);
+ }
+ return null;
+ }
+
+ private void placeOutgoingCall() {
+ TelecomManager tm = TelecomManager.from(this);
+ PhoneAccountHandle phoneAccountHandle = getSelectedPhoneAccountHandle();
+
+ if (mCheckIfPermittedBeforeCalling.isChecked()) {
+ if (!tm.isOutgoingCallPermitted(phoneAccountHandle)) {
+ Toast.makeText(this, R.string.outgoingCallNotPermitted , Toast.LENGTH_SHORT).show();
+ return;
+ }
+ }
+
+ Bundle extras = new Bundle();
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+ getSelectedPhoneAccountHandle());
+ tm.placeCall(Uri.parse(mNumber.getText().toString()), extras);
+ }
+
+ private void placeIncomingCall() {
+ TelecomManager tm = TelecomManager.from(this);
+ PhoneAccountHandle phoneAccountHandle = getSelectedPhoneAccountHandle();
+
+ if (mCheckIfPermittedBeforeCalling.isChecked()) {
+ if (!tm.isIncomingCallPermitted(phoneAccountHandle)) {
+ Toast.makeText(this, R.string.incomingCallNotPermitted , Toast.LENGTH_SHORT).show();
+ return;
+ }
+ }
+
+ Bundle extras = new Bundle();
+ extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
+ Uri.parse(mNumber.getText().toString()));
+ tm.addNewIncomingCall(getSelectedPhoneAccountHandle(), extras);
+ }
+}
\ No newline at end of file
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnection.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnection.java
new file mode 100644
index 0000000..5fd8eba
--- /dev/null
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnection.java
@@ -0,0 +1,89 @@
+/*
+ * 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.server.telecom.testapps;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.telecom.CallAudioState;
+import android.telecom.Connection;
+import android.telecom.ConnectionService;
+import android.telecom.DisconnectCause;
+
+/**
+ * Sample self-managed {@link Connection} for a self-managed {@link ConnectionService}.
+ * <p>
+ * See {@link android.telecom} for more information on self-managed {@link ConnectionService}s.
+ */
+public class SelfManagedConnection extends Connection {
+ public static final String EXTRA_PHONE_ACCOUNT_HANDLE =
+ "com.android.server.telecom.testapps.extra.PHONE_ACCOUNT_HANDLE";
+
+ private final SelfManagedCallList mCallList;
+ private final MediaPlayer mMediaPlayer;
+ private final boolean mIsIncomingCall;
+ private boolean mIsIncomingCallUiShowing;
+
+ SelfManagedConnection(SelfManagedCallList callList, Context context, boolean isIncoming) {
+ mCallList = callList;
+ mMediaPlayer = createMediaPlayer(context);
+ mIsIncomingCall = isIncoming;
+ }
+
+ /**
+ * Handles updates to the audio state of the connection.
+ * @param state The new connection audio state.
+ */
+ @Override
+ public void onCallAudioStateChanged(CallAudioState state) {
+ mCallList.notifyCallModified();
+ }
+
+ public void setConnectionActive() {
+ mMediaPlayer.start();
+ setActive();
+ }
+
+ public void setConnectionHeld() {
+ mMediaPlayer.pause();
+ setOnHold();
+ }
+
+ public void setConnectionDisconnected(int cause) {
+ mMediaPlayer.stop();
+ setDisconnected(new DisconnectCause(cause));
+ destroy();
+ }
+
+ public void setIsIncomingCallUiShowing(boolean showing) {
+ mIsIncomingCallUiShowing = showing;
+ }
+
+ public boolean isIncomingCallUiShowing() {
+ return mIsIncomingCallUiShowing;
+ }
+
+ public boolean isIncomingCall() {
+ return mIsIncomingCall;
+ }
+
+ private MediaPlayer createMediaPlayer(Context context) {
+ int audioToPlay = (Math.random() > 0.5f) ? R.raw.sample_audio : R.raw.sample_audio2;
+ MediaPlayer mediaPlayer = MediaPlayer.create(context, audioToPlay);
+ mediaPlayer.setLooping(true);
+ return mediaPlayer;
+ }
+}
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
new file mode 100644
index 0000000..31bbcd6
--- /dev/null
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
@@ -0,0 +1,83 @@
+/*
+ * 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.server.telecom.testapps;
+
+import android.os.Bundle;
+import android.telecom.Connection;
+import android.telecom.ConnectionRequest;
+import android.telecom.ConnectionService;
+import android.telecom.Log;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+
+import java.util.Objects;
+
+/**
+ * Sample implementation of the self-managed {@link ConnectionService} API.
+ * <p>
+ * See {@link android.telecom} for more information on self-managed {@link ConnectionService}s.
+ */
+public class SelfManagedConnectionService extends ConnectionService {
+ private final SelfManagedCallList mCallList = SelfManagedCallList.getInstance();
+
+ @Override
+ public Connection onCreateOutgoingConnection(
+ PhoneAccountHandle connectionManagerAccount,
+ final ConnectionRequest request) {
+
+ return createSelfManagedConnection(request, false);
+ }
+
+ @Override
+ public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ return createSelfManagedConnection(request, true);
+ }
+
+ @Override
+ public void onCreateIncomingConnectionFailed(ConnectionRequest request) {
+ mCallList.notifyCreateIncomingConnectionFailed(request);
+ }
+
+ @Override
+ public void onCreateOutgoingConnectionFailed(ConnectionRequest request) {
+ mCallList.notifyCreateOutgoingConnectionFailed(request);
+ }
+
+
+ private Connection createSelfManagedConnection(ConnectionRequest request, boolean isIncoming) {
+ SelfManagedConnection connection = new SelfManagedConnection(mCallList,
+ getApplicationContext(), isIncoming);
+ connection.setConnectionProperties(Connection.PROPERTY_SELF_MANAGED);
+ connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
+ connection.setExtras(request.getExtras());
+ if (isIncoming) {
+ connection.setIsIncomingCallUiShowing(request.shouldShowIncomingCallUi());
+ }
+
+ // Track the phone account handle which created this connection so we can distinguish them
+ // in the sample call list later.
+ Bundle moreExtras = new Bundle();
+ moreExtras.putParcelable(SelfManagedConnection.EXTRA_PHONE_ACCOUNT_HANDLE,
+ request.getAccountHandle());
+ connection.putExtras(moreExtras);
+ connection.setVideoState(request.getVideoState());
+ Log.i(this, "createSelfManagedConnection %s", connection);
+ mCallList.addConnection(connection);
+ return connection;
+ }
+}