Fix dialer simulator for conference calling funcitonality.
Updated the following contents:
1.Fix the order of spawning connections for GSM conference.
2.Make VOLTE conference call more realistic.
3.Fix minor bugs about simulator.
4.Add SimulatorConnectionsBank class to store connection tags created by simulator.
5.Fix tests influenced by SimulatorConnectionsBank.
WANT_LGTM=wangqi
Bug: 67785540
Test: In dialer lab.
PiperOrigin-RevId: 176127584
Change-Id: I846174b97ed9329df6347583c41095f45f43494b
diff --git a/java/com/android/dialer/simulator/Simulator.java b/java/com/android/dialer/simulator/Simulator.java
index 2094b42..d75d10e 100644
--- a/java/com/android/dialer/simulator/Simulator.java
+++ b/java/com/android/dialer/simulator/Simulator.java
@@ -42,6 +42,19 @@
static final int CONFERENCE_TYPE_GSM = 1;
static final int CONFERENCE_TYPE_VOLTE = 2;
+ /** The types of connection service listener events */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ ON_NEW_OUTGOING_CONNECTION,
+ ON_NEW_INCOMING_CONNECTION,
+ ON_CONFERENCE,
+ })
+ @interface ConnectionServiceEventType {}
+
+ static final int ON_NEW_OUTGOING_CONNECTION = 1;
+ static final int ON_NEW_INCOMING_CONNECTION = 2;
+ static final int ON_CONFERENCE = 3;
+
/** Information about a connection event. */
public static class Event {
/** The type of connection event. */
diff --git a/java/com/android/dialer/simulator/SimulatorComponent.java b/java/com/android/dialer/simulator/SimulatorComponent.java
index f14496b..dee1882 100644
--- a/java/com/android/dialer/simulator/SimulatorComponent.java
+++ b/java/com/android/dialer/simulator/SimulatorComponent.java
@@ -26,6 +26,8 @@
public abstract Simulator getSimulator();
+ public abstract SimulatorConnectionsBank getSimulatorConnectionsBank();
+
public static SimulatorComponent get(Context context) {
return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component())
.simulatorComponent();
diff --git a/java/com/android/dialer/simulator/SimulatorConnectionsBank.java b/java/com/android/dialer/simulator/SimulatorConnectionsBank.java
new file mode 100644
index 0000000..23c0042
--- /dev/null
+++ b/java/com/android/dialer/simulator/SimulatorConnectionsBank.java
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.telecom.Connection;
+import com.android.dialer.simulator.Simulator.ConferenceType;
+import java.util.List;
+
+/**
+ * Used to create a shared connections bank which contains methods to manipulate connections. This
+ * is used mainly for conference calling.
+ */
+public interface SimulatorConnectionsBank {
+
+ /** Add a connection into bank. */
+ void add(Connection connection);
+
+ /** Remove a connection from bank. */
+ void remove(Connection connection);
+
+ /** Merge all existing connections created by simulator into a conference. */
+ void mergeAllConnections(@ConferenceType int conferenceType, Context context);
+
+ /** Set all connections created by simulator to disconnected. */
+ void disconnectAllConnections();
+
+ /**
+ * Update conferenceable connections for all connections in bank (usually after adding a new
+ * connection). Before calling this method, make sure all connections are returned by
+ * ConnectionService.
+ */
+ void updateConferenceableConnections();
+
+ /** Determine whether a connection is created by simulator. */
+ boolean isSimulatorConnection(@NonNull Connection connection);
+
+ /** Get all connections tags from bank. */
+ List<String> getConnectionTags();
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java b/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java
index d024993..36c1995 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java
@@ -19,8 +19,6 @@
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;
@@ -28,8 +26,9 @@
import com.android.dialer.common.concurrent.ThreadUtil;
import com.android.dialer.simulator.Simulator;
import com.android.dialer.simulator.Simulator.Event;
+import com.android.dialer.simulator.SimulatorComponent;
+import com.android.dialer.simulator.SimulatorConnectionsBank;
import java.util.ArrayList;
-import java.util.List;
import java.util.Locale;
/** Creates a conference with a given number of participants. */
@@ -38,32 +37,59 @@
SimulatorConnection.Listener,
SimulatorConference.Listener {
private static final String EXTRA_CALL_COUNT = "call_count";
-
+ private static final String RECONNECT = "reconnect";
@NonNull private final Context context;
- @NonNull private final List<String> connectionTags = new ArrayList<>();
+
+ private final SimulatorConnectionsBank simulatorConnectionsBank;
+
+ private boolean onNewIncomingConnectionEnabled = false;
+
@Simulator.ConferenceType private final int conferenceType;
public SimulatorConferenceCreator(
@NonNull Context context, @Simulator.ConferenceType int conferenceType) {
this.context = Assert.isNotNull(context);
this.conferenceType = conferenceType;
+ simulatorConnectionsBank = SimulatorComponent.get(context).getSimulatorConnectionsBank();
}
void start(int callCount) {
+ onNewIncomingConnectionEnabled = true;
SimulatorConnectionService.addListener(this);
- addNextCall(callCount);
+ if (conferenceType == Simulator.CONFERENCE_TYPE_VOLTE) {
+ addNextCall(callCount, true);
+ } else if (conferenceType == Simulator.CONFERENCE_TYPE_GSM) {
+ addNextCall(callCount, false);
+ }
}
-
- private void addNextCall(int callCount) {
+ /**
+ * Add a call in a process of making a conference.
+ *
+ * @param callCount the remaining number of calls to make
+ * @param reconnect whether all connections should reconnect once (connections are reconnected
+ * once in making VoLTE conference)
+ */
+ private void addNextCall(int callCount, boolean reconnect) {
LogUtil.i("SimulatorConferenceCreator.addNextIncomingCall", "callCount: " + callCount);
if (callCount <= 0) {
LogUtil.i("SimulatorConferenceCreator.addNextCall", "done adding calls");
+ if (reconnect) {
+ simulatorConnectionsBank.disconnectAllConnections();
+ addNextCall(simulatorConnectionsBank.getConnectionTags().size(), false);
+ } else {
+ simulatorConnectionsBank.mergeAllConnections(conferenceType, context);
+ SimulatorConnectionService.removeListener(this);
+ }
return;
}
-
String callerId = String.format(Locale.US, "+1-650-234%04d", callCount);
Bundle extras = new Bundle();
extras.putInt(EXTRA_CALL_COUNT, callCount - 1);
+ extras.putBoolean(RECONNECT, reconnect);
+ addConferenceCall(callerId, extras);
+ }
+
+ private void addConferenceCall(String number, Bundle extras) {
switch (conferenceType) {
case Simulator.CONFERENCE_TYPE_VOLTE:
extras.putBoolean("ISVOLTE", true);
@@ -71,35 +97,25 @@
default:
break;
}
- connectionTags.add(
- SimulatorSimCallManager.addNewIncomingCall(context, callerId, false /* isVideo */, extras));
+ SimulatorSimCallManager.addNewIncomingCall(context, number, false /* isVideo */, extras);
}
@Override
public void onNewIncomingConnection(@NonNull SimulatorConnection connection) {
- if (!isMyConnection(connection)) {
+ if (!onNewIncomingConnectionEnabled) {
+ return;
+ }
+ if (!simulatorConnectionsBank.isSimulatorConnection(connection)) {
LogUtil.i("SimulatorConferenceCreator.onNewOutgoingConnection", "unknown connection");
return;
}
-
LogUtil.i("SimulatorConferenceCreator.onNewOutgoingConnection", "connection created");
connection.addListener(this);
-
// Once the connection is active, go ahead and conference it and add the next call.
ThreadUtil.postDelayedOnUiThread(
() -> {
- SimulatorConference conference = findCurrentConference();
- if (conference == null) {
- conference =
- SimulatorConference.newGsmConference(
- SimulatorSimCallManager.getSystemPhoneAccountHandle(context));
- conference.addListener(this);
- SimulatorConnectionService.getInstance().addConference(conference);
- }
- updateConferenceableConnections();
connection.setActive();
- conference.addConnection(connection);
- addNextCall(getCallCount(connection));
+ addNextCall(getCallCount(connection), shouldReconnect(connection));
},
1000);
}
@@ -115,7 +131,8 @@
public void onConference(
@NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) {
LogUtil.enterBlock("SimulatorConferenceCreator.onConference");
- if (!isMyConnection(connection1) || !isMyConnection(connection2)) {
+ if (!simulatorConnectionsBank.isSimulatorConnection(connection1)
+ || !simulatorConnectionsBank.isSimulatorConnection(connection2)) {
LogUtil.i("SimulatorConferenceCreator.onConference", "unknown connections, ignoring");
return;
}
@@ -125,7 +142,6 @@
} else if (connection2.getConference() != null) {
connection2.getConference().addConnection(connection1);
} else {
- Assert.checkArgument(conferenceType == Simulator.CONFERENCE_TYPE_GSM);
SimulatorConference conference =
SimulatorConference.newGsmConference(
SimulatorSimCallManager.getSystemPhoneAccountHandle(context));
@@ -136,54 +152,14 @@
}
}
- 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);
}
+ private static boolean shouldReconnect(@NonNull Connection connection) {
+ return connection.getExtras().getBoolean(RECONNECT);
+ }
+
@Override
public void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event) {
switch (event.type) {
@@ -222,6 +198,7 @@
for (Connection connection : new ArrayList<>(conference.getConnections())) {
connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
}
+ conference.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
break;
default:
LogUtil.i(
diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnection.java b/java/com/android/dialer/simulator/impl/SimulatorConnection.java
index 168f5db..2a24d8f 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorConnection.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorConnection.java
@@ -24,6 +24,8 @@
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.simulator.Simulator.Event;
+import com.android.dialer.simulator.SimulatorComponent;
+import com.android.dialer.simulator.SimulatorConnectionsBank;
import java.util.ArrayList;
import java.util.List;
@@ -31,6 +33,7 @@
public final class SimulatorConnection extends Connection {
private final List<Listener> listeners = new ArrayList<>();
private final List<Event> events = new ArrayList<>();
+ private final SimulatorConnectionsBank simulatorConnectionsBank;
private int currentState = STATE_NEW;
SimulatorConnection(@NonNull Context context, @NonNull ConnectionRequest request) {
@@ -47,6 +50,7 @@
setConnectionCapabilities(getConnectionCapabilities() | CAPABILITY_SEPARATE_FROM_CONFERENCE);
}
setVideoProvider(new SimulatorVideoProvider(context, this));
+ simulatorConnectionsBank = SimulatorComponent.get(context).getSimulatorConnectionsBank();
}
public void addListener(@NonNull Listener listener) {
@@ -71,6 +75,7 @@
@Override
public void onReject() {
LogUtil.enterBlock("SimulatorConnection.onReject");
+ simulatorConnectionsBank.remove(this);
onEvent(new Event(Event.REJECT));
}
@@ -89,6 +94,7 @@
@Override
public void onDisconnect() {
LogUtil.enterBlock("SimulatorConnection.onDisconnect");
+ simulatorConnectionsBank.remove(this);
onEvent(new Event(Event.DISCONNECT));
}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java b/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java
index 465890c..e6bf99f 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java
@@ -28,6 +28,9 @@
import android.widget.Toast;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.ThreadUtil;
+import com.android.dialer.simulator.SimulatorComponent;
+import com.android.dialer.simulator.SimulatorConnectionsBank;
import java.util.ArrayList;
import java.util.List;
@@ -35,6 +38,7 @@
public class SimulatorConnectionService extends ConnectionService {
private static final List<Listener> listeners = new ArrayList<>();
private static SimulatorConnectionService instance;
+ private SimulatorConnectionsBank simulatorConnectionsBank;
public static SimulatorConnectionService getInstance() {
return instance;
@@ -52,12 +56,14 @@
public void onCreate() {
super.onCreate();
instance = this;
+ simulatorConnectionsBank = SimulatorComponent.get(this).getSimulatorConnectionsBank();
}
@Override
public void onDestroy() {
LogUtil.enterBlock("SimulatorConnectionService.onDestroy");
instance = null;
+ simulatorConnectionsBank = null;
super.onDestroy();
}
@@ -78,7 +84,12 @@
SimulatorConnection connection = new SimulatorConnection(this, request);
connection.setDialing();
connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
-
+ simulatorConnectionsBank.add(connection);
+ ThreadUtil.postOnUiThread(
+ () ->
+ SimulatorComponent.get(instance)
+ .getSimulatorConnectionsBank()
+ .updateConferenceableConnections());
for (Listener listener : listeners) {
listener.onNewOutgoingConnection(connection);
}
@@ -102,7 +113,12 @@
SimulatorConnection connection = new SimulatorConnection(this, request);
connection.setRinging();
connection.setAddress(getPhoneNumber(request), TelecomManager.PRESENTATION_ALLOWED);
-
+ simulatorConnectionsBank.add(connection);
+ ThreadUtil.postOnUiThread(
+ () ->
+ SimulatorComponent.get(instance)
+ .getSimulatorConnectionsBank()
+ .updateConferenceableConnections());
for (Listener listener : listeners) {
listener.onNewIncomingConnection(connection);
}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnectionsBankImpl.java b/java/com/android/dialer/simulator/impl/SimulatorConnectionsBankImpl.java
new file mode 100644
index 0000000..75f144f
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorConnectionsBankImpl.java
@@ -0,0 +1,147 @@
+/*
+ * 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.support.annotation.NonNull;
+import android.telecom.Conferenceable;
+import android.telecom.Connection;
+import android.telecom.DisconnectCause;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.simulator.Simulator;
+import com.android.dialer.simulator.Simulator.ConferenceType;
+import com.android.dialer.simulator.Simulator.Event;
+import com.android.dialer.simulator.SimulatorConnectionsBank;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.inject.Inject;
+
+/** Wraps a list of connection tags and common methods around the connection tags list. */
+public class SimulatorConnectionsBankImpl
+ implements SimulatorConnectionsBank, SimulatorConference.Listener {
+ private final List<String> connectionTags = new ArrayList<>();
+
+ @Inject
+ public SimulatorConnectionsBankImpl() {}
+
+ @Override
+ public List<String> getConnectionTags() {
+ return connectionTags;
+ }
+
+ @Override
+ public void add(Connection connection) {
+ connectionTags.add(SimulatorSimCallManager.getConnectionTag(connection));
+ }
+
+ @Override
+ public void remove(Connection connection) {
+ connectionTags.remove(SimulatorSimCallManager.getConnectionTag(connection));
+ }
+
+ @Override
+ public void mergeAllConnections(@ConferenceType int conferenceType, Context context) {
+ SimulatorConference simulatorConference = null;
+ if (conferenceType == Simulator.CONFERENCE_TYPE_GSM) {
+ simulatorConference =
+ SimulatorConference.newGsmConference(
+ SimulatorSimCallManager.getSystemPhoneAccountHandle(context));
+ } else if (conferenceType == Simulator.CONFERENCE_TYPE_VOLTE) {
+ simulatorConference =
+ SimulatorConference.newVoLteConference(
+ SimulatorSimCallManager.getSystemPhoneAccountHandle(context));
+ }
+ Collection<Connection> connections =
+ SimulatorConnectionService.getInstance().getAllConnections();
+ for (Connection connection : connections) {
+ simulatorConference.addConnection(connection);
+ }
+ simulatorConference.addListener(this);
+ SimulatorConnectionService.getInstance().addConference(simulatorConference);
+ }
+
+ @Override
+ public void disconnectAllConnections() {
+ Collection<Connection> connections =
+ SimulatorConnectionService.getInstance().getAllConnections();
+ for (Connection connection : connections) {
+ connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
+ }
+ }
+
+ @Override
+ public void updateConferenceableConnections() {
+ LogUtil.enterBlock("SimulatorConferenceCreator.updateConferenceableConnections");
+ for (String connectionTag : connectionTags) {
+ SimulatorConnection connection = SimulatorSimCallManager.findConnectionByTag(connectionTag);
+ List<Conferenceable> conferenceables = getSimulatorConferenceables();
+ conferenceables.remove(connection);
+ conferenceables.remove(connection.getConference());
+ connection.setConferenceables(conferenceables);
+ }
+ }
+
+ private List<Conferenceable> getSimulatorConferenceables() {
+ 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;
+ }
+
+ @Override
+ public boolean isSimulatorConnection(@NonNull Connection connection) {
+ for (String connectionTag : connectionTags) {
+ if (connection.getExtras().getBoolean(connectionTag)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @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));
+ }
+ conference.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/SimulatorModule.java b/java/com/android/dialer/simulator/impl/SimulatorModule.java
index c0cca27..2bc72c9 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorModule.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorModule.java
@@ -17,6 +17,7 @@
package com.android.dialer.simulator.impl;
import com.android.dialer.simulator.Simulator;
+import com.android.dialer.simulator.SimulatorConnectionsBank;
import dagger.Binds;
import dagger.Module;
import javax.inject.Singleton;
@@ -27,4 +28,9 @@
@Binds
@Singleton
public abstract Simulator bindsSimulator(SimulatorImpl simulator);
+
+ @Binds
+ @Singleton
+ public abstract SimulatorConnectionsBank bindsSimulatorConnectionsBank(
+ SimulatorConnectionsBankImpl simulatorConnectionsBank);
}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
index d2eba6b..451896b 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
@@ -52,6 +52,8 @@
private SimulatorVoiceCall(@NonNull Context context) {
this.context = Assert.isNotNull(context);
SimulatorConnectionService.addListener(this);
+ SimulatorConnectionService.addListener(
+ new SimulatorConferenceCreator(context, Simulator.CONFERENCE_TYPE_GSM));
}
private void addNewIncomingCall(boolean isSpam) {