Support indicating call direction on existing connections.
This is important for ensuring the original call direction for existing
connections added from Conference Event Packages matches the call direction
of the original calls merged into the conference.
Also moved a utility function into ConferenceParticipant from
ConferenceParticipantConnection to make it generically usable inside
Telephony.
Test: Run all unit tests.
Test: Manual testing using VoLTE conference calls with mix of MO and MT
calls; verify call logging is appropriate.
Bug: 134471046
Change-Id: Iab09397b811782ab0f876aac02070e3447d81f09
diff --git a/telecomm/java/android/telecom/ConferenceParticipant.java b/telecomm/java/android/telecom/ConferenceParticipant.java
index 2f1505c..5e4818a 100644
--- a/telecomm/java/android/telecom/ConferenceParticipant.java
+++ b/telecomm/java/android/telecom/ConferenceParticipant.java
@@ -19,6 +19,7 @@
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
@@ -69,18 +70,28 @@
private long mConnectElapsedTime;
/**
+ * The direction of the call;
+ * {@link Call.Details#DIRECTION_INCOMING} for incoming calls, or
+ * {@link Call.Details#DIRECTION_OUTGOING} for outgoing calls.
+ */
+ private int mCallDirection;
+
+ /**
* Creates an instance of {@code ConferenceParticipant}.
*
* @param handle The conference participant's handle (e.g., phone number).
* @param displayName The display name for the participant.
* @param endpoint The enpoint Uri which uniquely identifies this conference participant.
* @param state The state of the participant in the conference.
+ * @param callDirection The direction of the call (incoming/outgoing).
*/
- public ConferenceParticipant(Uri handle, String displayName, Uri endpoint, int state) {
+ public ConferenceParticipant(Uri handle, String displayName, Uri endpoint, int state,
+ int callDirection) {
mHandle = handle;
mDisplayName = displayName;
mEndpoint = endpoint;
mState = state;
+ mCallDirection = callDirection;
}
/**
@@ -96,7 +107,16 @@
String displayName = source.readString();
Uri endpoint = source.readParcelable(classLoader);
int state = source.readInt();
- return new ConferenceParticipant(handle, displayName, endpoint, state);
+ long connectTime = source.readLong();
+ long elapsedRealTime = source.readLong();
+ int callDirection = source.readInt();
+ ConferenceParticipant participant =
+ new ConferenceParticipant(handle, displayName, endpoint, state,
+ callDirection);
+ participant.setConnectTime(connectTime);
+ participant.setConnectElapsedTime(elapsedRealTime);
+ participant.setCallDirection(callDirection);
+ return participant;
}
@Override
@@ -170,6 +190,9 @@
dest.writeString(mDisplayName);
dest.writeParcelable(mEndpoint, 0);
dest.writeInt(mState);
+ dest.writeLong(mConnectTime);
+ dest.writeLong(mConnectElapsedTime);
+ dest.writeInt(mCallDirection);
}
/**
@@ -192,6 +215,8 @@
sb.append(getConnectTime());
sb.append(" ConnectElapsedTime: ");
sb.append(getConnectElapsedTime());
+ sb.append(" Direction: ");
+ sb.append(getCallDirection() == Call.Details.DIRECTION_INCOMING ? "Incoming" : "Outgoing");
sb.append("]");
return sb.toString();
}
@@ -239,7 +264,7 @@
}
/**
- * The connect elpased time of the participant to the conference.
+ * The connect elapsed time of the participant to the conference.
*/
public long getConnectElapsedTime() {
return mConnectElapsedTime;
@@ -248,4 +273,76 @@
public void setConnectElapsedTime(long connectElapsedTime) {
mConnectElapsedTime = connectElapsedTime;
}
+
+ /**
+ * @return The direction of the call (incoming/outgoing).
+ */
+ public @Call.Details.CallDirection int getCallDirection() {
+ return mCallDirection;
+ }
+
+ /**
+ * Sets the direction of the call.
+ * @param callDirection Whether the call is incoming or outgoing.
+ */
+ public void setCallDirection(@Call.Details.CallDirection int callDirection) {
+ mCallDirection = callDirection;
+ }
+
+ /**
+ * Attempts to build a tel: style URI from a conference participant.
+ * Conference event package data contains SIP URIs, so we try to extract the phone number and
+ * format into a typical tel: style URI.
+ *
+ * @param address The conference participant's address.
+ * @param countryIso The country ISO of the current subscription; used when formatting the
+ * participant phone number to E.164 format.
+ * @return The participant's address URI.
+ * @hide
+ */
+ @VisibleForTesting
+ public static Uri getParticipantAddress(Uri address, String countryIso) {
+ if (address == null) {
+ return address;
+ }
+ // Even if address is already in tel: format, still parse it and rebuild.
+ // This is to recognize tel URIs such as:
+ // tel:6505551212;phone-context=ims.mnc012.mcc034.3gppnetwork.org
+
+ // Conference event package participants are identified using SIP URIs (see RFC3261).
+ // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
+ // Per RFC3261, the "user" can be a telephone number.
+ // For example: sip:1650555121;phone-context=blah.com@host.com
+ // In this case, the phone number is in the user field of the URI, and the parameters can be
+ // ignored.
+ //
+ // A SIP URI can also specify a phone number in a format similar to:
+ // sip:+1-212-555-1212@something.com;user=phone
+ // In this case, the phone number is again in user field and the parameters can be ignored.
+ // We can get the user field in these instances by splitting the string on the @, ;, or :
+ // and looking at the first found item.
+ String number = address.getSchemeSpecificPart();
+ if (TextUtils.isEmpty(number)) {
+ return address;
+ }
+
+ String numberParts[] = number.split("[@;:]");
+ if (numberParts.length == 0) {
+ return address;
+ }
+ number = numberParts[0];
+
+ // Attempt to format the number in E.164 format and use that as part of the TEL URI.
+ // RFC2806 recommends to format telephone numbers using E.164 since it is independent of
+ // how the dialing of said numbers takes place.
+ // If conversion to E.164 fails, the returned value is null. In that case, fallback to the
+ // number which was in the CEP data.
+ String formattedNumber = null;
+ if (!TextUtils.isEmpty(countryIso)) {
+ formattedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
+ }
+
+ return Uri.fromParts(PhoneAccount.SCHEME_TEL,
+ formattedNumber != null ? formattedNumber : number, null);
+ }
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 47587c5..0983eea 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -1793,6 +1793,11 @@
private ConnectionService mConnectionService;
private Bundle mExtras;
private final Object mExtrasLock = new Object();
+ /**
+ * The direction of the connection; used where an existing connection is created and we need to
+ * communicate to Telecom whether its incoming or outgoing.
+ */
+ private @Call.Details.CallDirection int mCallDirection = Call.Details.DIRECTION_UNKNOWN;
/**
* Tracks the key set for the extras bundle provided on the last invocation of
@@ -3357,4 +3362,21 @@
l.onConnectionEvent(this, event, extras);
}
}
+
+ /**
+ * @return The direction of the call.
+ * @hide
+ */
+ public final @Call.Details.CallDirection int getCallDirection() {
+ return mCallDirection;
+ }
+
+ /**
+ * Sets the direction of this connection.
+ * @param callDirection The direction of this connection.
+ * @hide
+ */
+ public void setCallDirection(@Call.Details.CallDirection int callDirection) {
+ mCallDirection = callDirection;
+ }
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 626fcc4..3548810 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -2144,7 +2144,8 @@
connection.getDisconnectCause(),
emptyList,
connection.getExtras(),
- conferenceId);
+ conferenceId,
+ connection.getCallDirection());
mAdapter.addExistingConnection(id, parcelableConnection);
}
}
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
index dab1c6e..4734af6 100644
--- a/telecomm/java/android/telecom/ParcelableConnection.java
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -53,6 +53,7 @@
private final List<String> mConferenceableConnectionIds;
private final Bundle mExtras;
private String mParentCallId;
+ private @Call.Details.CallDirection int mCallDirection;
/** @hide */
public ParcelableConnection(
@@ -75,13 +76,15 @@
DisconnectCause disconnectCause,
List<String> conferenceableConnectionIds,
Bundle extras,
- String parentCallId) {
+ String parentCallId,
+ @Call.Details.CallDirection int callDirection) {
this(phoneAccount, state, capabilities, properties, supportedAudioRoutes, address,
addressPresentation, callerDisplayName, callerDisplayNamePresentation,
videoProvider, videoState, ringbackRequested, isVoipAudioMode, connectTimeMillis,
connectElapsedTimeMillis, statusHints, disconnectCause, conferenceableConnectionIds,
extras);
mParentCallId = parentCallId;
+ mCallDirection = callDirection;
}
/** @hide */
@@ -125,6 +128,7 @@
mConferenceableConnectionIds = conferenceableConnectionIds;
mExtras = extras;
mParentCallId = null;
+ mCallDirection = Call.Details.DIRECTION_UNKNOWN;
}
public PhoneAccountHandle getPhoneAccount() {
@@ -219,6 +223,10 @@
return mParentCallId;
}
+ public @Call.Details.CallDirection int getCallDirection() {
+ return mCallDirection;
+ }
+
@Override
public String toString() {
return new StringBuilder()
@@ -234,6 +242,8 @@
.append(mExtras)
.append(", parent:")
.append(mParentCallId)
+ .append(", callDirection:")
+ .append(mCallDirection)
.toString();
}
@@ -265,6 +275,7 @@
int supportedAudioRoutes = source.readInt();
String parentCallId = source.readString();
long connectElapsedTimeMillis = source.readLong();
+ int callDirection = source.readInt();
return new ParcelableConnection(
phoneAccount,
@@ -286,7 +297,8 @@
disconnectCause,
conferenceableConnectionIds,
extras,
- parentCallId);
+ parentCallId,
+ callDirection);
}
@Override
@@ -325,5 +337,6 @@
destination.writeInt(mSupportedAudioRoutes);
destination.writeString(mParentCallId);
destination.writeLong(mConnectElapsedTimeMillis);
+ destination.writeInt(mCallDirection);
}
}