summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
authoryueg <yueg@google.com>2017-10-16 14:23:26 -0700
committerEric Erfanian <erfanian@google.com>2017-10-17 08:07:25 -0700
commit2f8d48f5e981ab9945c907cd9ab0f6bd6a82db22 (patch)
treead5e5bed1fe75941840374419f0bc06dc88641c3 /java
parent5a6cb962ae30ad9e308b5bcc00920daf84618808 (diff)
Add GSM conference calling to simulator.
This CL adds a new item to the simulator menu: - Add GSM conference The GSM conference action creates a conference with 5 phone calls. Users can individually separate or kick calls out of the conference. Hanging up the second last call finishes the conference. Bug: 67785540 Test: SimulatorConferenceTest PiperOrigin-RevId: 172377631 Change-Id: Ic30fa6c65cf782247f75bcdd1ecbd86b1c16f143
Diffstat (limited to 'java')
-rw-r--r--java/com/android/dialer/simulator/Simulator.java20
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorConference.java168
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java229
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorConnection.java6
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorConnectionService.java20
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorMissedCallCreator.java4
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java43
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorSpamCallCreator.java4
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorVideoCall.java11
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java30
10 files changed, 508 insertions, 27 deletions
diff --git a/java/com/android/dialer/simulator/Simulator.java b/java/com/android/dialer/simulator/Simulator.java
index f753e5f6b..4812fa5d6 100644
--- a/java/com/android/dialer/simulator/Simulator.java
+++ b/java/com/android/dialer/simulator/Simulator.java
@@ -30,6 +30,16 @@ public interface Simulator {
ActionProvider getActionProvider(Context context);
+ /** The type of conference to emulate. */
+ // TODO(b/67785540): add VoLTE and CDMA conference call
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ CONFERENCE_TYPE_GSM,
+ })
+ @interface ConferenceType {}
+
+ static final int CONFERENCE_TYPE_GSM = 1;
+
/** Information about a connection event. */
public static class Event {
/** The type of connection event. */
@@ -44,6 +54,11 @@ public interface Simulator {
STATE_CHANGE,
DTMF,
SESSION_MODIFY_REQUEST,
+ CALL_AUDIO_STATE_CHANGED,
+ CONNECTION_ADDED,
+ MERGE,
+ SEPARATE,
+ SWAP,
})
public @interface Type {}
@@ -56,6 +71,11 @@ public interface Simulator {
public static final int STATE_CHANGE = 6;
public static final int DTMF = 7;
public static final int SESSION_MODIFY_REQUEST = 8;
+ public static final int CALL_AUDIO_STATE_CHANGED = 9;
+ public static final int CONNECTION_ADDED = 10;
+ public static final int MERGE = 11;
+ public static final int SEPARATE = 12;
+ public static final int SWAP = 13;
@Type public final int type;
/** Holds event specific information. For example, for DTMF this could be the keycode. */
diff --git a/java/com/android/dialer/simulator/impl/SimulatorConference.java b/java/com/android/dialer/simulator/impl/SimulatorConference.java
new file mode 100644
index 000000000..7468b56b5
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorConference.java
@@ -0,0 +1,168 @@
+/*
+ * 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.dialer.simulator.impl;
+
+import android.support.annotation.NonNull;
+import android.telecom.CallAudioState;
+import android.telecom.Conference;
+import android.telecom.Connection;
+import android.telecom.PhoneAccountHandle;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.simulator.Simulator;
+import com.android.dialer.simulator.Simulator.Event;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a conference call. When a user merges two phone calls we create an instance of this
+ * conference object and add it to the connection service. All operations such as hold and DTMF are
+ * then performed on this object.
+ */
+public final class SimulatorConference extends Conference implements SimulatorConnection.Listener {
+ static final int PROPERTY_GENERIC_CONFERENCE = 1 << 1;
+
+ private final List<Listener> listeners = new ArrayList<>();
+ private final List<Event> events = new ArrayList<>();
+ private final int conferenceType;
+
+ private SimulatorConference(
+ PhoneAccountHandle handle, @Simulator.ConferenceType int conferenceType) {
+ super(handle);
+ this.conferenceType = conferenceType;
+ setActive();
+ }
+
+ static SimulatorConference newGsmConference(PhoneAccountHandle handle) {
+ SimulatorConference simulatorConference =
+ new SimulatorConference(handle, Simulator.CONFERENCE_TYPE_GSM);
+ simulatorConference.setConnectionCapabilities(
+ Connection.CAPABILITY_MUTE
+ | Connection.CAPABILITY_SUPPORT_HOLD
+ | Connection.CAPABILITY_HOLD
+ | Connection.CAPABILITY_MANAGE_CONFERENCE);
+ return simulatorConference;
+ }
+
+ public void addListener(@NonNull Listener listener) {
+ listeners.add(Assert.isNotNull(listener));
+ }
+
+ public void removeListener(@NonNull Listener listener) {
+ listeners.remove(Assert.isNotNull(listener));
+ }
+
+ @NonNull
+ public List<Event> getEvents() {
+ return events;
+ }
+
+ @Override
+ public void onCallAudioStateChanged(CallAudioState state) {
+ LogUtil.enterBlock("SimulatorConference.onCallAudioStateChanged");
+ onEvent(new Event(Event.CALL_AUDIO_STATE_CHANGED));
+ }
+
+ @Override
+ public void onConnectionAdded(Connection connection) {
+ LogUtil.enterBlock("SimulatorConference.onConnectionAdded");
+ onEvent(
+ new Event(
+ Event.CONNECTION_ADDED, SimulatorSimCallManager.getConnectionTag(connection), null));
+ ((SimulatorConnection) connection).addListener(this);
+ }
+
+ @Override
+ public void onDisconnect() {
+ LogUtil.enterBlock("SimulatorConference.onDisconnect");
+ onEvent(new Event(Event.DISCONNECT));
+ }
+
+ @Override
+ public void onHold() {
+ LogUtil.enterBlock("SimulatorConference.onHold");
+ onEvent(new Event(Event.HOLD));
+ }
+
+ @Override
+ public void onMerge(Connection connection) {
+ LogUtil.i("SimulatorConference.onMerge", "connection: " + connection);
+ onEvent(new Event(Event.MERGE, SimulatorSimCallManager.getConnectionTag(connection), null));
+ }
+
+ @Override
+ public void onMerge() {
+ LogUtil.enterBlock("SimulatorConference.onMerge");
+ onEvent(new Event(Event.MERGE));
+ }
+
+ @Override
+ public void onPlayDtmfTone(char c) {
+ LogUtil.enterBlock("SimulatorConference.onPlayDtmfTone");
+ onEvent(new Event(Event.DTMF, Character.toString(c), null));
+ }
+
+ @Override
+ public void onSeparate(Connection connection) {
+ LogUtil.i("SimulatorConference.onSeparate", "connection: " + connection);
+ onEvent(new Event(Event.SEPARATE, SimulatorSimCallManager.getConnectionTag(connection), null));
+ }
+
+ @Override
+ public void onSwap() {
+ LogUtil.enterBlock("SimulatorConference.onSwap");
+ onEvent(new Event(Event.SWAP));
+ }
+
+ @Override
+ public void onUnhold() {
+ LogUtil.enterBlock("SimulatorConference.onUnhold");
+ onEvent(new Event(Event.UNHOLD));
+ }
+
+ @Override
+ public void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event) {
+ if (conferenceType == Simulator.CONFERENCE_TYPE_GSM) {
+ onGsmEvent(connection, event);
+ }
+ }
+
+ private void onGsmEvent(@NonNull SimulatorConnection connection, @NonNull Event event) {
+ if (event.type == Event.STATE_CHANGE
+ && Connection.stateToString(Connection.STATE_DISCONNECTED).equals(event.data2)) {
+ removeConnection(connection);
+ connection.removeListener(this);
+ if (getConnections().size() <= 1) {
+ // When only one connection exists, it's not conference call anymore
+ setDisconnected(connection.getDisconnectCause());
+ destroy();
+ }
+ }
+ }
+
+ void onEvent(@NonNull Event event) {
+ events.add(Assert.isNotNull(event));
+ for (Listener listener : new ArrayList<>(listeners)) {
+ listener.onEvent(this, event);
+ }
+ }
+
+ /** Callback for when a new event arrives. */
+ public interface Listener {
+ void onEvent(@NonNull SimulatorConference conference, @NonNull Event event);
+ }
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java b/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java
new file mode 100644
index 000000000..838b58dc2
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java
@@ -0,0 +1,229 @@
+/*
+ * 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.dialer.simulator.impl;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.telecom.Conferenceable;
+import android.telecom.Connection;
+import android.telecom.DisconnectCause;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.ThreadUtil;
+import com.android.dialer.simulator.Simulator;
+import com.android.dialer.simulator.Simulator.Event;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/** Creates a conference with a given number of participants. */
+final class SimulatorConferenceCreator
+ implements SimulatorConnectionService.Listener,
+ SimulatorConnection.Listener,
+ SimulatorConference.Listener {
+ private static final String EXTRA_CALL_COUNT = "call_count";
+
+ @NonNull private final Context context;
+ @NonNull private final List<String> connectionTags = new ArrayList<>();
+ @Simulator.ConferenceType private final int conferenceType;
+
+ public SimulatorConferenceCreator(
+ @NonNull Context context, @Simulator.ConferenceType int conferenceType) {
+ this.context = Assert.isNotNull(context);
+ this.conferenceType = conferenceType;
+ }
+
+ void start(int callCount) {
+ SimulatorConnectionService.addListener(this);
+ addNextCall(callCount);
+ }
+
+ private void addNextCall(int callCount) {
+ LogUtil.i("SimulatorConferenceCreator.addNextIncomingCall", "callCount: " + callCount);
+ if (callCount <= 0) {
+ LogUtil.i("SimulatorConferenceCreator.addNextCall", "done adding calls");
+ return;
+ }
+
+ String callerId = String.format(Locale.US, "+1-650-234%04d", callCount);
+ Bundle extras = new Bundle();
+ extras.putInt(EXTRA_CALL_COUNT, callCount - 1);
+ connectionTags.add(
+ SimulatorSimCallManager.addNewIncomingCall(context, callerId, false /* isVideo */, extras));
+ }
+
+ @Override
+ public void onNewIncomingConnection(@NonNull SimulatorConnection connection) {
+ if (!isMyConnection(connection)) {
+ LogUtil.i("SimulatorConferenceCreator.onNewOutgoingConnection", "unknown connection");
+ return;
+ }
+
+ LogUtil.i("SimulatorConferenceCreator.onNewOutgoingConnection", "connection created");
+ connection.addListener(this);
+
+ // Telecom will force the connection to switch to DIALING when we return it. Wait until after
+ // we're returned it before changing call state.
+ ThreadUtil.postOnUiThread(() -> connection.setActive());
+
+ // Once the connection is active, go ahead and conference it and add the next call.
+ ThreadUtil.postDelayedOnUiThread(
+ () -> {
+ SimulatorConference conference = findCurrentConference();
+ if (conference == null) {
+ Assert.checkArgument(conferenceType == Simulator.CONFERENCE_TYPE_GSM);
+ conference =
+ SimulatorConference.newGsmConference(
+ SimulatorSimCallManager.getSystemPhoneAccountHandle(context));
+ conference.addListener(this);
+ SimulatorConnectionService.getInstance().addConference(conference);
+ }
+ updateConferenceableConnections();
+ conference.addConnection(connection);
+ addNextCall(getCallCount(connection));
+ },
+ 1000);
+ }
+
+ @Override
+ public void onNewOutgoingConnection(@NonNull SimulatorConnection connection) {}
+
+ /**
+ * This is called when the user clicks the merge button. We create the initial conference
+ * automatically but with this method we can let the user split and merge calls as desired.
+ */
+ @Override
+ public void onConference(
+ @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) {
+ LogUtil.enterBlock("SimulatorConferenceCreator.onConference");
+ if (!isMyConnection(connection1) || !isMyConnection(connection2)) {
+ LogUtil.i("SimulatorConferenceCreator.onConference", "unknown connections, ignoring");
+ return;
+ }
+
+ if (connection1.getConference() != null) {
+ connection1.getConference().addConnection(connection2);
+ } else if (connection2.getConference() != null) {
+ connection2.getConference().addConnection(connection1);
+ } else {
+ Assert.checkArgument(conferenceType == Simulator.CONFERENCE_TYPE_GSM);
+ SimulatorConference conference =
+ SimulatorConference.newGsmConference(
+ SimulatorSimCallManager.getSystemPhoneAccountHandle(context));
+ conference.addConnection(connection1);
+ conference.addConnection(connection2);
+ conference.addListener(this);
+ SimulatorConnectionService.getInstance().addConference(conference);
+ }
+ }
+
+ private boolean isMyConnection(@NonNull Connection connection) {
+ for (String connectionTag : connectionTags) {
+ if (connection.getExtras().getBoolean(connectionTag)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void updateConferenceableConnections() {
+ LogUtil.enterBlock("SimulatorConferenceCreator.updateConferenceableConnections");
+ for (String connectionTag : connectionTags) {
+ SimulatorConnection connection = SimulatorSimCallManager.findConnectionByTag(connectionTag);
+ List<Conferenceable> conferenceables = getMyConferenceables();
+ conferenceables.remove(connection);
+ conferenceables.remove(connection.getConference());
+ connection.setConferenceables(conferenceables);
+ }
+ }
+
+ private List<Conferenceable> getMyConferenceables() {
+ List<Conferenceable> conferenceables = new ArrayList<>();
+ for (String connectionTag : connectionTags) {
+ SimulatorConnection connection = SimulatorSimCallManager.findConnectionByTag(connectionTag);
+ conferenceables.add(connection);
+ if (connection.getConference() != null
+ && !conferenceables.contains(connection.getConference())) {
+ conferenceables.add(connection.getConference());
+ }
+ }
+ return conferenceables;
+ }
+
+ @Nullable
+ private SimulatorConference findCurrentConference() {
+ for (String connectionTag : connectionTags) {
+ SimulatorConnection connection = SimulatorSimCallManager.findConnectionByTag(connectionTag);
+ if (connection.getConference() != null) {
+ return (SimulatorConference) connection.getConference();
+ }
+ }
+ return null;
+ }
+
+ private static int getCallCount(@NonNull Connection connection) {
+ return connection.getExtras().getInt(EXTRA_CALL_COUNT);
+ }
+
+ @Override
+ public void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event) {
+ switch (event.type) {
+ case Event.NONE:
+ throw Assert.createIllegalStateFailException();
+ case Event.HOLD:
+ connection.setOnHold();
+ break;
+ case Event.UNHOLD:
+ connection.setActive();
+ break;
+ case Event.DISCONNECT:
+ connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
+ break;
+ default:
+ LogUtil.i(
+ "SimulatorConferenceCreator.onEvent", "unexpected conference event: " + event.type);
+ break;
+ }
+ }
+
+ @Override
+ public void onEvent(@NonNull SimulatorConference conference, @NonNull Event event) {
+ switch (event.type) {
+ case Event.MERGE:
+ int capabilities = conference.getConnectionCapabilities();
+ capabilities |= Connection.CAPABILITY_SWAP_CONFERENCE;
+ conference.setConnectionCapabilities(capabilities);
+ break;
+ case Event.SEPARATE:
+ SimulatorConnection connectionToRemove =
+ SimulatorSimCallManager.findConnectionByTag(event.data1);
+ conference.removeConnection(connectionToRemove);
+ break;
+ case Event.DISCONNECT:
+ for (Connection connection : new ArrayList<>(conference.getConnections())) {
+ connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
+ }
+ break;
+ default:
+ LogUtil.i(
+ "SimulatorConferenceCreator.onEvent", "unexpected conference event: " + event.type);
+ break;
+ }
+ }
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnection.java b/java/com/android/dialer/simulator/impl/SimulatorConnection.java
index 70c1095dc..e4a34b51b 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorConnection.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorConnection.java
@@ -41,7 +41,9 @@ public final class SimulatorConnection extends Connection {
CAPABILITY_MUTE
| CAPABILITY_SUPPORT_HOLD
| CAPABILITY_HOLD
- | CAPABILITY_CAN_UPGRADE_TO_VIDEO);
+ | CAPABILITY_CAN_UPGRADE_TO_VIDEO
+ | CAPABILITY_DISCONNECT_FROM_CONFERENCE
+ | CAPABILITY_SEPARATE_FROM_CONFERENCE);
setVideoProvider(new SimulatorVideoProvider(context, this));
}
@@ -108,7 +110,7 @@ public final class SimulatorConnection extends Connection {
void onEvent(@NonNull Event event) {
events.add(Assert.isNotNull(event));
- for (Listener listener : listeners) {
+ for (Listener listener : new ArrayList<>(listeners)) {
listener.onEvent(this, event);
}
}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java b/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java
index 25d4a7240..465890cf0 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java
@@ -109,6 +109,19 @@ public class SimulatorConnectionService extends ConnectionService {
return connection;
}
+ @Override
+ public void onConference(Connection connection1, Connection connection2) {
+ LogUtil.i(
+ "SimulatorConnectionService.onConference",
+ "connection1: "
+ + SimulatorSimCallManager.getConnectionTag(connection1)
+ + ", connection2: "
+ + SimulatorSimCallManager.getConnectionTag(connection2));
+ for (Listener listener : listeners) {
+ listener.onConference((SimulatorConnection) connection1, (SimulatorConnection) connection2);
+ }
+ }
+
private static Uri getPhoneNumber(ConnectionRequest request) {
String phoneNumber = request.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
return Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null);
@@ -116,8 +129,11 @@ public class SimulatorConnectionService extends ConnectionService {
/** Callback used to notify listeners when a new connection has been added. */
public interface Listener {
- void onNewOutgoingConnection(SimulatorConnection connection);
+ void onNewOutgoingConnection(@NonNull SimulatorConnection connection);
+
+ void onNewIncomingConnection(@NonNull SimulatorConnection connection);
- void onNewIncomingConnection(SimulatorConnection connection);
+ void onConference(
+ @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2);
}
}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorMissedCallCreator.java b/java/com/android/dialer/simulator/impl/SimulatorMissedCallCreator.java
index f85f46602..6d4a26278 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorMissedCallCreator.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorMissedCallCreator.java
@@ -62,6 +62,10 @@ final class SimulatorMissedCallCreator implements SimulatorConnectionService.Lis
DISCONNECT_DELAY_MILLIS);
}
+ @Override
+ public void onConference(
+ @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) {}
+
private void addNextIncomingCall(int callCount) {
if (callCount <= 0) {
LogUtil.i("SimulatorMissedCallCreator.addNextIncomingCall", "done adding calls");
diff --git a/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java b/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java
index 33eac51d1..00899fd69 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
+import android.telecom.Connection;
import android.telecom.ConnectionRequest;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
@@ -47,6 +48,7 @@ public class SimulatorSimCallManager {
private static final String SIM_CALL_MANAGER_ACCOUNT_ID = "SIMULATOR_ACCOUNT_ID";
private static final String VIDEO_PROVIDER_ACCOUNT_ID = "SIMULATOR_VIDEO_ACCOUNT_ID";
private static final String EXTRA_IS_SIMULATOR_CONNECTION = "is_simulator_connection";
+ private static final String EXTRA_CONNECTION_TAG = "connection_tag";
static void register(@NonNull Context context) {
LogUtil.enterBlock("SimulatorSimCallManager.register");
@@ -85,9 +87,7 @@ public class SimulatorSimCallManager {
register(context);
extras = new Bundle(extras);
- extras.putBoolean(EXTRA_IS_SIMULATOR_CONNECTION, true);
- String connectionTag = createUniqueConnectionTag();
- extras.putBoolean(connectionTag, true);
+ extras.putAll(createSimulatorConnectionExtras());
Bundle outgoingCallExtras = new Bundle();
outgoingCallExtras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
@@ -102,7 +102,7 @@ public class SimulatorSimCallManager {
} catch (SecurityException e) {
throw Assert.createIllegalStateFailException("Unable to place call: " + e);
}
- return connectionTag;
+ return extras.getString(EXTRA_CONNECTION_TAG);
}
@NonNull
@@ -123,14 +123,12 @@ public class SimulatorSimCallManager {
extras = new Bundle(extras);
extras.putString(TelephonyManager.EXTRA_INCOMING_NUMBER, callerId);
- extras.putBoolean(EXTRA_IS_SIMULATOR_CONNECTION, true);
- String connectionTag = createUniqueConnectionTag();
- extras.putBoolean(connectionTag, true);
+ extras.putAll(createSimulatorConnectionExtras());
TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
telecomManager.addNewIncomingCall(
isVideo ? getVideoProviderHandle(context) : getSystemPhoneAccountHandle(context), extras);
- return connectionTag;
+ return extras.getString(EXTRA_CONNECTION_TAG);
}
@NonNull
@@ -167,7 +165,7 @@ public class SimulatorSimCallManager {
}
@NonNull
- private static PhoneAccountHandle getSystemPhoneAccountHandle(@NonNull Context context) {
+ public static PhoneAccountHandle getSystemPhoneAccountHandle(@NonNull Context context) {
TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
List<PhoneAccountHandle> handles;
try {
@@ -190,10 +188,37 @@ public class SimulatorSimCallManager {
}
@NonNull
+ public static String getConnectionTag(@NonNull Connection connection) {
+ String connectionTag = connection.getExtras().getString(EXTRA_CONNECTION_TAG);
+ return Assert.isNotNull(connectionTag);
+ }
+
+ @NonNull
+ public static SimulatorConnection findConnectionByTag(@NonNull String connectionTag) {
+ Assert.isNotNull(connectionTag);
+ for (Connection connection : SimulatorConnectionService.getInstance().getAllConnections()) {
+ if (connection.getExtras().getBoolean(connectionTag)) {
+ return (SimulatorConnection) connection;
+ }
+ }
+ throw Assert.createIllegalStateFailException();
+ }
+
+ @NonNull
private static String createUniqueConnectionTag() {
int callId = new Random().nextInt();
return String.format("simulator_phone_call_%x", Math.abs(callId));
}
+ @NonNull
+ static Bundle createSimulatorConnectionExtras() {
+ Bundle extras = new Bundle();
+ extras.putBoolean(EXTRA_IS_SIMULATOR_CONNECTION, true);
+ String connectionTag = createUniqueConnectionTag();
+ extras.putString(EXTRA_CONNECTION_TAG, connectionTag);
+ extras.putBoolean(connectionTag, true);
+ return extras;
+ }
+
private SimulatorSimCallManager() {}
}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorSpamCallCreator.java b/java/com/android/dialer/simulator/impl/SimulatorSpamCallCreator.java
index 757658ddf..a843ec03f 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorSpamCallCreator.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorSpamCallCreator.java
@@ -74,6 +74,10 @@ final class SimulatorSpamCallCreator implements SimulatorConnectionService.Liste
DISCONNECT_DELAY_MILLIS);
}
+ @Override
+ public void onConference(
+ @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) {}
+
private void addNextIncomingCall(int callCount) {
if (callCount <= 0) {
LogUtil.i("SimulatorSpamCallCreator.addNextIncomingCall", "done adding calls");
diff --git a/java/com/android/dialer/simulator/impl/SimulatorVideoCall.java b/java/com/android/dialer/simulator/impl/SimulatorVideoCall.java
index 3f00ab183..f7256a11c 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorVideoCall.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorVideoCall.java
@@ -109,6 +109,10 @@ final class SimulatorVideoCall
}
}
+ @Override
+ public void onConference(
+ @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) {}
+
private boolean isVideoAccountEnabled() {
SimulatorSimCallManager.register(context);
return context
@@ -150,15 +154,12 @@ final class SimulatorVideoCall
case Event.DISCONNECT:
connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
break;
- case Event.STATE_CHANGE:
- break;
- case Event.DTMF:
- break;
case Event.SESSION_MODIFY_REQUEST:
ThreadUtil.postDelayedOnUiThread(() -> connection.handleSessionModifyRequest(event), 2000);
break;
default:
- throw Assert.createIllegalStateFailException();
+ LogUtil.i("SimulatorVideoCall.onEvent", "unexpected event: " + event.type);
+ break;
}
}
}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
index 8eefb48d9..f478d55d0 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
@@ -25,6 +25,7 @@ import android.view.ActionProvider;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.ThreadUtil;
+import com.android.dialer.simulator.Simulator;
import com.android.dialer.simulator.Simulator.Event;
/** Entry point in the simulator to create voice calls. */
@@ -37,7 +38,10 @@ final class SimulatorVoiceCall
return new SimulatorSubMenu(context)
.addItem("Incoming call", () -> new SimulatorVoiceCall(context).addNewIncomingCall(false))
.addItem("Outgoing call", () -> new SimulatorVoiceCall(context).addNewOutgoingCall())
- .addItem("Spam call", () -> new SimulatorVoiceCall(context).addNewIncomingCall(true));
+ .addItem("Spam call", () -> new SimulatorVoiceCall(context).addNewIncomingCall(true))
+ .addItem(
+ "GSM conference",
+ () -> new SimulatorConferenceCreator(context, Simulator.CONFERENCE_TYPE_GSM).start(5));
}
private SimulatorVoiceCall(@NonNull Context context) {
@@ -62,21 +66,28 @@ final class SimulatorVoiceCall
@Override
public void onNewOutgoingConnection(@NonNull SimulatorConnection connection) {
- if (connection.getExtras().getBoolean(connectionTag)) {
+ if (isMyConnection(connection)) {
LogUtil.i("SimulatorVoiceCall.onNewOutgoingConnection", "connection created");
handleNewConnection(connection);
- connection.setActive();
+
+ // Telecom will force the connection to switch to Dialing when we return it. Wait until after
+ // we're returned it before changing call state.
+ ThreadUtil.postOnUiThread(connection::setActive);
}
}
@Override
public void onNewIncomingConnection(@NonNull SimulatorConnection connection) {
- if (connection.getExtras().getBoolean(connectionTag)) {
+ if (isMyConnection(connection)) {
LogUtil.i("SimulatorVoiceCall.onNewIncomingConnection", "connection created");
handleNewConnection(connection);
}
}
+ @Override
+ public void onConference(
+ @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) {}
+
private void handleNewConnection(@NonNull SimulatorConnection connection) {
connection.addListener(this);
connection.setConnectionCapabilities(
@@ -85,6 +96,10 @@ final class SimulatorVoiceCall
| Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
}
+ private boolean isMyConnection(@NonNull Connection connection) {
+ return connection.getExtras().getBoolean(connectionTag);
+ }
+
@Override
public void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event) {
switch (event.type) {
@@ -105,15 +120,12 @@ final class SimulatorVoiceCall
case Event.DISCONNECT:
connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
break;
- case Event.STATE_CHANGE:
- break;
- case Event.DTMF:
- break;
case Event.SESSION_MODIFY_REQUEST:
ThreadUtil.postDelayedOnUiThread(() -> connection.handleSessionModifyRequest(event), 2000);
break;
default:
- throw Assert.createIllegalStateFailException();
+ LogUtil.i("SimulatorVoiceCall.onEvent", "unexpected event: " + event.type);
+ break;
}
}
}