Merge "Modify ConnectionServiceAdapter to include Session" am: 6acdeda101 -s ours
am: 0d61bbb380
Change-Id: I77dd1ae685a744b0a10e767075631ef880b96c35
diff --git a/res/values/config.xml b/res/values/config.xml
index be0f72a..4e86503 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -45,4 +45,9 @@
<!-- Flag indicating whether audio should be routed to speaker when docked -->
<bool name="use_speaker_when_docked">true</bool>
+
+ <!-- Flag indicating whether allow (silence rather than reject) the incoming call if it has a
+ different source (connection service) from the existing ringing call when reaching
+ maximum ringing calls. -->
+ <bool name="silence_incoming_when_different_service_and_maximum_ringing">false</bool>
</resources>
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index b9b2fda..a03257d 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -26,6 +26,7 @@
import android.os.RemoteException;
import android.os.Trace;
import android.provider.ContactsContract.Contacts;
+import android.telecom.Conference;
import android.telecom.DisconnectCause;
import android.telecom.Connection;
import android.telecom.GatewayInfo;
@@ -387,6 +388,17 @@
private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
/**
+ * For {@link Connection}s or {@link android.telecom.Conference}s added via a ConnectionManager
+ * using the {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
+ * Connection)} or {@link android.telecom.ConnectionService#addConference(Conference)},
+ * indicates the ID of this call as it was referred to by the {@code ConnectionService} which
+ * originally created it.
+ *
+ * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID} for more information.
+ */
+ private String mOriginalConnectionId;
+
+ /**
* Persists the specified parameters and initializes the new instance.
*
* @param context The context.
@@ -1127,6 +1139,34 @@
}
/**
+ * Perform an in-place replacement of the {@link ConnectionServiceWrapper} for this Call.
+ * Removes the call from its former {@link ConnectionServiceWrapper}, ensuring that the
+ * ConnectionService is NOT unbound if the call count hits zero.
+ * This is used by the {@link ConnectionServiceWrapper} when handling {@link Connection} and
+ * {@link Conference} additions via a ConnectionManager.
+ * The original {@link android.telecom.ConnectionService} will directly add external calls and
+ * conferences to Telecom as well as the ConnectionManager, which will add to Telecom. In these
+ * cases since its first added to via the original CS, we want to change the CS responsible for
+ * the call to the ConnectionManager rather than adding it again as another call/conference.
+ *
+ * @param service The new {@link ConnectionServiceWrapper}.
+ */
+ public void replaceConnectionService(ConnectionServiceWrapper service) {
+ Preconditions.checkNotNull(service);
+
+ if (mConnectionService != null) {
+ ConnectionServiceWrapper serviceTemp = mConnectionService;
+ mConnectionService = null;
+ serviceTemp.removeCall(this);
+ serviceTemp.decrementAssociatedCallCount(true /*isSuppressingUnbind*/);
+ }
+
+ service.incrementAssociatedCallCount();
+ mConnectionService = service;
+ mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
+ }
+
+ /**
* Clears the associated connection service.
*/
void clearConnectionService() {
@@ -2189,6 +2229,24 @@
}
}
+ public void setOriginalConnectionId(String originalConnectionId) {
+ mOriginalConnectionId = originalConnectionId;
+ }
+
+ /**
+ * For calls added via a ConnectionManager using the
+ * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
+ * Connection)}, or {@link android.telecom.ConnectionService#addConference(Conference)} APIS,
+ * indicates the ID of this call as it was referred to by the {@code ConnectionService} which
+ * originally created it.
+ *
+ * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}.
+ * @return The original connection ID.
+ */
+ public String getOriginalConnectionId() {
+ return mOriginalConnectionId;
+ }
+
/**
* Determines if a {@link Call}'s capabilities bitmask indicates that video is supported either
* remotely or locally.
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index b5c99bc..b4952a5 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -76,6 +76,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -388,9 +389,13 @@
if (result.shouldAllowCall) {
if (hasMaximumRingingCalls()) {
- Log.i(this, "onCallFilteringCompleted: Call rejected! Exceeds maximum number of " +
- "ringing calls.");
- rejectCallAndLog(incomingCall);
+ if (shouldSilenceInsteadOfReject(incomingCall)) {
+ incomingCall.silence();
+ } else {
+ Log.i(this, "onCallFilteringCompleted: Call rejected! " +
+ "Exceeds maximum number of ringing calls.");
+ rejectCallAndLog(incomingCall);
+ }
} else if (hasMaximumDialingCalls()) {
Log.i(this, "onCallFilteringCompleted: Call rejected! Exceeds maximum number of " +
"dialing calls.");
@@ -418,6 +423,37 @@
}
}
+ /**
+ * Whether allow (silence rather than reject) the incoming call if it has a different source
+ * (connection service) from the existing ringing call when reaching maximum ringing calls.
+ */
+ private boolean shouldSilenceInsteadOfReject(Call incomingCall) {
+ if (!mContext.getResources().getBoolean(
+ R.bool.silence_incoming_when_different_service_and_maximum_ringing)) {
+ return false;
+ }
+
+ Call ringingCall = null;
+
+ for (Call call : mCalls) {
+ // Only operate on top-level calls
+ if (call.getParentCall() != null) {
+ continue;
+ }
+
+ if (call.isExternalCall()) {
+ continue;
+ }
+
+ if (CallState.RINGING == call.getState() &&
+ call.getConnectionService() == incomingCall.getConnectionService()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
@Override
public void onFailedIncomingCall(Call call) {
setCallState(call, CallState.DISCONNECTED, "failed incoming call");
@@ -1667,6 +1703,12 @@
call.setVideoProvider(parcelableConference.getVideoProvider());
call.setStatusHints(parcelableConference.getStatusHints());
call.putExtras(Call.SOURCE_CONNECTION_SERVICE, parcelableConference.getExtras());
+ // In case this Conference was added via a ConnectionManager, keep track of the original
+ // Connection ID as created by the originating ConnectionService.
+ Bundle extras = parcelableConference.getExtras();
+ if (extras != null && extras.containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
+ call.setOriginalConnectionId(extras.getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID));
+ }
// TODO: Move this to be a part of addCall()
call.addListener(this);
@@ -2070,14 +2112,44 @@
call.setConnectionProperties(connection.getConnectionProperties());
call.setCallerDisplayName(connection.getCallerDisplayName(),
connection.getCallerDisplayNamePresentation());
-
call.addListener(this);
+
+ // In case this connection was added via a ConnectionManager, keep track of the original
+ // Connection ID as created by the originating ConnectionService.
+ Bundle extras = connection.getExtras();
+ if (extras != null && extras.containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
+ call.setOriginalConnectionId(extras.getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID));
+ }
addCall(call);
return call;
}
/**
+ * Determines whether Telecom already knows about a Connection added via the
+ * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
+ * Connection)} API via a ConnectionManager.
+ *
+ * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}.
+ * @param originalConnectionId The new connection ID to check.
+ * @return {@code true} if this connection is already known by Telecom.
+ */
+ Call getAlreadyAddedConnection(String originalConnectionId) {
+ Optional<Call> existingCall = mCalls.stream()
+ .filter(call -> originalConnectionId.equals(call.getOriginalConnectionId()) ||
+ originalConnectionId.equals(call.getId()))
+ .findFirst();
+
+ if (existingCall.isPresent()) {
+ Log.i(this, "isExistingConnectionAlreadyAdded - call %s already added with id %s",
+ originalConnectionId, existingCall.get().getId());
+ return existingCall.get();
+ }
+
+ return null;
+ }
+
+ /**
* @return A new unique telecom call Id.
*/
private String getNextCallId() {
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index b30f52d..a740df4 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -35,7 +35,6 @@
import android.telecom.Logging.Session;
import android.telecom.ParcelableConference;
import android.telecom.ParcelableConnection;
-import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.StatusHints;
import android.telecom.TelecomManager;
@@ -370,6 +369,8 @@
"call id %s", callId);
return;
}
+ logIncoming("addConferenceCall %s %s [%s]", callId, parcelableConference,
+ parcelableConference.getConnectionIds());
// Make sure that there's at least one valid call. For remote connections
// we'll get a add conference msg from both the remote connection service
@@ -387,16 +388,46 @@
return;
}
- // need to create a new Call
PhoneAccountHandle phAcc = null;
if (parcelableConference != null &&
parcelableConference.getPhoneAccount() != null) {
phAcc = parcelableConference.getPhoneAccount();
}
- Call conferenceCall = mCallsManager.createConferenceCall(callId,
- phAcc, parcelableConference);
- mCallIdMapper.addCall(conferenceCall, callId);
- conferenceCall.setConnectionService(ConnectionServiceWrapper.this);
+
+ Bundle connectionExtras = parcelableConference.getExtras();
+
+ String connectIdToCheck = null;
+ if (connectionExtras != null && connectionExtras
+ .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
+ // Conference was added via a connection manager, see if its original id is
+ // known.
+ connectIdToCheck = connectionExtras
+ .getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID);
+ } else {
+ connectIdToCheck = callId;
+ }
+
+ Call conferenceCall;
+ // Check to see if this conference has already been added.
+ Call alreadyAddedConnection = mCallsManager
+ .getAlreadyAddedConnection(connectIdToCheck);
+ if (alreadyAddedConnection != null && mCallIdMapper.getCall(callId) == null) {
+ // We are currently attempting to add the conference via a connection mgr,
+ // and the originating ConnectionService has already added it. Instead of
+ // making a new Telecom call, we will simply add it to the ID mapper here,
+ // and replace the ConnectionService on the call.
+ mCallIdMapper.addCall(alreadyAddedConnection, callId);
+ alreadyAddedConnection.replaceConnectionService(
+ ConnectionServiceWrapper.this);
+ conferenceCall = alreadyAddedConnection;
+ } else {
+ // need to create a new Call
+ Call newConferenceCall = mCallsManager.createConferenceCall(callId,
+ phAcc, parcelableConference);
+ mCallIdMapper.addCall(newConferenceCall, callId);
+ newConferenceCall.setConnectionService(ConnectionServiceWrapper.this);
+ conferenceCall = newConferenceCall;
+ }
Log.d(this, "adding children to conference %s phAcc %s",
parcelableConference.getConnectionIds(), phAcc);
@@ -611,10 +642,11 @@
long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- logIncoming("setConferenceableConnections %s %s", callId,
- conferenceableCallIds);
+
Call call = mCallIdMapper.getCall(callId);
if (call != null) {
+ logIncoming("setConferenceableConnections %s %s", callId,
+ conferenceableCallIds);
List<Call> conferenceableCalls =
new ArrayList<>(conferenceableCallIds.size());
for (String otherId : conferenceableCallIds) {
@@ -658,8 +690,37 @@
phoneAccountHandle = accountHandle;
}
}
+ // Allow the Sim call manager account as well, even if its disabled.
+ if (phoneAccountHandle == null && callingPhoneAccountHandle != null) {
+ if (callingPhoneAccountHandle.equals(
+ mPhoneAccountRegistrar.getSimCallManager(userHandle))) {
+ phoneAccountHandle = callingPhoneAccountHandle;
+ }
+ }
if (phoneAccountHandle != null) {
- logIncoming("addExistingConnection %s %s", callId, connection);
+ logIncoming("addExistingConnection %s %s", callId, connection);
+
+ Bundle connectionExtras = connection.getExtras();
+ String connectIdToCheck = null;
+ if (connectionExtras != null && connectionExtras
+ .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
+ connectIdToCheck = connectionExtras
+ .getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID);
+ } else {
+ connectIdToCheck = callId;
+ }
+ // Check to see if this Connection has already been added.
+ Call alreadyAddedConnection = mCallsManager
+ .getAlreadyAddedConnection(connectIdToCheck);
+
+ if (alreadyAddedConnection != null
+ && mCallIdMapper.getCall(callId) == null) {
+ mCallIdMapper.addCall(alreadyAddedConnection, callId);
+ alreadyAddedConnection
+ .replaceConnectionService(ConnectionServiceWrapper.this);
+ return;
+ }
+
Call existingCall = mCallsManager
.createCallForExistingConnection(callId, connection);
mCallIdMapper.addCall(existingCall, callId);
@@ -1135,11 +1196,13 @@
}
private void logIncoming(String msg, Object... params) {
- Log.d(this, "ConnectionService -> Telecom: " + msg, params);
+ Log.d(this, "ConnectionService -> Telecom[" + mComponentName.flattenToShortString() + "]: "
+ + msg, params);
}
private void logOutgoing(String msg, Object... params) {
- Log.d(this, "Telecom -> ConnectionService: " + msg, params);
+ Log.d(this, "Telecom -> ConnectionService[" + mComponentName.flattenToShortString() + "]: "
+ + msg, params);
}
private void queryRemoteConnectionServices(final UserHandle userHandle,
diff --git a/src/com/android/server/telecom/ServiceBinder.java b/src/com/android/server/telecom/ServiceBinder.java
index c86b5af..5a1ae02 100644
--- a/src/com/android/server/telecom/ServiceBinder.java
+++ b/src/com/android/server/telecom/ServiceBinder.java
@@ -170,7 +170,7 @@
private final String mServiceAction;
/** The component name of the service to bind to. */
- private final ComponentName mComponentName;
+ protected final ComponentName mComponentName;
/** The set of callbacks waiting for notification of the binding's success or failure. */
private final Set<BindCallback> mCallbacks = new ArraySet<>();
@@ -228,12 +228,16 @@
}
final void decrementAssociatedCallCount() {
+ decrementAssociatedCallCount(false /*isSuppressingUnbind*/);
+ }
+
+ final void decrementAssociatedCallCount(boolean isSuppressingUnbind) {
if (mAssociatedCallCount > 0) {
mAssociatedCallCount--;
Log.v(this, "Call count decrement %d, %s", mAssociatedCallCount,
mComponentName.flattenToShortString());
- if (mAssociatedCallCount == 0) {
+ if (!isSuppressingUnbind && mAssociatedCallCount == 0) {
unbind();
}
} else {