Add support for adding external calls to Telecom.
- Modify PstnIncomingCallNotifier to include the external call ID in the
"addNewUnknownCall" request to Telecom; this ensures we can key back in on
the ID when Telephony is asked to add that connection.
- Modify TelephonyConnectionService so it can add new external calls.
- Update TelephonyConnection to translate the new internal connection
properties related to multi-endpoint to ones Telecom can relate to.
Bug: 27458894
Change-Id: I47ad22aaeed955bcecbae894486c00539f8f7ea7
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index 91d75c4..cfcf466 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -29,6 +29,8 @@
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
+import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
+import com.android.internal.telephony.imsphone.ImsExternalConnection;
import com.android.phone.PhoneUtils;
import com.google.common.base.Preconditions;
@@ -177,6 +179,18 @@
Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, connection.getAddress(), null);
extras.putParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE, uri);
}
+ // ImsExternalConnections are keyed by a unique mCallId; include this as an extra on
+ // the call to addNewUknownCall in Telecom. This way when the request comes back to the
+ // TelephonyConnectionService, we will be able to determine which unknown connection is
+ // being added.
+ if (connection instanceof ImsExternalConnection) {
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ ImsExternalConnection externalConnection = (ImsExternalConnection) connection;
+ extras.putInt(ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID,
+ externalConnection.getCallId());
+ }
TelecomManager.from(mPhone.getContext()).addNewUnknownCall(
PhoneUtils.makePstnPhoneAccountHandle(mPhone), extras);
} else {
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index ba20e74..8e67549 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -614,6 +614,8 @@
newCapabilities = applyOriginalConnectionCapabilities(newCapabilities);
newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PAUSE_VIDEO,
mIsVideoPauseSupported && isVideoCapable());
+ newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PULL_CALL,
+ isExternalConnection() && isPullable());
newCapabilities = applyConferenceTerminationCapabilities(newCapabilities);
if (getConnectionCapabilities() != newCapabilities) {
@@ -642,6 +644,8 @@
newProperties = changeBitmask(newProperties, PROPERTY_HIGH_DEF_AUDIO, mHasHighDefAudio);
newProperties = changeBitmask(newProperties, PROPERTY_WIFI, mIsWifi);
+ newProperties = changeBitmask(newProperties, PROPERTY_IS_EXTERNAL_CALL,
+ isExternalConnection());
if (getConnectionProperties() != newProperties) {
setConnectionProperties(newProperties);
@@ -1100,6 +1104,33 @@
}
/**
+ * Determines if the current connection is an external connection.
+ *
+ * A connection is deemed to be external if the original connection capabilities state that it
+ * is.
+ *
+ * @return {@code true} if the connection is external, {@code false} otherwise.
+ */
+ private boolean isExternalConnection() {
+ return can(mOriginalConnectionCapabilities, Capability.IS_EXTERNAL_CONNECTION)
+ && can(mOriginalConnectionCapabilities,
+ Capability.IS_EXTERNAL_CONNECTION);
+ }
+
+ /**
+ * Determines if the current connection is pullable.
+ *
+ * A connection is deemed to be pullable if the original connection capabilities state that it
+ * is.
+ *
+ * @return {@code true} if the connection is pullable, {@code false} otherwise.
+ */
+ private boolean isPullable() {
+ return can(mOriginalConnectionCapabilities, Capability.IS_EXTERNAL_CONNECTION)
+ && can(mOriginalConnectionCapabilities, Capability.IS_PULLABLE);
+ }
+
+ /**
* Applies capabilities specific to conferences termination to the
* {@code ConnectionCapabilities} bit-mask.
*
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index d93fb96..4844df9 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.Bundle;
import android.telecom.Conference;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
@@ -45,6 +46,8 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
+import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.phone.MMIDialogActivity;
import com.android.phone.PhoneUtils;
import com.android.phone.R;
@@ -396,26 +399,51 @@
android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
"Phone is null"));
}
+ Bundle extras = request.getExtras();
final List<com.android.internal.telephony.Connection> allConnections = new ArrayList<>();
- final Call ringingCall = phone.getRingingCall();
- if (ringingCall.hasConnections()) {
- allConnections.addAll(ringingCall.getConnections());
- }
- final Call foregroundCall = phone.getForegroundCall();
- if ((foregroundCall.getState() != Call.State.DISCONNECTED)
- && (foregroundCall.hasConnections())) {
- allConnections.addAll(foregroundCall.getConnections());
- }
- if (phone.getImsPhone() != null) {
- final Call imsFgCall = phone.getImsPhone().getForegroundCall();
- if ((imsFgCall.getState() != Call.State.DISCONNECTED) && imsFgCall.hasConnections()) {
- allConnections.addAll(imsFgCall.getConnections());
+
+ // Handle the case where an unknown connection has an IMS external call ID specified; we can
+ // skip the rest of the guesswork and just grad that unknown call now.
+ if (phone.getImsPhone() != null && extras != null &&
+ extras.containsKey(ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID)) {
+
+ ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
+ ImsExternalCallTracker externalCallTracker = imsPhone.getExternalCallTracker();
+ int externalCallId = extras.getInt(ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID,
+ -1);
+
+ if (externalCallTracker != null) {
+ com.android.internal.telephony.Connection connection =
+ externalCallTracker.getConnectionById(externalCallId);
+
+ if (connection != null) {
+ allConnections.add(connection);
+ }
}
}
- final Call backgroundCall = phone.getBackgroundCall();
- if (backgroundCall.hasConnections()) {
- allConnections.addAll(phone.getBackgroundCall().getConnections());
+
+ if (allConnections.isEmpty()) {
+ final Call ringingCall = phone.getRingingCall();
+ if (ringingCall.hasConnections()) {
+ allConnections.addAll(ringingCall.getConnections());
+ }
+ final Call foregroundCall = phone.getForegroundCall();
+ if ((foregroundCall.getState() != Call.State.DISCONNECTED)
+ && (foregroundCall.hasConnections())) {
+ allConnections.addAll(foregroundCall.getConnections());
+ }
+ if (phone.getImsPhone() != null) {
+ final Call imsFgCall = phone.getImsPhone().getForegroundCall();
+ if ((imsFgCall.getState() != Call.State.DISCONNECTED) && imsFgCall
+ .hasConnections()) {
+ allConnections.addAll(imsFgCall.getConnections());
+ }
+ }
+ final Call backgroundCall = phone.getBackgroundCall();
+ if (backgroundCall.hasConnections()) {
+ allConnections.addAll(phone.getBackgroundCall().getConnections());
+ }
}
com.android.internal.telephony.Connection unknownConnection = null;