Merge "Add support for DTMF and RTP header extension communications." am: 49612b38d8 am: 884d18b7d1 am: b18d3a92c8

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1487449

Change-Id: Id3ccaaa4f6d55d24620711b90e014c4058bba7d9
This commit is contained in:
Tyler Gunn
2020-11-05 20:06:57 +00:00
committed by Automerger Merge Worker
13 changed files with 633 additions and 1 deletions

View File

@@ -11786,6 +11786,7 @@ package android.telephony.ims {
ctor public ImsCallProfile(int, int);
ctor public ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile);
method public int describeContents();
method @NonNull public java.util.Set<android.telephony.ims.RtpHeaderExtensionType> getAcceptedRtpHeaderExtensionTypes();
method public String getCallExtra(String);
method public String getCallExtra(String, String);
method public boolean getCallExtraBoolean(String);
@@ -11800,6 +11801,7 @@ package android.telephony.ims {
method public int getEmergencyServiceCategories();
method @NonNull public java.util.List<java.lang.String> getEmergencyUrns();
method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
method @NonNull public java.util.Set<android.telephony.ims.RtpHeaderExtensionType> getOfferedRtpHeaderExtensionTypes();
method @NonNull public android.os.Bundle getProprietaryCallExtras();
method public int getRestrictCause();
method public int getServiceType();
@@ -11810,6 +11812,7 @@ package android.telephony.ims {
method public boolean isVideoCall();
method public boolean isVideoPaused();
method public static int presentationToOir(int);
method public void setAcceptedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
method public void setCallExtra(String, String);
method public void setCallExtraBoolean(String, boolean);
method public void setCallExtraInt(String, int);
@@ -11820,6 +11823,7 @@ package android.telephony.ims {
method public void setEmergencyServiceCategories(int);
method public void setEmergencyUrns(@NonNull java.util.List<java.lang.String>);
method public void setHasKnownUserIntentEmergency(boolean);
method public void setOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
method public void updateCallType(android.telephony.ims.ImsCallProfile);
method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
@@ -11879,6 +11883,7 @@ package android.telephony.ims {
method public void callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
method public void callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
method public void callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState);
method public void callSessionDtmfReceived(char);
method @Deprecated public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
method @Deprecated public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
method public void callSessionHeld(android.telephony.ims.ImsCallProfile);
@@ -11899,6 +11904,7 @@ package android.telephony.ims {
method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo);
method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile);
method public void callSessionResumed(android.telephony.ims.ImsCallProfile);
method public void callSessionRtpHeaderExtensionsReceived(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtension>);
method public void callSessionRttAudioIndicatorChanged(@NonNull android.telephony.ims.ImsStreamMediaProfile);
method public void callSessionRttMessageReceived(String);
method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile);
@@ -12255,6 +12261,24 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
}
public final class RtpHeaderExtension implements android.os.Parcelable {
ctor public RtpHeaderExtension(@IntRange(from=1, to=14) int, @NonNull byte[]);
method public int describeContents();
method @NonNull public byte[] getExtensionData();
method @IntRange(from=1, to=14) public int getLocalIdentifier();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RtpHeaderExtension> CREATOR;
}
public final class RtpHeaderExtensionType implements android.os.Parcelable {
ctor public RtpHeaderExtensionType(@IntRange(from=1, to=14) int, @NonNull android.net.Uri);
method public int describeContents();
method @IntRange(from=1, to=14) public int getLocalIdentifier();
method @NonNull public android.net.Uri getUri();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RtpHeaderExtensionType> CREATOR;
}
public class SipDelegateManager {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException;
}
@@ -12371,6 +12395,7 @@ package android.telephony.ims.stub {
method public void removeParticipants(String[]);
method public void resume(android.telephony.ims.ImsStreamMediaProfile);
method public void sendDtmf(char, android.os.Message);
method public void sendRtpHeaderExtensions(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtension>);
method public void sendRttMessage(String);
method public void sendRttModifyRequest(android.telephony.ims.ImsCallProfile);
method public void sendRttModifyResponse(boolean);

View File

@@ -10668,6 +10668,7 @@ package android.telephony.ims {
ctor public ImsCallProfile(int, int);
ctor public ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile);
method public int describeContents();
method @NonNull public java.util.Set<android.telephony.ims.RtpHeaderExtensionType> getAcceptedRtpHeaderExtensionTypes();
method public String getCallExtra(String);
method public String getCallExtra(String, String);
method public boolean getCallExtraBoolean(String);
@@ -10682,6 +10683,7 @@ package android.telephony.ims {
method public int getEmergencyServiceCategories();
method @NonNull public java.util.List<java.lang.String> getEmergencyUrns();
method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
method @NonNull public java.util.Set<android.telephony.ims.RtpHeaderExtensionType> getOfferedRtpHeaderExtensionTypes();
method @NonNull public android.os.Bundle getProprietaryCallExtras();
method public int getRestrictCause();
method public int getServiceType();
@@ -10692,6 +10694,7 @@ package android.telephony.ims {
method public boolean isVideoCall();
method public boolean isVideoPaused();
method public static int presentationToOir(int);
method public void setAcceptedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
method public void setCallExtra(String, String);
method public void setCallExtraBoolean(String, boolean);
method public void setCallExtraInt(String, int);
@@ -10702,6 +10705,7 @@ package android.telephony.ims {
method public void setEmergencyServiceCategories(int);
method public void setEmergencyUrns(@NonNull java.util.List<java.lang.String>);
method public void setHasKnownUserIntentEmergency(boolean);
method public void setOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
method public void updateCallType(android.telephony.ims.ImsCallProfile);
method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
@@ -10761,6 +10765,7 @@ package android.telephony.ims {
method public void callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
method public void callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
method public void callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState);
method public void callSessionDtmfReceived(char);
method @Deprecated public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
method @Deprecated public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
method public void callSessionHeld(android.telephony.ims.ImsCallProfile);
@@ -10781,6 +10786,7 @@ package android.telephony.ims {
method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo);
method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile);
method public void callSessionResumed(android.telephony.ims.ImsCallProfile);
method public void callSessionRtpHeaderExtensionsReceived(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtension>);
method public void callSessionRttAudioIndicatorChanged(@NonNull android.telephony.ims.ImsStreamMediaProfile);
method public void callSessionRttMessageReceived(String);
method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile);
@@ -11137,6 +11143,24 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
}
public final class RtpHeaderExtension implements android.os.Parcelable {
ctor public RtpHeaderExtension(@IntRange(from=1, to=14) int, @NonNull byte[]);
method public int describeContents();
method @NonNull public byte[] getExtensionData();
method @IntRange(from=1, to=14) public int getLocalIdentifier();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RtpHeaderExtension> CREATOR;
}
public final class RtpHeaderExtensionType implements android.os.Parcelable {
ctor public RtpHeaderExtensionType(@IntRange(from=1, to=14) int, @NonNull android.net.Uri);
method public int describeContents();
method @IntRange(from=1, to=14) public int getLocalIdentifier();
method @NonNull public android.net.Uri getUri();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RtpHeaderExtensionType> CREATOR;
}
public class SipDelegateManager {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException;
}
@@ -11253,6 +11277,7 @@ package android.telephony.ims.stub {
method public void removeParticipants(String[]);
method public void resume(android.telephony.ims.ImsStreamMediaProfile);
method public void sendDtmf(char, android.os.Message);
method public void sendRtpHeaderExtensions(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtension>);
method public void sendRttMessage(String);
method public void sendRttModifyRequest(android.telephony.ims.ImsCallProfile);
method public void sendRttModifyResponse(boolean);

View File

@@ -29,6 +29,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;
@@ -38,7 +40,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
@@ -445,6 +450,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>
@@ -683,6 +692,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) {
@@ -697,9 +708,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);
@@ -1086,4 +1104,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);
}
}

View File

@@ -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.
@@ -467,12 +472,32 @@ public class ImsCallSession {
// no-op
}
/**
* 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;
@@ -1118,6 +1143,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},
@@ -1476,6 +1526,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
*/
@@ -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));
}
}
}
/**

View File

@@ -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.
@@ -682,6 +686,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

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -30,11 +30,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 {
public void sendRttMessage(String rttMessage) {
}
/**
* 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

View File

@@ -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;

View File

@@ -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);
}