Merge "Implement setVisualVoicemailEnabled()" into nyc-mr1-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c003662..61e0a5b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -995,6 +995,8 @@
<string name="incall_error_no_phone_number_supplied">To place a call, enter a valid number.</string>
<!-- In-call screen: call failure message displayed in an error dialog -->
<string name="incall_error_call_failed">Call failed.</string>
+ <!-- In-call screen: call failure message displayed in an error dialog -->
+ <string name="incall_error_cannot_add_call">Call cannot be added at this time.</string>
<!-- In-call screen: status message displayed in a dialog when starting an MMI -->
<string name="incall_status_dialed_mmi">Starting MMI sequence\u2026</string>
<!-- In-call screen: message displayed in an error dialog -->
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 2654ad7..3f49c6f 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -30,6 +30,7 @@
import android.telecom.StatusHints;
import android.telecom.VideoProfile;
import android.telephony.PhoneNumberUtils;
+import android.util.Pair;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
@@ -45,7 +46,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
/**
* Represents an IMS conference call.
@@ -230,11 +230,12 @@
private Uri[] mConferenceHostAddress;
/**
- * The known conference participant connections. The HashMap is keyed by endpoint Uri.
+ * The known conference participant connections. The HashMap is keyed by a Pair containing
+ * the handle and endpoint Uris.
* Access to the hashmap is protected by the {@link #mUpdateSyncRoot}.
*/
- private final HashMap<Uri, ConferenceParticipantConnection>
- mConferenceParticipantConnections = new HashMap<Uri, ConferenceParticipantConnection>();
+ private final HashMap<Pair<Uri, Uri>, ConferenceParticipantConnection>
+ mConferenceParticipantConnections = new HashMap<>();
/**
* Sychronization root used to ensure that updates to the
@@ -637,11 +638,12 @@
boolean newParticipantsAdded = false;
boolean oldParticipantsRemoved = false;
ArrayList<ConferenceParticipant> newParticipants = new ArrayList<>(participants.size());
- HashSet<Uri> participantUserEntities = new HashSet<>(participants.size());
+ HashSet<Pair<Uri,Uri>> participantUserEntities = new HashSet<>(participants.size());
// Add any new participants and update existing.
for (ConferenceParticipant participant : participants) {
- Uri userEntity = participant.getEndpoint();
+ Pair<Uri,Uri> userEntity = new Pair<>(participant.getHandle(),
+ participant.getEndpoint());
participantUserEntities.add(userEntity);
if (!mConferenceParticipantConnections.containsKey(userEntity)) {
@@ -666,17 +668,20 @@
// Set the state of the new participants at once and add to the conference
for (ConferenceParticipant newParticipant : newParticipants) {
ConferenceParticipantConnection connection =
- mConferenceParticipantConnections.get(newParticipant.getEndpoint());
+ mConferenceParticipantConnections.get(new Pair<>(
+ newParticipant.getHandle(),
+ newParticipant.getEndpoint()));
connection.updateState(newParticipant.getState());
}
}
// Finally, remove any participants from the conference that no longer exist in the
// conference event package data.
- Iterator<Map.Entry<Uri, ConferenceParticipantConnection>> entryIterator =
+ Iterator<Map.Entry<Pair<Uri, Uri>, ConferenceParticipantConnection>> entryIterator =
mConferenceParticipantConnections.entrySet().iterator();
while (entryIterator.hasNext()) {
- Map.Entry<Uri, ConferenceParticipantConnection> entry = entryIterator.next();
+ Map.Entry<Pair<Uri, Uri>, ConferenceParticipantConnection> entry =
+ entryIterator.next();
if (!participantUserEntities.contains(entry.getKey())) {
ConferenceParticipantConnection participant = entry.getValue();
@@ -721,7 +726,8 @@
participant, connection);
synchronized(mUpdateSyncRoot) {
- mConferenceParticipantConnections.put(participant.getEndpoint(), connection);
+ mConferenceParticipantConnections.put(new Pair<>(participant.getHandle(),
+ participant.getEndpoint()), connection);
}
mTelephonyConnectionService.addExistingConnection(mConferenceHostPhoneAccountHandle,
connection);
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index d75481b..d017a9e 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -29,8 +29,10 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.stream.Collectors;
/**
* Manages conferences for IMS connections.
@@ -169,8 +171,8 @@
*/
private void recalculateConferenceable() {
Log.v(this, "recalculateConferenceable : %d", mTelephonyConnections.size());
- List<Conferenceable> activeConnections = new ArrayList<>(mTelephonyConnections.size());
- List<Conferenceable> backgroundConnections = new ArrayList<>(mTelephonyConnections.size());
+ HashSet<Conferenceable> conferenceableSet = new HashSet<>(mTelephonyConnections.size() +
+ mImsConferences.size());
// Loop through and collect all calls which are active or holding
for (TelephonyConnection connection : mTelephonyConnections) {
@@ -191,23 +193,23 @@
// If this connection does not support being in a conference call, then it is not
// conferenceable with any other connection.
if (!connection.isConferenceSupported()) {
- connection.setConferenceableConnections(Collections.<Connection>emptyList());
+ connection.setConferenceables(Collections.<Conferenceable>emptyList());
continue;
}
switch (connection.getState()) {
case Connection.STATE_ACTIVE:
- activeConnections.add(connection);
- continue;
+ // fall through
case Connection.STATE_HOLDING:
- backgroundConnections.add(connection);
+ conferenceableSet.add(connection);
continue;
default:
break;
}
- connection.setConferenceableConnections(Collections.<Connection>emptyList());
+ // This connection is not active or holding, so clear all conferencable connections
+ connection.setConferenceables(Collections.<Conferenceable>emptyList());
}
-
+ // Also loop through all active conferences and collect the ones that are ACTIVE or HOLDING.
for (ImsConference conference : mImsConferences) {
if (Log.DEBUG) {
Log.d(this, "recalc - %s %s", conference.getState(), conference);
@@ -222,62 +224,37 @@
switch (conference.getState()) {
case Connection.STATE_ACTIVE:
- activeConnections.add(conference);
- continue;
+ //fall through
case Connection.STATE_HOLDING:
- backgroundConnections.add(conference);
+ conferenceableSet.add(conference);
continue;
default:
break;
}
}
- Log.v(this, "active: %d, holding: %d", activeConnections.size(),
- backgroundConnections.size());
+ Log.v(this, "conferenceableSet size: " + conferenceableSet.size());
- // Go through all the active connections and set the background connections as
- // conferenceable.
- for (Conferenceable conferenceable : activeConnections) {
- if (conferenceable instanceof Connection) {
- Connection connection = (Connection) conferenceable;
- connection.setConferenceables(backgroundConnections);
+ for (Conferenceable c : conferenceableSet) {
+ if (c instanceof Connection) {
+ // Remove this connection from the Set and add all others
+ List<Conferenceable> conferenceables = conferenceableSet
+ .stream()
+ .filter(conferenceable -> c != conferenceable)
+ .collect(Collectors.toList());
+ ((Connection) c).setConferenceables(conferenceables);
+ } else if (c instanceof Conference) {
+ // Remove all conferences from the set, since we can not conference a conference
+ // to another conference.
+ List<Connection> connections = conferenceableSet
+ .stream()
+ .filter(conferenceable -> conferenceable instanceof Connection)
+ .map(conferenceable -> (Connection) conferenceable)
+ .collect(Collectors.toList());
+ // Conference equivalent to setConferenceables that only accepts Connections
+ ((Conference) c).setConferenceableConnections(connections);
}
}
-
- // Go through all the background connections and set the active connections as
- // conferenceable.
- for (Conferenceable conferenceable : backgroundConnections) {
- if (conferenceable instanceof Connection) {
- Connection connection = (Connection) conferenceable;
- connection.setConferenceables(activeConnections);
- }
-
- }
-
- // Set the conference as conferenceable with all the connections
- for (ImsConference conference : mImsConferences) {
- // If this conference is not being hosted on the current device, we cannot conference it
- // with any other connections.
- if (!conference.isConferenceHost()) {
- if (Log.VERBOSE) {
- Log.v(this, "skipping conference (not hosted on this device): %s",
- conference);
- }
- continue;
- }
-
- List<Connection> nonConferencedConnections =
- new ArrayList<>(mTelephonyConnections.size());
- for (TelephonyConnection c : mTelephonyConnections) {
- if (c.getConference() == null && c.isConferenceSupported()) {
- nonConferencedConnections.add(c);
- }
- }
- if (Log.VERBOSE) {
- Log.v(this, "conference conferenceable: %s", nonConferencedConnections);
- }
- conference.setConferenceableConnections(nonConferencedConnections);
- }
}
/**
diff --git a/src/com/android/services/telephony/TelephonyConferenceController.java b/src/com/android/services/telephony/TelephonyConferenceController.java
index 7da9ea5..fbf5ad0 100644
--- a/src/com/android/services/telephony/TelephonyConferenceController.java
+++ b/src/com/android/services/telephony/TelephonyConferenceController.java
@@ -23,6 +23,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
import android.net.Uri;
import android.telecom.Conference;
@@ -113,10 +114,7 @@
*/
private void recalculateConferenceable() {
Log.v(this, "recalculateConferenceable : %d", mTelephonyConnections.size());
-
- List<Connection> activeConnections = new ArrayList<>(mTelephonyConnections.size());
- List<Connection> backgroundConnections = new ArrayList<>(
- mTelephonyConnections.size());
+ HashSet<Connection> conferenceableConnections = new HashSet<>(mTelephonyConnections.size());
// Loop through and collect all calls which are active or holding
for (TelephonyConnection connection : mTelephonyConnections) {
@@ -126,10 +124,9 @@
if (connection.isConferenceSupported() && !participatesInFullConference(connection)) {
switch (connection.getState()) {
case Connection.STATE_ACTIVE:
- activeConnections.add(connection);
- continue;
+ //fall through
case Connection.STATE_HOLDING:
- backgroundConnections.add(connection);
+ conferenceableConnections.add(connection);
continue;
default:
break;
@@ -139,34 +136,30 @@
connection.setConferenceableConnections(Collections.<Connection>emptyList());
}
- Log.v(this, "active: %d, holding: %d",
- activeConnections.size(), backgroundConnections.size());
+ Log.v(this, "conferenceable: " + conferenceableConnections.size());
- // Go through all the active connections and set the background connections as
- // conferenceable.
- for (Connection connection : activeConnections) {
- connection.setConferenceableConnections(backgroundConnections);
+ // Go through all the conferenceable connections and add all other conferenceable
+ // connections that is not the connection itself
+ for (Connection c : conferenceableConnections) {
+ List<Connection> connections = conferenceableConnections
+ .stream()
+ // Filter out this connection from the list of connections
+ .filter(connection -> c != connection)
+ .collect(Collectors.toList());
+ c.setConferenceableConnections(connections);
}
- // Go through all the background connections and set the active connections as
- // conferenceable.
- for (Connection connection : backgroundConnections) {
- connection.setConferenceableConnections(activeConnections);
- }
-
- // Set the conference as conferenceable with all the connections
+ // Set the conference as conferenceable with all of the connections that are not in the
+ // conference.
if (mTelephonyConference != null && !isFullConference(mTelephonyConference)) {
- List<Connection> nonConferencedConnections =
- new ArrayList<>(mTelephonyConnections.size());
- for (TelephonyConnection c : mTelephonyConnections) {
- if (c.isConferenceSupported() && c.getConference() == null) {
- nonConferencedConnections.add(c);
- }
- }
- Log.v(this, "conference conferenceable: %s", nonConferencedConnections);
+ List<Connection> nonConferencedConnections = mTelephonyConnections
+ .stream()
+ // Only retrieve Connections that are not in a conference (but support
+ // conferences).
+ .filter(c -> c.isConferenceSupported() && c.getConference() == null)
+ .collect(Collectors.toList());
mTelephonyConference.setConferenceableConnections(nonConferencedConnections);
}
-
// TODO: Do not allow conferencing of already conferenced connections.
}
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index a9f2e14..a8f6bca 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -53,6 +53,7 @@
import com.android.phone.R;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
@@ -242,6 +243,17 @@
// connecting it to the underlying Phone.
return emergencyConnection;
} else {
+ if (!canAddCall() && !isEmergencyNumber) {
+ Log.d(this, "onCreateOutgoingConnection, cannot add call .");
+ return Connection.createFailedConnection(
+ new DisconnectCause(DisconnectCause.ERROR,
+ getApplicationContext().getText(
+ R.string.incall_error_cannot_add_call),
+ getApplicationContext().getText(
+ R.string.incall_error_cannot_add_call),
+ "Add call restricted due to ongoing video call"));
+ }
+
// Get the right phone object from the account data passed in.
final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber);
Connection resultConnection = getTelephonyConnection(request, number, isEmergencyNumber,
@@ -255,6 +267,21 @@
}
}
+ /**
+ * @return {@code true} if any other call is disabling the ability to add calls, {@code false}
+ * otherwise.
+ */
+ private boolean canAddCall() {
+ Collection<Connection> connections = getAllConnections();
+ for (Connection connection : connections) {
+ if (connection.getExtras() != null &&
+ connection.getExtras().getBoolean(Connection.EXTRA_DISABLE_ADD_CALL, false)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private Connection getTelephonyConnection(final ConnectionRequest request, final String number,
boolean isEmergencyNumber, final Uri handle, Phone phone) {