diff options
author | Tyler Gunn <tgunn@google.com> | 2020-09-24 15:56:08 -0700 |
---|---|---|
committer | Tyler Gunn <tgunn@google.com> | 2020-11-05 00:19:52 +0000 |
commit | 08ddbc2b6dd4e7432e25e9d4c4ba523beafefdb3 (patch) | |
tree | 042f8cf98abb0a0882ebdc187e5549eccf9ea2ea /telephony | |
parent | 730f35321d4e0539f640a9f54336c3e50841acf6 (diff) |
Add support for DTMF and RTP header extension communications.
Add support for:
- reporting of incoming DTMF tones from IMS stack.
- incoming/outgoing RTP header extension data.
Test: Added unit tests where possible.
Test: Added test intents to inject test data into framework for platform
testing.
Bug: 163085177
Change-Id: If34faeba0461c677a1381c82ead4a79c607bcf13
Diffstat (limited to 'telephony')
11 files changed, 583 insertions, 1 deletions
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index 47a0ab61f970..dcb8736de69c 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -28,6 +28,8 @@ import android.telecom.VideoProfile; import android.telephony.emergency.EmergencyNumber; import android.telephony.emergency.EmergencyNumber.EmergencyCallRouting; import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories; +import android.telephony.ims.feature.MmTelFeature; +import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -37,7 +39,10 @@ import com.android.internal.telephony.util.TelephonyUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * A Parcelable object to handle the IMS call profile, which provides the service, call type, and @@ -444,6 +449,10 @@ public final class ImsCallProfile implements Parcelable { /** Indicates if we have known the intent of the user for the call is emergency */ private boolean mHasKnownUserIntentEmergency = false; + private Set<RtpHeaderExtensionType> mOfferedRtpHeaderExtensionTypes = new ArraySet<>(); + + private Set<RtpHeaderExtensionType> mAcceptedRtpHeaderExtensionTypes = new ArraySet<>(); + /** * Extras associated with this {@link ImsCallProfile}. * <p> @@ -682,6 +691,8 @@ public final class ImsCallProfile implements Parcelable { out.writeBoolean(mHasKnownUserIntentEmergency); out.writeInt(mRestrictCause); out.writeInt(mCallerNumberVerificationStatus); + out.writeArray(mOfferedRtpHeaderExtensionTypes.toArray()); + out.writeArray(mAcceptedRtpHeaderExtensionTypes.toArray()); } private void readFromParcel(Parcel in) { @@ -696,9 +707,16 @@ public final class ImsCallProfile implements Parcelable { mHasKnownUserIntentEmergency = in.readBoolean(); mRestrictCause = in.readInt(); mCallerNumberVerificationStatus = in.readInt(); + Object[] offered = in.readArray(RtpHeaderExtensionType.class.getClassLoader()); + mOfferedRtpHeaderExtensionTypes = Arrays.stream(offered) + .map(o -> (RtpHeaderExtensionType) o).collect(Collectors.toSet()); + Object[] accepted = in.readArray(RtpHeaderExtensionType.class.getClassLoader()); + mAcceptedRtpHeaderExtensionTypes = Arrays.stream(accepted) + .map(o -> (RtpHeaderExtensionType) o).collect(Collectors.toSet()); } - public static final @android.annotation.NonNull Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() { + public static final @android.annotation.NonNull Creator<ImsCallProfile> CREATOR = + new Creator<ImsCallProfile>() { @Override public ImsCallProfile createFromParcel(Parcel in) { return new ImsCallProfile(in); @@ -1085,4 +1103,66 @@ public final class ImsCallProfile implements Parcelable { public boolean hasKnownUserIntentEmergency() { return mHasKnownUserIntentEmergency; } + + /** + * For an incoming or outgoing call, indicates the {@link RtpHeaderExtensionType}s which the + * caller is offering to make available. + * <p> + * For outgoing calls, an {@link ImsService} will reserve + * {@link RtpHeaderExtensionType#getLocalIdentifier()} identifiers the telephony stack has + * proposed to use and not use these same local identifiers. The offered header extension + * types for an outgoing call can be found in the + * {@link ImsCallProfile#getOfferedRtpHeaderExtensionTypes()} and will be available to the + * {@link ImsService} in {@link MmTelFeature#createCallSession(ImsCallProfile)}. + * The {@link ImsService} sets the accepted {@link #setAcceptedRtpHeaderExtensionTypes(Set)} + * when the SDP offer/accept process has completed. + * <p> + * According to RFC8285, RTP header extensions available to a call are determined using the + * offer/accept phase of the SDP protocol (see RFC4566). + * + * @return the {@link RtpHeaderExtensionType}s which were offered by other end of the call. + */ + public @NonNull Set<RtpHeaderExtensionType> getOfferedRtpHeaderExtensionTypes() { + return mOfferedRtpHeaderExtensionTypes; + } + + /** + * Sets the offered {@link RtpHeaderExtensionType}s for this call. + * <p> + * According to RFC8285, RTP header extensions available to a call are determined using the + * offer/accept phase of the SDP protocol (see RFC4566). + * + * @param rtpHeaderExtensions the {@link RtpHeaderExtensionType}s which are offered. + */ + public void setOfferedRtpHeaderExtensionTypes(@NonNull Set<RtpHeaderExtensionType> + rtpHeaderExtensions) { + mOfferedRtpHeaderExtensionTypes.clear(); + mOfferedRtpHeaderExtensionTypes.addAll(rtpHeaderExtensions); + } + + /** + * Gets the {@link RtpHeaderExtensionType}s which have been accepted by both ends of the call. + * <p> + * According to RFC8285, RTP header extensions available to a call are determined using the + * offer/accept phase of the SDP protocol (see RFC4566). + * + * @return the {@link RtpHeaderExtensionType}s which were accepted by the other end of the call. + */ + public @NonNull Set<RtpHeaderExtensionType> getAcceptedRtpHeaderExtensionTypes() { + return mAcceptedRtpHeaderExtensionTypes; + } + + /** + * Sets the accepted {@link RtpHeaderExtensionType}s for this call. + * <p> + * According to RFC8285, RTP header extensions available to a call are determined using the + * offer/accept phase of the SDP protocol (see RFC4566). + * + * @param rtpHeaderExtensions + */ + public void setAcceptedRtpHeaderExtensionTypes(@NonNull Set<RtpHeaderExtensionType> + rtpHeaderExtensions) { + mAcceptedRtpHeaderExtensionTypes.clear(); + mAcceptedRtpHeaderExtensionTypes.addAll(rtpHeaderExtensions); + } } diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java index 8857b9b36d0c..a3efb799029a 100755 --- a/telephony/java/android/telephony/ims/ImsCallSession.java +++ b/telephony/java/android/telephony/ims/ImsCallSession.java @@ -22,11 +22,16 @@ import android.os.Message; import android.os.RemoteException; import android.telephony.CallQuality; import android.telephony.ims.aidl.IImsCallSessionListener; +import android.util.ArraySet; import android.util.Log; import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsVideoCallProvider; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + /** * Provides the call initiation/termination, and media exchange between two IMS endpoints. * It directly communicates with IMS service which implements the IMS protocol behavior. @@ -468,11 +473,31 @@ public class ImsCallSession { } /** + * Informs the framework of a DTMF digit which was received from the network. + * <p> + * According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833 sec 3.10</a>, + * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to + * 12 ~ 15. + * @param digit the DTMF digit + */ + public void callSessionDtmfReceived(char digit) { + // no-op + } + + /** * Called when the IMS service reports a change to the call quality. */ public void callQualityChanged(CallQuality callQuality) { // no-op } + + /** + * Called when the IMS service reports incoming RTP header extension data. + */ + public void callSessionRtpHeaderExtensionsReceived( + @NonNull Set<RtpHeaderExtension> extensions) { + // no-op + } } private final IImsCallSession miSession; @@ -1119,6 +1144,31 @@ public class ImsCallSession { } /** + * Requests that {@code rtpHeaderExtensions} are sent as a header extension with the next + * RTP packet sent by the IMS stack. + * <p> + * The {@link RtpHeaderExtensionType}s negotiated during SDP (Session Description Protocol) + * signalling determine the {@link RtpHeaderExtension}s which can be sent using this method. + * See RFC8285 for more information. + * <p> + * By specification, the RTP header extension is an unacknowledged transmission and there is no + * guarantee that the header extension will be delivered by the network to the other end of the + * call. + * @param rtpHeaderExtensions The header extensions to be included in the next RTP header. + */ + public void sendRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) { + if (mClosed) { + return; + } + + try { + miSession.sendRtpHeaderExtensions( + new ArrayList<RtpHeaderExtension>(rtpHeaderExtensions)); + } catch (RemoteException e) { + } + } + + /** * A listener type for receiving notification on IMS call session events. * When an event is generated for an {@link IImsCallSession}, * the application is notified by having one of the methods called on @@ -1477,6 +1527,17 @@ public class ImsCallSession { } /** + * DTMF digit received. + * @param dtmf The DTMF digit. + */ + @Override + public void callSessionDtmfReceived(char dtmf) { + if (mListener != null) { + mListener.callSessionDtmfReceived(dtmf); + } + } + + /** * Call quality updated */ @Override @@ -1485,6 +1546,19 @@ public class ImsCallSession { mListener.callQualityChanged(callQuality); } } + + /** + * RTP header extension data received. + * @param extensions The header extension data. + */ + @Override + public void callSessionRtpHeaderExtensionsReceived( + @NonNull List<RtpHeaderExtension> extensions) { + if (mListener != null) { + mListener.callSessionRtpHeaderExtensionsReceived( + new ArraySet<RtpHeaderExtension>(extensions)); + } + } } /** diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java index 2fdd195bbb26..86bb5d9f0b09 100644 --- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java +++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java @@ -28,6 +28,10 @@ import android.telephony.ims.stub.ImsCallSessionImplBase; import com.android.ims.internal.IImsCallSession; +import java.util.ArrayList; +import java.util.Objects; +import java.util.Set; + /** * Listener interface for notifying the Framework's {@link ImsCallSession} for updates to an ongoing * IMS call. @@ -683,6 +687,59 @@ public class ImsCallSessionListener { } /** + * The {@link ImsService} calls this method to inform the framework of a DTMF digit which was + * received from the network. + * <p> + * According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833 sec 3.10</a>, + * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15. + * <p> + * <em>Note:</em> Alpha DTMF digits are converted from lower-case to upper-case. + * + * @param dtmf The DTMF digit received, '0'-'9', *, #, A, B, C, or D. + * @throws IllegalArgumentException If an invalid DTMF character is provided. + */ + public void callSessionDtmfReceived(char dtmf) { + if (!(dtmf >= '0' && dtmf <= '9' + || dtmf >= 'A' && dtmf <= 'D' + || dtmf >= 'a' && dtmf <= 'd' + || dtmf == '*' + || dtmf == '#')) { + throw new IllegalArgumentException("DTMF digit must be 0-9, *, #, A, B, C, D"); + } + try { + mListener.callSessionDtmfReceived(Character.toUpperCase(dtmf)); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * The {@link ImsService} calls this method to inform the framework of RTP header extension data + * which was received from the network. + * <p> + * The set of {@link RtpHeaderExtension} data are identified by local identifiers which were + * negotiated during SDP signalling. See RFC8285, + * {@link ImsCallProfile#getAcceptedRtpHeaderExtensionTypes()} and + * {@link RtpHeaderExtensionType} for more information. + * <p> + * By specification, the RTP header extension is an unacknowledged transmission and there is no + * guarantee that the header extension will be delivered by the network to the other end of the + * call. + * + * @param extensions The RTP header extension data received. + */ + public void callSessionRtpHeaderExtensionsReceived( + @NonNull Set<RtpHeaderExtension> extensions) { + Objects.requireNonNull(extensions, "extensions are required."); + try { + mListener.callSessionRtpHeaderExtensionsReceived( + new ArrayList<RtpHeaderExtension>(extensions)); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** * Notifies the result of transfer request. * @hide */ diff --git a/telephony/java/android/telephony/ims/RtpHeaderExtension.aidl b/telephony/java/android/telephony/ims/RtpHeaderExtension.aidl new file mode 100644 index 000000000000..cbf79d352ad8 --- /dev/null +++ b/telephony/java/android/telephony/ims/RtpHeaderExtension.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 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 android.telephony.ims; + +parcelable RtpHeaderExtension;
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/RtpHeaderExtension.java b/telephony/java/android/telephony/ims/RtpHeaderExtension.java new file mode 100644 index 000000000000..f9ab7016facb --- /dev/null +++ b/telephony/java/android/telephony/ims/RtpHeaderExtension.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2020 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 android.telephony.ims; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; +import java.util.Objects; + +/** + * A representation of an RTP header extension. + * <p> + * Per RFC8285, an RTP header extension consists of both a local identifier in the range 1-14, an + * 8-bit length indicator and a number of extension data bytes equivalent to the stated length. + * @hide + */ +@SystemApi +public final class RtpHeaderExtension implements Parcelable { + private int mLocalIdentifier; + private byte[] mExtensionData; + + /** + * Creates a new {@link RtpHeaderExtension}. + * @param localIdentifier The local identifier for this RTP header extension. + * @param extensionData The data for this RTP header extension. + * @throws IllegalArgumentException if {@code extensionId} is not in the range 1-14. + * @throws NullPointerException if {@code extensionData} is null. + */ + public RtpHeaderExtension(@IntRange(from = 1, to = 14) int localIdentifier, + @NonNull byte[] extensionData) { + if (localIdentifier < 1 || localIdentifier > 13) { + throw new IllegalArgumentException("localIdentifier must be in range 1-14"); + } + if (extensionData == null) { + throw new NullPointerException("extensionDa is required."); + } + mLocalIdentifier = localIdentifier; + mExtensionData = extensionData; + } + + /** + * Creates a new instance of {@link RtpHeaderExtension} from a parcel. + * @param in The parceled data to read. + */ + private RtpHeaderExtension(@NonNull Parcel in) { + mLocalIdentifier = in.readInt(); + mExtensionData = in.createByteArray(); + } + + /** + * The local identifier for the RTP header extension. + * <p> + * Per RFC8285, the extension ID is a value in the range 1-14 (0 is reserved for padding and + * 15 is reserved for the one-byte header form. + * <p> + * Within the current call, this extension ID will match one of the + * {@link RtpHeaderExtensionType#getLocalIdentifier()}s. + * + * @return The local identifier for this RTP header extension. + */ + @IntRange(from = 1, to = 14) + public int getLocalIdentifier() { + return mLocalIdentifier; + } + + /** + * The data payload for the RTP header extension. + * <p> + * Per RFC8285 Sec 4.3, an RTP header extension includes an 8-bit length field which indicate + * how many bytes of data are present in the RTP header extension. The extension includes this + * many bytes of actual data. + * <p> + * We represent this as a byte array who's length is equivalent to the 8-bit length field. + * @return RTP header extension data payload. The payload may be up to 255 bytes in length. + */ + public @NonNull byte[] getExtensionData() { + return mExtensionData; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mLocalIdentifier); + dest.writeByteArray(mExtensionData); + } + + public static final @NonNull Creator<RtpHeaderExtension> CREATOR = + new Creator<RtpHeaderExtension>() { + @Override + public RtpHeaderExtension createFromParcel(@NonNull Parcel in) { + return new RtpHeaderExtension(in); + } + + @Override + public RtpHeaderExtension[] newArray(int size) { + return new RtpHeaderExtension[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RtpHeaderExtension that = (RtpHeaderExtension) o; + return mLocalIdentifier == that.mLocalIdentifier + && Arrays.equals(mExtensionData, that.mExtensionData); + } + + @Override + public int hashCode() { + int result = Objects.hash(mLocalIdentifier); + result = 31 * result + Arrays.hashCode(mExtensionData); + return result; + } +} diff --git a/telephony/java/android/telephony/ims/RtpHeaderExtensionType.aidl b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.aidl new file mode 100644 index 000000000000..3e62ffff5127 --- /dev/null +++ b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 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 android.telephony.ims; + +parcelable RtpHeaderExtensionType;
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java new file mode 100644 index 000000000000..e1d39c217395 --- /dev/null +++ b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 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 android.telephony.ims; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Defines a mapping between a local identifier and a {@link Uri} which identifies an RTP header + * extension. + * <p> + * According to RFC8285, SDP (Session Description Protocol) signalling for a call provides a means + * for the supported RTP header extensions for a call to be negotiated at call initiation time. + * The types of RTP header extensions potentially usable in a session are identified by a local + * identifier ({@link #getLocalIdentifier()}) when RTP header extensions are present on RTP packets. + * A {@link Uri} ({@link #getUri()}) provides a unique identifier for the RTP header extension + * format which parties in a call can use to identify supported RTP header extensions. + * @hide + */ +@SystemApi +public final class RtpHeaderExtensionType implements Parcelable { + private int mLocalIdentifier; + private Uri mUri; + + /** + * Create a new RTP header extension type. + * @param localIdentifier the local identifier. + * @param uri the {@link Uri} identifying the RTP header extension type. + * @throws IllegalArgumentException if {@code localIdentifier} is out of the expected range. + * @throws NullPointerException if {@code uri} is null. + */ + public RtpHeaderExtensionType(@IntRange(from = 1, to = 14) int localIdentifier, + @NonNull Uri uri) { + if (localIdentifier < 1 || localIdentifier > 13) { + throw new IllegalArgumentException("localIdentifier must be in range 1-14"); + } + if (uri == null) { + throw new NullPointerException("uri is required."); + } + mLocalIdentifier = localIdentifier; + mUri = uri; + } + + private RtpHeaderExtensionType(Parcel in) { + mLocalIdentifier = in.readInt(); + mUri = in.readParcelable(Uri.class.getClassLoader()); + } + + public static final @NonNull Creator<RtpHeaderExtensionType> CREATOR = + new Creator<RtpHeaderExtensionType>() { + @Override + public RtpHeaderExtensionType createFromParcel(@NonNull Parcel in) { + return new RtpHeaderExtensionType(in); + } + + @Override + public @NonNull RtpHeaderExtensionType[] newArray(int size) { + return new RtpHeaderExtensionType[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mLocalIdentifier); + dest.writeParcelable(mUri, flags); + } + + /** + * The local identifier for this RTP header extension type. + * <p> + * {@link RtpHeaderExtension}s which indicate a {@link RtpHeaderExtension#getLocalIdentifier()} + * matching this local identifier will have the format specified by {@link #getUri()}. + * <p> + * Per RFC8285, the extension ID is a value in the range 1-14 (0 is reserved for padding and + * 15 is reserved for the one-byte header form. + * + * @return The local identifier associated with this {@link #getUri()}. + */ + public @IntRange(from = 1, to = 14) int getLocalIdentifier() { + return mLocalIdentifier; + } + + /** + * A {@link Uri} which identifies the format of the RTP extension header. + * <p> + * According to RFC8285 section 5, URIs MUST be absolute and SHOULD contain a month/date pair + * in the form mmyyyy to indicate versioning of the extension. Extension headers defined in an + * RFC are typically defined using URNs starting with {@code urn:ietf:params:rtp-hdrext:}. + * For example, RFC6464 defines {@code urn:ietf:params:rtp-hdrext:ssrc-audio-level} which is an + * RTP header extension for communicating client to mixer audio level indications. + * + * @return A unique {@link Uri} identifying the format of the RTP extension header. + */ + public @NonNull Uri getUri() { + return mUri; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RtpHeaderExtensionType that = (RtpHeaderExtensionType) o; + return mLocalIdentifier == that.mLocalIdentifier + && mUri.equals(that.mUri); + } + + @Override + public int hashCode() { + return Objects.hash(mLocalIdentifier, mUri); + } +} diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl index 36d2067ad016..ed895b77a164 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl @@ -21,9 +21,12 @@ import android.telephony.ims.ImsStreamMediaProfile; import android.telephony.ims.ImsCallProfile; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ImsConferenceState; +import android.telephony.ims.RtpHeaderExtension; import com.android.ims.internal.IImsCallSession; import android.telephony.ims.ImsSuppServiceNotification; +import java.util.List; + /** * A listener type for receiving notification on IMS call session events. * When an event is generated for an {@link IImsCallSession}, the application is notified @@ -153,9 +156,17 @@ oneway interface IImsCallSessionListener { void callSessionTransferred(); void callSessionTransferFailed(in ImsReasonInfo reasonInfo); + void callSessionDtmfReceived(char dtmf); + /** * Notifies of a change to the call quality. * @param callQuality then updated call quality */ void callQualityChanged(in CallQuality callQuality); + + /** + * Notifies of incoming RTP header extensions from the network. + * @param extensions the RTP header extensions received. + */ + void callSessionRtpHeaderExtensionsReceived(in List<RtpHeaderExtension> extensions); } diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java index 06aa6428b1b2..06f03976e52b 100755 --- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java +++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java @@ -29,11 +29,14 @@ import android.telephony.ims.ImsConferenceState; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ImsStreamMediaProfile; import android.telephony.ims.ImsSuppServiceNotification; +import android.telephony.ims.RtpHeaderExtension; import android.telephony.ims.aidl.IImsCallSessionListener; import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsVideoCallProvider; +import java.util.List; + /** * Compat implementation of ImsCallSessionImplBase for older implementations. * @@ -404,6 +407,15 @@ public class ImsCallSessionImplBase extends IImsCallSession.Stub { } /** + * Device sends RTP header extensions. + * @param headerExtensions The header extensions to send. + */ + @Override + public void sendRtpHeaderExtensions(@NonNull List<RtpHeaderExtension> headerExtensions) { + // no-op; not supported in compat layer. + } + + /** * There are two different ImsCallSessionListeners that need to reconciled here, we need to * convert the "old" version of the com.android.ims.internal.IImsCallSessionListener to the * "new" version of the Listener android.telephony.ims.ImsCallSessionListener when calling diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java index 1cebdd582b58..a3a6cb864fa5 100644 --- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java @@ -26,10 +26,17 @@ import android.telephony.ims.ImsCallSessionListener; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ImsStreamMediaProfile; import android.telephony.ims.ImsVideoCallProvider; +import android.telephony.ims.RtpHeaderExtension; +import android.telephony.ims.RtpHeaderExtensionType; import android.telephony.ims.aidl.IImsCallSessionListener; +import android.util.ArraySet; import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsVideoCallProvider; + +import java.util.List; +import java.util.Set; + /** * Base implementation of IImsCallSession, which implements stub versions of the methods available. * @@ -277,6 +284,12 @@ public class ImsCallSessionImplBase implements AutoCloseable { public void sendRttMessage(String rttMessage) { ImsCallSessionImplBase.this.sendRttMessage(rttMessage); } + + @Override + public void sendRtpHeaderExtensions(@NonNull List<RtpHeaderExtension> extensions) { + ImsCallSessionImplBase.this.sendRtpHeaderExtensions( + new ArraySet<RtpHeaderExtension>(extensions)); + } }; /** @@ -636,6 +649,22 @@ public class ImsCallSessionImplBase implements AutoCloseable { public void sendRttMessage(String rttMessage) { } + /** + * Device requests that {@code rtpHeaderExtensions} are sent as a header extension with the next + * RTP packet sent by the IMS stack. + * <p> + * The {@link RtpHeaderExtensionType}s negotiated during SDP (Session Description Protocol) + * signalling determine the {@link RtpHeaderExtension}s which can be sent using this method. + * See RFC8285 for more information. + * <p> + * By specification, the RTP header extension is an unacknowledged transmission and there is no + * guarantee that the header extension will be delivered by the network to the other end of the + * call. + * @param rtpHeaderExtensions The RTP header extensions to be included in the next RTP header. + */ + public void sendRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) { + } + /** @hide */ public IImsCallSession getServiceImpl() { return mServiceImpl; diff --git a/telephony/java/com/android/ims/internal/IImsCallSession.aidl b/telephony/java/com/android/ims/internal/IImsCallSession.aidl index ab14e82b7087..e3a8aeed7ad5 100644 --- a/telephony/java/com/android/ims/internal/IImsCallSession.aidl +++ b/telephony/java/com/android/ims/internal/IImsCallSession.aidl @@ -20,6 +20,8 @@ import android.os.Message; import android.telephony.ims.aidl.IImsCallSessionListener; import android.telephony.ims.ImsCallProfile; import android.telephony.ims.ImsStreamMediaProfile; +import android.telephony.ims.RtpHeaderExtension; + import com.android.ims.internal.IImsVideoCallProvider; /** @@ -297,4 +299,10 @@ interface IImsCallSession { * @param rttMessage RTT message to be sent */ void sendRttMessage(in String rttMessage); + + /* + * Device sends RTP header extension(s). + * @param extensions the header extensions to be sent + */ + void sendRtpHeaderExtensions(in List<RtpHeaderExtension> extensions); } |