Merge "Media quality CB: API interface"
This commit is contained in:
@@ -13795,6 +13795,7 @@ package android.telephony {
|
||||
field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c
|
||||
field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36; // 0x24
|
||||
field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_LINK_CAPACITY_ESTIMATE_CHANGED = 37; // 0x25
|
||||
field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_MEDIA_QUALITY_STATUS_CHANGED = 39; // 0x27
|
||||
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3
|
||||
field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf
|
||||
field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d
|
||||
@@ -13830,6 +13831,10 @@ package android.telephony {
|
||||
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onLinkCapacityEstimateChanged(@NonNull java.util.List<android.telephony.LinkCapacityEstimate>);
|
||||
}
|
||||
|
||||
public static interface TelephonyCallback.MediaQualityStatusChangedListener {
|
||||
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onMediaQualityStatusChanged(@NonNull android.telephony.ims.MediaQualityStatus);
|
||||
}
|
||||
|
||||
public static interface TelephonyCallback.OutgoingEmergencyCallListener {
|
||||
method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
|
||||
}
|
||||
@@ -15372,6 +15377,37 @@ package android.telephony.ims {
|
||||
method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
|
||||
}
|
||||
|
||||
public final class MediaQualityStatus implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method @NonNull public String getCallSessionId();
|
||||
method public int getMediaSessionType();
|
||||
method public long getRtpInactivityMillis();
|
||||
method public int getRtpJitterMillis();
|
||||
method @IntRange(from=0, to=100) public int getRtpPacketLossRate();
|
||||
method public int getTransportType();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.MediaQualityStatus> CREATOR;
|
||||
field public static final int MEDIA_SESSION_TYPE_AUDIO = 1; // 0x1
|
||||
field public static final int MEDIA_SESSION_TYPE_VIDEO = 2; // 0x2
|
||||
}
|
||||
|
||||
public static final class MediaQualityStatus.Builder {
|
||||
ctor public MediaQualityStatus.Builder(@NonNull String, int, int);
|
||||
method @NonNull public android.telephony.ims.MediaQualityStatus build();
|
||||
method @NonNull public android.telephony.ims.MediaQualityStatus.Builder setRtpInactivityMillis(long);
|
||||
method @NonNull public android.telephony.ims.MediaQualityStatus.Builder setRtpJitterMillis(int);
|
||||
method @NonNull public android.telephony.ims.MediaQualityStatus.Builder setRtpPacketLossRate(@IntRange(from=0, to=100) int);
|
||||
}
|
||||
|
||||
public final class MediaThreshold implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method @NonNull public long[] getThresholdsRtpInactivityTimeMillis();
|
||||
method @NonNull public int[] getThresholdsRtpJitterMillis();
|
||||
method @NonNull public int[] getThresholdsRtpPacketLossRate();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.MediaThreshold> CREATOR;
|
||||
}
|
||||
|
||||
public class ProvisioningManager {
|
||||
method @NonNull public static android.telephony.ims.ProvisioningManager createForSubscriptionId(int);
|
||||
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public int getProvisioningIntValue(int);
|
||||
@@ -15823,6 +15859,7 @@ package android.telephony.ims.feature {
|
||||
ctor public MmTelFeature(@NonNull java.util.concurrent.Executor);
|
||||
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
|
||||
method public void changeOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
|
||||
method public void clearMediaThreshold(int);
|
||||
method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int);
|
||||
method @Nullable public android.telephony.ims.stub.ImsCallSessionImplBase createCallSession(@NonNull android.telephony.ims.ImsCallProfile);
|
||||
method @NonNull public android.telephony.ims.stub.ImsEcbmImplBase getEcbm();
|
||||
@@ -15832,6 +15869,7 @@ package android.telephony.ims.feature {
|
||||
method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
|
||||
method @Deprecated public final void notifyIncomingCall(@NonNull android.telephony.ims.stub.ImsCallSessionImplBase, @NonNull android.os.Bundle);
|
||||
method @Nullable public final android.telephony.ims.ImsCallSessionListener notifyIncomingCall(@NonNull android.telephony.ims.stub.ImsCallSessionImplBase, @NonNull String, @NonNull android.os.Bundle);
|
||||
method public final void notifyMediaQualityStatusChanged(@NonNull android.telephony.ims.MediaQualityStatus);
|
||||
method public final void notifyRejectedCall(@NonNull android.telephony.ims.ImsCallProfile, @NonNull android.telephony.ims.ImsReasonInfo);
|
||||
method public void notifySrvccCanceled();
|
||||
method public void notifySrvccCompleted();
|
||||
@@ -15842,7 +15880,9 @@ package android.telephony.ims.feature {
|
||||
method public void onFeatureRemoved();
|
||||
method public boolean queryCapabilityConfiguration(int, int);
|
||||
method @NonNull public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus();
|
||||
method @Nullable public android.telephony.ims.MediaQualityStatus queryMediaQualityStatus(int);
|
||||
method public final void setCallAudioHandler(int);
|
||||
method public void setMediaThreshold(int, @NonNull android.telephony.ims.MediaThreshold);
|
||||
method public void setTerminalBasedCallWaitingStatus(boolean);
|
||||
method public void setUiTtyMode(int, @Nullable android.os.Message);
|
||||
method public int shouldProcessCall(@NonNull String[]);
|
||||
|
||||
@@ -34,6 +34,7 @@ import android.telephony.Annotation.SrvccState;
|
||||
import android.telephony.TelephonyManager.DataEnabledReason;
|
||||
import android.telephony.emergency.EmergencyNumber;
|
||||
import android.telephony.ims.ImsReasonInfo;
|
||||
import android.telephony.ims.MediaQualityStatus;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.telephony.IPhoneStateListener;
|
||||
@@ -1664,6 +1665,10 @@ public class PhoneStateListener {
|
||||
List<LinkCapacityEstimate> linkCapacityEstimateList) {
|
||||
// default implementation empty
|
||||
}
|
||||
|
||||
public final void onMediaQualityStatusChanged(MediaQualityStatus mediaQualityStatus) {
|
||||
// not support. Can't override. Use TelephonyCallback.
|
||||
}
|
||||
}
|
||||
|
||||
private void log(String s) {
|
||||
|
||||
@@ -27,6 +27,8 @@ import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.telephony.emergency.EmergencyNumber;
|
||||
import android.telephony.ims.ImsReasonInfo;
|
||||
import android.telephony.ims.MediaQualityStatus;
|
||||
import android.telephony.ims.MediaThreshold;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -591,6 +593,19 @@ public class TelephonyCallback {
|
||||
*/
|
||||
public static final int EVENT_TRIGGER_NOTIFY_ANBR = 38;
|
||||
|
||||
/**
|
||||
* Event for changes to the media quality status
|
||||
*
|
||||
* <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
|
||||
*
|
||||
* @see MediaQualityStatusChangedListener#onMediaQualityStatusChanged
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
|
||||
public static final int EVENT_MEDIA_QUALITY_STATUS_CHANGED = 39;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -632,7 +647,8 @@ public class TelephonyCallback {
|
||||
EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED,
|
||||
EVENT_LEGACY_CALL_STATE_CHANGED,
|
||||
EVENT_LINK_CAPACITY_ESTIMATE_CHANGED,
|
||||
EVENT_TRIGGER_NOTIFY_ANBR
|
||||
EVENT_TRIGGER_NOTIFY_ANBR,
|
||||
EVENT_MEDIA_QUALITY_STATUS_CHANGED
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface TelephonyEvent {
|
||||
@@ -1516,6 +1532,30 @@ public class TelephonyCallback {
|
||||
@NonNull List<LinkCapacityEstimate> linkCapacityEstimateList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for media quality status changed listener.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public interface MediaQualityStatusChangedListener {
|
||||
/**
|
||||
* Callback invoked when the media quality status of IMS call changes. This call back
|
||||
* means current media quality status crosses at least one of threshold values in {@link
|
||||
* MediaThreshold}. Listener needs to get quality information & check whether it crossed
|
||||
* listener's threshold.
|
||||
*
|
||||
* <p/> Currently thresholds for this indication can be configurable by CARRIER_CONFIG
|
||||
* {@link CarrierConfigManager#KEY_VOICE_RTP_THRESHOLDS_PACKET_LOSS_RATE_INT}
|
||||
* {@link CarrierConfigManager#KEY_VOICE_RTP_THRESHOLDS_INACTIVITY_TIME_IN_MILLIS_INT}
|
||||
* {@link CarrierConfigManager#KEY_VOICE_RTP_THRESHOLDS_JITTER_INT}
|
||||
*
|
||||
* @param mediaQualityStatus The media quality status currently measured.
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
|
||||
void onMediaQualityStatusChanged(@NonNull MediaQualityStatus mediaQualityStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* The callback methods need to be called on the handler thread where
|
||||
* this object was created. If the binder did that for us it'd be nice.
|
||||
@@ -1873,5 +1913,16 @@ public class TelephonyCallback {
|
||||
() -> mExecutor.execute(() -> listener.onLinkCapacityEstimateChanged(
|
||||
linkCapacityEstimateList)));
|
||||
}
|
||||
|
||||
public void onMediaQualityStatusChanged(
|
||||
MediaQualityStatus mediaQualityStatus) {
|
||||
MediaQualityStatusChangedListener listener =
|
||||
(MediaQualityStatusChangedListener) mTelephonyCallbackWeakRef.get();
|
||||
if (listener == null) return;
|
||||
|
||||
Binder.withCleanCallingIdentity(
|
||||
() -> mExecutor.execute(() -> listener.onMediaQualityStatusChanged(
|
||||
mediaQualityStatus)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import android.telephony.TelephonyManager.CarrierPrivilegesCallback;
|
||||
import android.telephony.emergency.EmergencyNumber;
|
||||
import android.telephony.ims.ImsCallSession;
|
||||
import android.telephony.ims.ImsReasonInfo;
|
||||
import android.telephony.ims.MediaQualityStatus;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -544,6 +545,27 @@ public class TelephonyRegistryManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify change of media quality status {@link MediaQualityStatus} crosses media quality
|
||||
* threshold
|
||||
* <p/>
|
||||
* Currently thresholds for this indication can be configurable by CARRIER_CONFIG
|
||||
* {@link CarrierConfigManager#KEY_VOICE_RTP_THRESHOLDS_PACKET_LOSS_RATE_INT}
|
||||
* {@link CarrierConfigManager#KEY_VOICE_RTP_THRESHOLDS_INACTIVITY_TIME_IN_MILLIS_INT}
|
||||
* {@link CarrierConfigManager#KEY_VOICE_RTP_THRESHOLDS_JITTER_INT}
|
||||
*
|
||||
* @param status media quality status
|
||||
*/
|
||||
public void notifyMediaQualityStatusChanged(
|
||||
int slotIndex, int subId, @NonNull MediaQualityStatus status) {
|
||||
try {
|
||||
sRegistry.notifyMediaQualityStatusChanged(slotIndex, subId, status);
|
||||
} catch (RemoteException ex) {
|
||||
// system server crash
|
||||
throw ex.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify emergency number list changed on certain subscription.
|
||||
*
|
||||
@@ -1087,6 +1109,10 @@ public class TelephonyRegistryManager {
|
||||
eventList.add(TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED);
|
||||
}
|
||||
|
||||
if (telephonyCallback instanceof TelephonyCallback.MediaQualityStatusChangedListener) {
|
||||
eventList.add(TelephonyCallback.EVENT_MEDIA_QUALITY_STATUS_CHANGED);
|
||||
}
|
||||
|
||||
return eventList;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.telephony.ServiceState;
|
||||
import android.telephony.SignalStrength;
|
||||
import android.telephony.emergency.EmergencyNumber;
|
||||
import android.telephony.ims.ImsReasonInfo;
|
||||
import android.telephony.ims.MediaQualityStatus;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
@@ -76,4 +77,5 @@ oneway interface IPhoneStateListener {
|
||||
void onDataEnabledChanged(boolean enabled, int reason);
|
||||
void onAllowedNetworkTypesChanged(in int reason, in long allowedNetworkType);
|
||||
void onLinkCapacityEstimateChanged(in List<LinkCapacityEstimate> linkCapacityEstimateList);
|
||||
void onMediaQualityStatusChanged(in MediaQualityStatus mediaQualityStatus);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ import android.telephony.PreciseDataConnectionState;
|
||||
import android.telephony.ServiceState;
|
||||
import android.telephony.SignalStrength;
|
||||
import android.telephony.emergency.EmergencyNumber;
|
||||
import android.telephony.ims.MediaQualityStatus;
|
||||
|
||||
import com.android.internal.telephony.ICarrierConfigChangeListener;
|
||||
import com.android.internal.telephony.ICarrierPrivilegesCallback;
|
||||
import com.android.internal.telephony.IPhoneStateListener;
|
||||
@@ -92,6 +94,7 @@ interface ITelephonyRegistry {
|
||||
in EmergencyNumber emergencyNumber);
|
||||
void notifyCallQualityChanged(in CallQuality callQuality, int phoneId, int subId,
|
||||
int callNetworkType);
|
||||
void notifyMediaQualityStatusChanged(int phoneId, int subId, in MediaQualityStatus status);
|
||||
void notifyImsDisconnectCause(int subId, in ImsReasonInfo imsReasonInfo);
|
||||
void notifyRegistrationFailed(int slotIndex, int subId, in CellIdentity cellIdentity,
|
||||
String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
|
||||
|
||||
@@ -84,11 +84,13 @@ import android.telephony.data.ApnSetting;
|
||||
import android.telephony.emergency.EmergencyNumber;
|
||||
import android.telephony.ims.ImsCallSession;
|
||||
import android.telephony.ims.ImsReasonInfo;
|
||||
import android.telephony.ims.MediaQualityStatus;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.LocalLog;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.app.IBatteryStats;
|
||||
@@ -357,6 +359,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
|
||||
private CallQuality[] mCallQuality;
|
||||
|
||||
private List<SparseArray<MediaQualityStatus>> mMediaQualityStatus;
|
||||
|
||||
private ArrayList<List<CallState>> mCallStateLists;
|
||||
|
||||
// network type of the call associated with the mCallStateLists and mCallQuality
|
||||
@@ -483,6 +487,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
TelephonyCallback.EVENT_DATA_ENABLED_CHANGED);
|
||||
REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
|
||||
TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED);
|
||||
REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
|
||||
TelephonyCallback.EVENT_MEDIA_QUALITY_STATUS_CHANGED);
|
||||
}
|
||||
|
||||
private boolean isLocationPermissionRequired(Set<Integer> events) {
|
||||
@@ -715,6 +721,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
cutListToSize(mCarrierPrivilegeStates, mNumPhones);
|
||||
cutListToSize(mCarrierServiceStates, mNumPhones);
|
||||
cutListToSize(mCallStateLists, mNumPhones);
|
||||
cutListToSize(mMediaQualityStatus, mNumPhones);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -738,6 +745,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
|
||||
mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
|
||||
mCallQuality[i] = createCallQuality();
|
||||
mMediaQualityStatus.add(i, new SparseArray<>());
|
||||
mCallStateLists.add(i, new ArrayList<>());
|
||||
mCallNetworkType[i] = TelephonyManager.NETWORK_TYPE_UNKNOWN;
|
||||
mPreciseCallState[i] = createPreciseCallState();
|
||||
@@ -805,6 +813,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
mCallDisconnectCause = new int[numPhones];
|
||||
mCallPreciseDisconnectCause = new int[numPhones];
|
||||
mCallQuality = new CallQuality[numPhones];
|
||||
mMediaQualityStatus = new ArrayList<>();
|
||||
mCallNetworkType = new int[numPhones];
|
||||
mCallStateLists = new ArrayList<>();
|
||||
mPreciseDataConnectionStates = new ArrayList<>();
|
||||
@@ -844,6 +853,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
|
||||
mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
|
||||
mCallQuality[i] = createCallQuality();
|
||||
mMediaQualityStatus.add(i, new SparseArray<>());
|
||||
mCallStateLists.add(i, new ArrayList<>());
|
||||
mCallNetworkType[i] = TelephonyManager.NETWORK_TYPE_UNKNOWN;
|
||||
mPreciseCallState[i] = createPreciseCallState();
|
||||
@@ -1392,6 +1402,32 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
remove(r.binder);
|
||||
}
|
||||
}
|
||||
if (events.contains(TelephonyCallback.EVENT_MEDIA_QUALITY_STATUS_CHANGED)) {
|
||||
CallState callState = null;
|
||||
for (CallState cs : mCallStateLists.get(r.phoneId)) {
|
||||
if (cs.getCallState() == PreciseCallState.PRECISE_CALL_STATE_ACTIVE) {
|
||||
callState = cs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (callState != null) {
|
||||
String callId = callState.getImsCallSessionId();
|
||||
try {
|
||||
MediaQualityStatus status = mMediaQualityStatus.get(r.phoneId).get(
|
||||
MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO);
|
||||
if (status != null && status.getCallSessionId().equals(callId)) {
|
||||
r.callback.onMediaQualityStatusChanged(status);
|
||||
}
|
||||
status = mMediaQualityStatus.get(r.phoneId).get(
|
||||
MediaQualityStatus.MEDIA_SESSION_TYPE_VIDEO);
|
||||
if (status != null && status.getCallSessionId().equals(callId)) {
|
||||
r.callback.onMediaQualityStatusChanged(status);
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
remove(r.binder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2283,6 +2319,17 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
}
|
||||
mCallStateLists.get(phoneId).add(builder.build());
|
||||
}
|
||||
boolean hasOngoingCall = false;
|
||||
for (CallState cs : mCallStateLists.get(phoneId)) {
|
||||
if (cs.getCallState() != PreciseCallState.PRECISE_CALL_STATE_DISCONNECTED) {
|
||||
hasOngoingCall = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasOngoingCall) {
|
||||
//no ongoing call. clear media quality status cached.
|
||||
mMediaQualityStatus.get(phoneId).clear();
|
||||
}
|
||||
}
|
||||
|
||||
for (Record r : mRecords) {
|
||||
@@ -2626,7 +2673,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleRemoveListLocked();
|
||||
}
|
||||
}
|
||||
@@ -3127,6 +3173,55 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyMediaQualityStatusChanged(int phoneId, int subId, MediaQualityStatus status) {
|
||||
if (!checkNotifyPermission("notifyMediaQualityStatusChanged()")) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mRecords) {
|
||||
if (validatePhoneId(phoneId)) {
|
||||
if (mCallStateLists.get(phoneId).size() > 0) {
|
||||
CallState callState = null;
|
||||
for (CallState cs : mCallStateLists.get(phoneId)) {
|
||||
if (cs.getCallState() == PreciseCallState.PRECISE_CALL_STATE_ACTIVE) {
|
||||
callState = cs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (callState != null) {
|
||||
String callSessionId = callState.getImsCallSessionId();
|
||||
if (callSessionId != null
|
||||
&& callSessionId.equals(status.getCallSessionId())) {
|
||||
mMediaQualityStatus.get(phoneId)
|
||||
.put(status.getMediaSessionType(), status);
|
||||
} else {
|
||||
log("SessionId mismatch active call:" + callSessionId
|
||||
+ " media quality:" + status.getCallSessionId());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
log("There is no active call to report CallQaulity");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Record r : mRecords) {
|
||||
if (r.matchTelephonyCallbackEvent(
|
||||
TelephonyCallback.EVENT_MEDIA_QUALITY_STATUS_CHANGED)
|
||||
&& idMatch(r, subId, phoneId)) {
|
||||
try {
|
||||
r.callback.onMediaQualityStatusChanged(status);
|
||||
} catch (RemoteException ex) {
|
||||
mRemoveList.add(r.binder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handleRemoveListLocked();
|
||||
}
|
||||
}
|
||||
|
||||
@NeverCompile // Avoid size overhead of debugging code.
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
|
||||
|
||||
@@ -45,6 +45,7 @@ import android.telephony.gba.UaSecurityProtocolIdentifier;
|
||||
import android.telephony.ims.ImsReasonInfo;
|
||||
import android.telephony.ims.ImsRegistrationAttributes;
|
||||
import android.telephony.ims.ImsSsData;
|
||||
import android.telephony.ims.MediaQualityStatus;
|
||||
import android.telephony.ims.RcsUceAdapter;
|
||||
import android.telephony.ims.feature.MmTelFeature;
|
||||
import android.telephony.ims.feature.RcsFeature;
|
||||
@@ -6347,6 +6348,50 @@ public class CarrierConfigManager {
|
||||
public static final String KEY_DTMFNB_PAYLOAD_TYPE_INT_ARRAY =
|
||||
KEY_PREFIX + "dtmfnb_payload_type_int_array";
|
||||
|
||||
/**
|
||||
* This indicates the threshold for RTP packet loss rate in percentage. If measured packet
|
||||
* loss rate crosses this, a callback with {@link MediaQualityStatus} will be invoked to
|
||||
* listeners.
|
||||
* See {@link android.telephony.TelephonyCallback.MediaQualityStatusChangedListener}
|
||||
*
|
||||
* <p/>
|
||||
* Valid threshold range : 0 ~ 100
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String KEY_VOICE_RTP_PACKET_LOSS_RATE_THRESHOLD_INT =
|
||||
KEY_PREFIX + "rtp_packet_loss_rate_threshold_int";
|
||||
|
||||
|
||||
/**
|
||||
* This indicates the threshold for RTP jitter value in milliseconds (RFC3550). If measured
|
||||
* jitter value crosses this, a callback with {@link MediaQualityStatus} will be invoked
|
||||
* to listeners.
|
||||
* See {@link android.telephony.TelephonyCallback.MediaQualityStatusChangedListener}
|
||||
*
|
||||
* <p/>
|
||||
* Valid threshold range : 0 ~ 10000
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String KEY_VOICE_RTP_JITTER_THRESHOLD_MILLIS_INT =
|
||||
KEY_PREFIX + "rtp_jitter_threshold_millis_int";
|
||||
|
||||
/**
|
||||
* This indicates the threshold for RTP inactivity time in milliseconds. If measured
|
||||
* inactivity timer crosses this, a callback with {@link MediaQualityStatus} will be invoked
|
||||
* to listeners.
|
||||
* See {@link android.telephony.TelephonyCallback.MediaQualityStatusChangedListener}
|
||||
*
|
||||
* <p/>
|
||||
* Valid threshold range : 0 ~ 60000
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String KEY_VOICE_RTP_INACTIVITY_TIME_THRESHOLD_MILLIS_LONG =
|
||||
KEY_PREFIX + "rtp_inactivity_time_threshold_millis_long";
|
||||
|
||||
|
||||
/** @hide */
|
||||
@IntDef({
|
||||
BANDWIDTH_EFFICIENT,
|
||||
@@ -6833,6 +6878,9 @@ public class CarrierConfigManager {
|
||||
defaults.putInt(KEY_AUDIO_AS_BANDWIDTH_KBPS_INT, 41);
|
||||
defaults.putInt(KEY_AUDIO_RS_BANDWIDTH_BPS_INT, 600);
|
||||
defaults.putInt(KEY_AUDIO_RR_BANDWIDTH_BPS_INT, 2000);
|
||||
defaults.putInt(KEY_VOICE_RTP_PACKET_LOSS_RATE_THRESHOLD_INT, 40);
|
||||
defaults.putInt(KEY_VOICE_RTP_JITTER_THRESHOLD_MILLIS_INT, 120);
|
||||
defaults.putLong(KEY_VOICE_RTP_INACTIVITY_TIME_THRESHOLD_MILLIS_LONG, 5000);
|
||||
|
||||
defaults.putIntArray(
|
||||
KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY,
|
||||
|
||||
19
telephony/java/android/telephony/ims/MediaQualityStatus.aidl
Normal file
19
telephony/java/android/telephony/ims/MediaQualityStatus.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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 MediaQualityStatus;
|
||||
287
telephony/java/android/telephony/ims/MediaQualityStatus.java
Normal file
287
telephony/java/android/telephony/ims/MediaQualityStatus.java
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.IntDef;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.telephony.AccessNetworkConstants.TransportType;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A representation of Media quality status.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class MediaQualityStatus implements Parcelable {
|
||||
public static final int MEDIA_SESSION_TYPE_AUDIO = 1;
|
||||
public static final int MEDIA_SESSION_TYPE_VIDEO = 2;
|
||||
|
||||
private final String mImsCallSessionId;
|
||||
private final int mMediaSessionType;
|
||||
private final int mTransportType;
|
||||
private final int mRtpPacketLossRate;
|
||||
private final int mRtpJitter;
|
||||
private final long mRtpInactivityTimeMillis;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(
|
||||
value = {
|
||||
MEDIA_SESSION_TYPE_AUDIO,
|
||||
MEDIA_SESSION_TYPE_VIDEO,
|
||||
})
|
||||
public @interface MediaSessionType {}
|
||||
|
||||
/**
|
||||
* Constructor for this
|
||||
*
|
||||
* @param imsCallSessionId IMS call session id of this quality status
|
||||
* @param mediaSessionType media session type of this quality status
|
||||
* @param transportType transport type of this quality status
|
||||
* @param rtpPacketLossRate measured RTP packet loss rate
|
||||
* @param rtpJitter measured RTP jitter value
|
||||
* @param rptInactivityTimeMillis measured RTP inactivity time in milliseconds
|
||||
*/
|
||||
private MediaQualityStatus(@NonNull String imsCallSessionId,
|
||||
@MediaSessionType int mediaSessionType, @TransportType int transportType,
|
||||
int rtpPacketLossRate, int rtpJitter, long rptInactivityTimeMillis) {
|
||||
mImsCallSessionId = imsCallSessionId;
|
||||
mMediaSessionType = mediaSessionType;
|
||||
mTransportType = transportType;
|
||||
mRtpPacketLossRate = rtpPacketLossRate;
|
||||
mRtpJitter = rtpJitter;
|
||||
mRtpInactivityTimeMillis = rptInactivityTimeMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves call session ID for this quality status
|
||||
*/
|
||||
@NonNull
|
||||
public String getCallSessionId() {
|
||||
return mImsCallSessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves media session type of this quality status
|
||||
*/
|
||||
public @MediaSessionType int getMediaSessionType() {
|
||||
return mMediaSessionType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves Transport type for which this media quality was measured.
|
||||
*/
|
||||
public @TransportType int getTransportType() {
|
||||
return mTransportType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves measured RTP packet loss rate in percentage.
|
||||
*/
|
||||
@IntRange(from = 0, to = 100)
|
||||
public int getRtpPacketLossRate() {
|
||||
return mRtpPacketLossRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves measured RTP jitter(RFC3550) value in milliseconds
|
||||
*/
|
||||
public int getRtpJitterMillis() {
|
||||
return mRtpJitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves measured RTP inactivity time in milliseconds
|
||||
*/
|
||||
public long getRtpInactivityMillis() {
|
||||
return mRtpInactivityTimeMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link MediaQualityStatus} from a parcel.
|
||||
* @param in The parceled data to read.
|
||||
*/
|
||||
private MediaQualityStatus(@NonNull Parcel in) {
|
||||
mImsCallSessionId = in.readString();
|
||||
mMediaSessionType = in.readInt();
|
||||
mTransportType = in.readInt();
|
||||
mRtpPacketLossRate = in.readInt();
|
||||
mRtpJitter = in.readInt();
|
||||
mRtpInactivityTimeMillis = in.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeString(mImsCallSessionId);
|
||||
dest.writeInt(mMediaSessionType);
|
||||
dest.writeInt(mTransportType);
|
||||
dest.writeInt(mRtpPacketLossRate);
|
||||
dest.writeInt(mRtpJitter);
|
||||
dest.writeLong(mRtpInactivityTimeMillis);
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<MediaQualityStatus> CREATOR =
|
||||
new Creator<MediaQualityStatus>() {
|
||||
@Override
|
||||
public MediaQualityStatus createFromParcel(@NonNull Parcel in) {
|
||||
return new MediaQualityStatus(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaQualityStatus[] newArray(int size) {
|
||||
return new MediaQualityStatus[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MediaQualityStatus that = (MediaQualityStatus) o;
|
||||
return mImsCallSessionId != null && mImsCallSessionId.equals(that.mImsCallSessionId)
|
||||
&& mMediaSessionType == that.mMediaSessionType
|
||||
&& mTransportType == that.mTransportType
|
||||
&& mRtpPacketLossRate == that.mRtpPacketLossRate
|
||||
&& mRtpJitter == that.mRtpJitter
|
||||
&& mRtpInactivityTimeMillis == that.mRtpInactivityTimeMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mImsCallSessionId, mMediaSessionType, mTransportType,
|
||||
mRtpPacketLossRate, mRtpJitter, mRtpInactivityTimeMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("MediaThreshold{mImsCallSessionId=");
|
||||
sb.append(mImsCallSessionId);
|
||||
sb.append(", mMediaSessionType=");
|
||||
sb.append(mMediaSessionType);
|
||||
sb.append(", mTransportType=");
|
||||
sb.append(mTransportType);
|
||||
sb.append(", mRtpPacketLossRate=");
|
||||
sb.append(mRtpPacketLossRate);
|
||||
sb.append(", mRtpJitter=");
|
||||
sb.append(mRtpJitter);
|
||||
sb.append(", mRtpInactivityTimeMillis=");
|
||||
sb.append(mRtpInactivityTimeMillis);
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a convenient way to set the fields of an {@link MediaQualityStatus} when creating a
|
||||
* new instance.
|
||||
*
|
||||
* <p>The example below shows how you might create a new {@code RtpQualityStatus}:
|
||||
*
|
||||
* <pre><code>
|
||||
*
|
||||
* MediaQualityStatus = new MediaQualityStatus.Builder(
|
||||
* callSessionId, mediaSessionType, transportType)
|
||||
* .setRtpPacketLossRate(packetLossRate)
|
||||
* .setRtpJitter(jitter)
|
||||
* .setRtpInactivityMillis(inactivityTimeMillis)
|
||||
* .build();
|
||||
* </code></pre>
|
||||
*/
|
||||
public static final class Builder {
|
||||
private final String mImsCallSessionId;
|
||||
private final int mMediaSessionType;
|
||||
private final int mTransportType;
|
||||
private int mRtpPacketLossRate;
|
||||
private int mRtpJitter;
|
||||
private long mRtpInactivityTimeMillis;
|
||||
|
||||
/**
|
||||
* Default constructor for the Builder.
|
||||
*/
|
||||
public Builder(
|
||||
@NonNull String imsCallSessionId,
|
||||
@MediaSessionType int mediaSessionType,
|
||||
int transportType) {
|
||||
mImsCallSessionId = imsCallSessionId;
|
||||
mMediaSessionType = mediaSessionType;
|
||||
mTransportType = transportType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set RTP packet loss info.
|
||||
*
|
||||
* @param packetLossRate RTP packet loss rate in percentage
|
||||
* @return The same instance of the builder.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setRtpPacketLossRate(@IntRange(from = 0, to = 100) int packetLossRate) {
|
||||
this.mRtpPacketLossRate = packetLossRate;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set calculated RTP jitter(RFC3550) value in milliseconds.
|
||||
*
|
||||
* @param jitter calculated RTP jitter value.
|
||||
* @return The same instance of the builder.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setRtpJitterMillis(int jitter) {
|
||||
this.mRtpJitter = jitter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set measured RTP inactivity time.
|
||||
*
|
||||
* @param inactivityTimeMillis RTP inactivity time in Milliseconds.
|
||||
* @return The same instance of the builder.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setRtpInactivityMillis(long inactivityTimeMillis) {
|
||||
this.mRtpInactivityTimeMillis = inactivityTimeMillis;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the {@link MediaQualityStatus}
|
||||
*
|
||||
* @return the {@link MediaQualityStatus} object
|
||||
*/
|
||||
@NonNull
|
||||
public MediaQualityStatus build() {
|
||||
return new MediaQualityStatus(
|
||||
mImsCallSessionId,
|
||||
mMediaSessionType,
|
||||
mTransportType,
|
||||
mRtpPacketLossRate,
|
||||
mRtpJitter,
|
||||
mRtpInactivityTimeMillis);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
telephony/java/android/telephony/ims/MediaThreshold.aidl
Normal file
19
telephony/java/android/telephony/ims/MediaThreshold.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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 MediaThreshold;
|
||||
333
telephony/java/android/telephony/ims/MediaThreshold.java
Normal file
333
telephony/java/android/telephony/ims/MediaThreshold.java
Normal file
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.telephony.ims.feature.MmTelFeature;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* A MediaThreshold represents a series of packet loss rate, jitter and rtp inactivity time
|
||||
* thresholds which when crossed should result in a {@link MediaQualityStatus} report being
|
||||
* generated by the {@link ImsService} via {@link MmTelFeature#notifyMediaQualityStatusChanged(
|
||||
* MediaQualityStatus)}
|
||||
*
|
||||
* <p/>
|
||||
* A {@link MediaQualityStatus} should be triggered when any of various
|
||||
* attributes pass one of the thresholds defined here.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class MediaThreshold implements Parcelable {
|
||||
private final int[] mRtpPacketLossRate;
|
||||
private final int[] mRtpJitter;
|
||||
private final long[] mRtpInactivityTimeMillis;
|
||||
|
||||
/**
|
||||
* Retrieves threshold values for RTP packet loss rate in percentage.
|
||||
*
|
||||
* @return int array including threshold values for packet loss rate
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
@SystemApi
|
||||
public int[] getThresholdsRtpPacketLossRate() {
|
||||
return mRtpPacketLossRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves threshold values for jitter(RFC3550) in milliseconds.
|
||||
*
|
||||
* @return int array including threshold values for RTP jitter.
|
||||
*/
|
||||
@NonNull
|
||||
public int[] getThresholdsRtpJitterMillis() {
|
||||
return mRtpJitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves threshold values for RTP inactivity time in milliseconds.
|
||||
*
|
||||
* @return int array including threshold values for RTP inactivity time.
|
||||
*/
|
||||
@NonNull
|
||||
public long[] getThresholdsRtpInactivityTimeMillis() {
|
||||
return mRtpInactivityTimeMillis;
|
||||
}
|
||||
|
||||
private MediaThreshold(
|
||||
int[] packetLossRateThresholds,
|
||||
int[] jitterThresholds,
|
||||
long[] inactivityTimeThresholds) {
|
||||
mRtpPacketLossRate = packetLossRateThresholds;
|
||||
mRtpJitter = jitterThresholds;
|
||||
mRtpInactivityTimeMillis = inactivityTimeThresholds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link MediaThreshold} from a parcel.
|
||||
* @param in The parceled data to read.
|
||||
*/
|
||||
private MediaThreshold(@NonNull Parcel in) {
|
||||
mRtpPacketLossRate = in.createIntArray();
|
||||
mRtpJitter = in.createIntArray();
|
||||
mRtpInactivityTimeMillis = in.createLongArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeIntArray(mRtpPacketLossRate);
|
||||
dest.writeIntArray(mRtpJitter);
|
||||
dest.writeLongArray(mRtpInactivityTimeMillis);
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<MediaThreshold> CREATOR =
|
||||
new Creator<MediaThreshold>() {
|
||||
@Override
|
||||
public MediaThreshold createFromParcel(@NonNull Parcel in) {
|
||||
return new MediaThreshold(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaThreshold[] newArray(int size) {
|
||||
return new MediaThreshold[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether the RTP packet loss rate threshold is valid or not.
|
||||
*
|
||||
* @param packetLossRate packet loss rate
|
||||
* @return the threshold is valid or not.
|
||||
* @hide
|
||||
*/
|
||||
public static boolean isValidRtpPacketLossRate(int packetLossRate) {
|
||||
return (packetLossRate >= 0 && packetLossRate <= 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the RTP jitter threshold is valid or not.
|
||||
*
|
||||
* @param jitter jitter value in milliseconds
|
||||
* @return the threshold is valid or not.
|
||||
* @hide
|
||||
*/
|
||||
public static boolean isValidJitterMillis(int jitter) {
|
||||
return (jitter >= 0 && jitter <= 10000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the RTP packet loss rate threshold is valid or not.
|
||||
*
|
||||
* @param inactivityTime packet loss rate
|
||||
* @return the threshold is valid or not.
|
||||
* @hide
|
||||
*/
|
||||
public static boolean isValidRtpInactivityTimeMillis(long inactivityTime) {
|
||||
return (inactivityTime >= 0 && inactivityTime <= 60000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MediaThreshold that = (MediaThreshold) o;
|
||||
return Arrays.equals(mRtpPacketLossRate, that.mRtpPacketLossRate)
|
||||
&& Arrays.equals(mRtpJitter, that.mRtpJitter)
|
||||
&& Arrays.equals(mRtpInactivityTimeMillis, that.mRtpInactivityTimeMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(Arrays.hashCode(mRtpPacketLossRate), Arrays.hashCode(mRtpJitter),
|
||||
Arrays.hashCode(mRtpInactivityTimeMillis));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("MediaThreshold{mRtpPacketLossRate=");
|
||||
for (int i : mRtpPacketLossRate) {
|
||||
sb.append(" ").append(i);
|
||||
}
|
||||
sb.append(", mRtpJitter=");
|
||||
for (int b : mRtpJitter) {
|
||||
sb.append(" ").append(b);
|
||||
}
|
||||
sb.append(", mRtpInactivityTimeMillis=");
|
||||
for (long i : mRtpInactivityTimeMillis) {
|
||||
sb.append(" ").append(i);
|
||||
}
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a convenient way to set the fields of an {@link MediaThreshold} when creating a
|
||||
* new instance.
|
||||
*
|
||||
* <p>The example below shows how you might create a new {@code RtpThreshold}:
|
||||
*
|
||||
* <pre><code>
|
||||
*
|
||||
* RtpThreshold = new RtpThreshold.Builder()
|
||||
* .setRtpSessionType({@link MediaQualityStatus#MEDIA_SESSION_TYPE_AUDIO} or
|
||||
* {@link MediaQualityStatus#MEDIA_SESSION_TYPE_VIDEO})
|
||||
* .setThresholdsRtpPacketLossRate(int[] packetLossRateThresholds)
|
||||
* .setThresholdsRtpJitterMillis(int[] jitterThresholds)
|
||||
* .setThresholdsRtpInactivityTimeMillis(int[] inactivityTimeThresholds)
|
||||
* .build();
|
||||
* </code></pre>
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final class Builder {
|
||||
private int[] mRtpPacketLossRate = null;
|
||||
private int[] mRtpJitter = null;
|
||||
private long[] mRtpInactivityTimeMillis = null;
|
||||
|
||||
/**
|
||||
* Default constructor for the Builder.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set threshold values for RTP packet loss rate in percentage.
|
||||
* <p/>
|
||||
* The packet loss calculation should be done at least once per
|
||||
* second. It should be calculated with at least the last 3 seconds
|
||||
* of data.
|
||||
*
|
||||
* @param packetLossRateThresholds int array for threshold values.
|
||||
* @return The same instance of the builder.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setThresholdsRtpPacketLossRate(int[] packetLossRateThresholds) {
|
||||
if (packetLossRateThresholds.length > 0) {
|
||||
TreeSet<Integer> thresholds = new TreeSet<>();
|
||||
for (Integer value : packetLossRateThresholds) {
|
||||
if (isValidRtpPacketLossRate(value)) {
|
||||
thresholds.add(value);
|
||||
}
|
||||
}
|
||||
int[] targetArray = new int[thresholds.size()];
|
||||
int i = 0;
|
||||
for (int element : thresholds) {
|
||||
targetArray[i++] = element;
|
||||
}
|
||||
this.mRtpPacketLossRate = targetArray;
|
||||
} else {
|
||||
this.mRtpPacketLossRate = packetLossRateThresholds;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set threshold values for RTP jitter in Milliseconds.
|
||||
*
|
||||
* @param jitterThresholds int array including threshold values for Jitter.
|
||||
* @return The same instance of the builder.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setThresholdsRtpJitterMillis(int[] jitterThresholds) {
|
||||
if (jitterThresholds.length > 0) {
|
||||
TreeSet<Integer> thresholds = new TreeSet<>();
|
||||
for (Integer value : jitterThresholds) {
|
||||
if (isValidJitterMillis(value)) {
|
||||
thresholds.add(value);
|
||||
}
|
||||
}
|
||||
int[] targetArray = new int[thresholds.size()];
|
||||
int i = 0;
|
||||
for (int element : thresholds) {
|
||||
targetArray[i++] = element;
|
||||
}
|
||||
this.mRtpJitter = targetArray;
|
||||
} else {
|
||||
this.mRtpJitter = jitterThresholds;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set threshold values for RTP inactivity time.
|
||||
*
|
||||
* @param inactivityTimeThresholds int array including threshold
|
||||
* values for RTP inactivity time.
|
||||
* @return The same instance of the builder.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setThresholdsRtpInactivityTimeMillis(long[] inactivityTimeThresholds) {
|
||||
if (inactivityTimeThresholds.length > 0) {
|
||||
TreeSet<Long> thresholds = new TreeSet<>();
|
||||
for (Long value : inactivityTimeThresholds) {
|
||||
if (isValidRtpInactivityTimeMillis(value)) {
|
||||
thresholds.add(value);
|
||||
}
|
||||
}
|
||||
long[] targetArray = new long[thresholds.size()];
|
||||
int i = 0;
|
||||
for (long element : thresholds) {
|
||||
targetArray[i++] = element;
|
||||
}
|
||||
this.mRtpInactivityTimeMillis = targetArray;
|
||||
} else {
|
||||
this.mRtpInactivityTimeMillis = inactivityTimeThresholds;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the {@link MediaThreshold}
|
||||
*
|
||||
* @return the {@link MediaThreshold} object
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public MediaThreshold build() {
|
||||
mRtpPacketLossRate = mRtpPacketLossRate != null ? mRtpPacketLossRate : new int[0];
|
||||
mRtpJitter = mRtpJitter != null ? mRtpJitter : new int[0];
|
||||
mRtpInactivityTimeMillis =
|
||||
mRtpInactivityTimeMillis != null ? mRtpInactivityTimeMillis : new long[0];
|
||||
return new MediaThreshold(mRtpPacketLossRate, mRtpJitter, mRtpInactivityTimeMillis);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ import android.telephony.ims.aidl.IImsSmsListener;
|
||||
import android.telephony.ims.aidl.IImsCapabilityCallback;
|
||||
import android.telephony.ims.aidl.ISrvccStartedCallback;
|
||||
import android.telephony.ims.feature.CapabilityChangeRequest;
|
||||
import android.telephony.ims.MediaQualityStatus;
|
||||
import android.telephony.ims.MediaThreshold;
|
||||
import android.telephony.ims.RtpHeaderExtensionType;
|
||||
|
||||
import android.telephony.ims.ImsCallProfile;
|
||||
@@ -60,6 +62,9 @@ interface IImsMmTelFeature {
|
||||
oneway void notifySrvccCompleted();
|
||||
oneway void notifySrvccFailed();
|
||||
oneway void notifySrvccCanceled();
|
||||
oneway void setMediaQualityThreshold(int mediaSessionType, in MediaThreshold threshold);
|
||||
MediaQualityStatus queryMediaQualityStatus(int mediaSessionType);
|
||||
|
||||
// SMS APIs
|
||||
void setSmsListener(IImsSmsListener l);
|
||||
oneway void sendSms(in int token, int messageRef, String format, String smsc, boolean retry,
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.os.Bundle;
|
||||
|
||||
import android.telephony.ims.ImsCallProfile;
|
||||
import android.telephony.ims.ImsReasonInfo;
|
||||
import android.telephony.ims.MediaQualityStatus;
|
||||
import android.telephony.ims.aidl.IImsCallSessionListener;
|
||||
import android.telephony.ims.aidl.IImsTrafficSessionCallback;
|
||||
|
||||
@@ -42,4 +43,5 @@ interface IImsMmTelListener {
|
||||
int trafficDirection, in IImsTrafficSessionCallback callback);
|
||||
oneway void onModifyImsTrafficSession(int token, int accessNetworkType);
|
||||
oneway void onStopImsTrafficSession(int token);
|
||||
oneway void onMediaQualityStatusChanged(in MediaQualityStatus status);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ import android.telephony.ims.ImsCallSessionListener;
|
||||
import android.telephony.ims.ImsException;
|
||||
import android.telephony.ims.ImsReasonInfo;
|
||||
import android.telephony.ims.ImsService;
|
||||
import android.telephony.ims.MediaQualityStatus;
|
||||
import android.telephony.ims.MediaThreshold;
|
||||
import android.telephony.ims.RtpHeaderExtensionType;
|
||||
import android.telephony.ims.SrvccCall;
|
||||
import android.telephony.ims.aidl.IImsCallSessionListener;
|
||||
@@ -257,7 +259,7 @@ public class MmTelFeature extends ImsFeature {
|
||||
public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
|
||||
IImsCapabilityCallback c) {
|
||||
executeMethodAsyncNoException(() -> MmTelFeature.this
|
||||
.requestChangeEnabledCapabilities(request, c),
|
||||
.requestChangeEnabledCapabilities(request, c),
|
||||
"changeCapabilitiesConfiguration");
|
||||
}
|
||||
|
||||
@@ -268,6 +270,26 @@ public class MmTelFeature extends ImsFeature {
|
||||
capability, radioTech, c), "queryCapabilityConfiguration");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaQualityThreshold(@MediaQualityStatus.MediaSessionType int sessionType,
|
||||
MediaThreshold mediaThreshold) {
|
||||
if (mediaThreshold != null) {
|
||||
executeMethodAsyncNoException(() -> setMediaThreshold(sessionType, mediaThreshold),
|
||||
"setMediaQualityThreshold");
|
||||
} else {
|
||||
executeMethodAsyncNoException(() -> clearMediaThreshold(sessionType),
|
||||
"clearMediaQualityThreshold");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaQualityStatus queryMediaQualityStatus(
|
||||
@MediaQualityStatus.MediaSessionType int sessionType)
|
||||
throws RemoteException {
|
||||
return executeMethodAsyncForResult(() -> MmTelFeature.this.queryMediaQualityStatus(
|
||||
sessionType), "queryMediaQualityStatus");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSmsListener(IImsSmsListener l) {
|
||||
executeMethodAsyncNoException(() -> MmTelFeature.this.setSmsListener(l),
|
||||
@@ -696,6 +718,17 @@ public class MmTelFeature extends ImsFeature {
|
||||
public void onStopImsTrafficSession(int token) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the IMS provider notifies {@link MediaQualityStatus}.
|
||||
*
|
||||
* @param status media quality status currently measured.
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void onMediaQualityStatusChanged(MediaQualityStatus status) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1059,6 +1092,32 @@ public class MmTelFeature extends ImsFeature {
|
||||
super.notifyCapabilitiesStatusChanged(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the framework that the measured media quality has crossed a threshold set by {@link
|
||||
* MmTelFeature#setMediaThreshold}
|
||||
*
|
||||
* @param status current media quality status measured.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final void notifyMediaQualityStatusChanged(
|
||||
@NonNull MediaQualityStatus status) {
|
||||
if (status == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"MediaQualityStatus must be non-null!");
|
||||
}
|
||||
Log.i(LOG_TAG, "notifyMediaQualityStatusChanged " + status);
|
||||
IImsMmTelListener listener = getListener();
|
||||
if (listener == null) {
|
||||
throw new IllegalStateException("Session is not available.");
|
||||
}
|
||||
try {
|
||||
listener.onMediaQualityStatusChanged(status);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the framework of an incoming call.
|
||||
* @param c The {@link ImsCallSessionImplBase} of the new incoming call.
|
||||
@@ -1391,6 +1450,62 @@ public class MmTelFeature extends ImsFeature {
|
||||
// Base implementation, no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the framework to pass {@link MediaThreshold}. The MmTelFeature should override this
|
||||
* method to get Media quality threshold. This will pass the consolidated threshold values from
|
||||
* Telephony framework. IMS provider needs to monitor media quality of active call and notify
|
||||
* media quality {@link #notifyMediaQualityStatusChanged(MediaQualityStatus)} when the measured
|
||||
* media quality crosses at least one of {@link MediaThreshold} set by this.
|
||||
*
|
||||
* @param mediaSessionType media session type for this Threshold info.
|
||||
* @param mediaThreshold media threshold information
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public void setMediaThreshold(
|
||||
@MediaQualityStatus.MediaSessionType int mediaSessionType,
|
||||
@NonNull MediaThreshold mediaThreshold) {
|
||||
// Base Implementation - Should be overridden.
|
||||
Log.d(LOG_TAG, "setMediaThreshold is not supported." + mediaThreshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* The MmTelFeature should override this method to clear Media quality thresholds that were
|
||||
* registered and stop media quality status updates.
|
||||
*
|
||||
* @param mediaSessionType media session type
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public void clearMediaThreshold(@MediaQualityStatus.MediaSessionType int mediaSessionType) {
|
||||
// Base Implementation - Should be overridden.
|
||||
Log.d(LOG_TAG, "clearMediaThreshold is not supported." + mediaSessionType);
|
||||
}
|
||||
|
||||
/**
|
||||
* IMS provider should override this method to return currently measured media quality status.
|
||||
*
|
||||
* <p/>
|
||||
* If media quality status is not yet measured after call is active, it needs to notify media
|
||||
* quality status {@link #notifyMediaQualityStatusChanged(MediaQualityStatus)} when the first
|
||||
* measurement is done.
|
||||
*
|
||||
* @param mediaSessionType media session type
|
||||
* @return Current media quality status. It could be null if media quality status is not
|
||||
* measured yet or {@link MediaThreshold} was not set corresponding to the media session
|
||||
* type.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public MediaQualityStatus queryMediaQualityStatus(
|
||||
@MediaQualityStatus.MediaSessionType int mediaSessionType) {
|
||||
// Base Implementation - Should be overridden.
|
||||
Log.d(LOG_TAG, "queryMediaQualityStatus is not supported." + mediaSessionType);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user