diff options
Diffstat (limited to 'services')
12 files changed, 2151 insertions, 192 deletions
diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsCallStatusTracker.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsCallStatusTracker.java index 0800787..095cf95 100644 --- a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsCallStatusTracker.java +++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsCallStatusTracker.java @@ -19,12 +19,20 @@ package com.android.telephony.qns; import android.annotation.NonNull; import android.net.NetworkCapabilities; import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.telephony.AccessNetworkConstants; +import android.telephony.Annotation; +import android.telephony.CallQuality; import android.telephony.CallState; import android.telephony.PreciseCallState; import android.telephony.PreciseDataConnectionState; import android.telephony.TelephonyManager; import android.telephony.ims.ImsCallProfile; +import android.telephony.ims.MediaQualityStatus; import android.util.Log; +import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; @@ -38,19 +46,695 @@ import java.util.function.Consumer; public class QnsCallStatusTracker { private final String mLogTag; private QnsTelephonyListener mTelephonyListener; + private QnsCarrierConfigManager mConfigManager; private List<CallState> mCallStates = new ArrayList<>(); private QnsRegistrant mCallTypeChangedEventListener; private QnsRegistrant mEmergencyCallTypeChangedEventListener; private int mLastNormalCallType = QnsConstants.CALL_TYPE_IDLE; private int mLastEmergencyCallType = QnsConstants.CALL_TYPE_IDLE; private boolean mEmergencyOverIms; + private ActiveCallTracker mActiveCallTracker; private Consumer<List<CallState>> mCallStatesConsumer = callStateList -> updateCallState(callStateList); private Consumer<Integer> mSrvccStateConsumer = state -> onSrvccStateChangedInternal(state); + private Consumer<MediaQualityStatus> mMediaQualityStatusConsumer = + status -> mActiveCallTracker.onMediaQualityStatusChanged(status); - QnsCallStatusTracker(QnsTelephonyListener telephonyListener, int slotIndex) { + static class CallQualityBlock { + int mUpLinkLevel; + int mDownLinkLevel; + long mCreatedElapsedTime; + long mDurationMillis; + CallQualityBlock(int uplinkLevel, int downLinkLevel, long createdElapsedTime) { + mUpLinkLevel = uplinkLevel; + mDownLinkLevel = downLinkLevel; + mCreatedElapsedTime = createdElapsedTime; + } + + long getUpLinkQualityVolume() { + if (mDurationMillis > 0) { + return mUpLinkLevel * mDurationMillis; + } else { + long now = QnsUtils.getSystemElapsedRealTime(); + return (now - mCreatedElapsedTime) * mUpLinkLevel; + } + } + + long getDownLinkQualityVolume() { + if (mDurationMillis > 0) { + return mDownLinkLevel * mDurationMillis; + } else { + long now = QnsUtils.getSystemElapsedRealTime(); + return (now - mCreatedElapsedTime) * mDownLinkLevel; + } + } + } + + class ActiveCallTracker { + private static final int EVENT_DATA_CONNECTION_STATUS_CHANGED = 3300; + + @QnsConstants.QnsCallType + private int mCallType = QnsConstants.CALL_TYPE_IDLE; + @Annotation.NetCapability + private int mNetCapability = QnsConstants.INVALID_VALUE; + private QnsRegistrantList mLowMediaQualityListeners = new QnsRegistrantList(); + private int mAccessNetwork = AccessNetworkConstants.AccessNetworkType.UNKNOWN; + private int mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; + private SparseArray<CallQuality> mCallQualities = new SparseArray(); + private TransportQuality mCurrentQuality; + /** A list of TransportQuality for each Transport type */ + private SparseArray<List<TransportQuality>> mTransportQualityArray = new SparseArray<>(); + private boolean mWwanAvailable = false; + private boolean mWlanAvailable = false; + + private boolean mMediaThresholdBreached = false; + private HandlerThread mHandlerThread; + private ActiveCallTrackerHandler mActiveCallHandler; + private MediaLowQualityHandler mLowQualityHandler; + private String mLogTag; + + private class ActiveCallTrackerHandler extends Handler { + ActiveCallTrackerHandler(Looper l) { + super(l); + } + + @Override + public void handleMessage(Message message) { + QnsAsyncResult ar; + int transportType; + Log.d(mLogTag, "handleMessage : " + message.what); + switch (message.what) { + case EVENT_DATA_CONNECTION_STATUS_CHANGED: + ar = (QnsAsyncResult) message.obj; + onDataConnectionStatusChanged( + (PreciseDataConnectionState) ar.mResult); + break; + + default: + Log.d(mLogTag, "unHandleMessage : " + message.what); + break; + } + + } + } + + /** Tracking low quality status */ + private class MediaLowQualityHandler extends Handler { + private static final int EVENT_MEDIA_QUALITY_CHANGED = 3401; + private static final int EVENT_PACKET_LOSS_TIMER_EXPIRED = 3402; + private static final int EVENT_HYSTERESIS_FOR_NORMAL_QUALITY = 3403; + private static final int EVENT_POLLING_CHECK_LOW_QUALITY = 3404; + private static final int EVENT_LOW_QUALITY_HANDLER_MAX = 3405; + + private static final int STATE_NORMAL_QUALITY = 0; + private static final int STATE_SUSPECT_LOW_QUALITY = 1; + private static final int STATE_LOW_QUALITY = 2; + + private static final int HYSTERESIS_TIME_NORMAL_QUALITY_MILLIS = 3000; + private static final int LOW_QUALITY_CHECK_INTERVAL_MILLIS = 15000; + private static final int LOW_QUALITY_CHECK_AFTER_HO_MILLIS = 3000; + private static final int LOW_QUALITY_REPORTED_TIME_INITIAL_VALUE = -1; + + private int mState = STATE_NORMAL_QUALITY; + private MediaQualityStatus mMediaQualityStatus; + private String mTag; + + MediaLowQualityHandler(Looper l) { + super(l); + mTag = mLogTag + "_LQH"; + } + + @Override + public void handleMessage(Message message) { + Log.d(mTag, "handleMessage : " + message.what); + switch (message.what) { + case EVENT_MEDIA_QUALITY_CHANGED: + MediaQualityStatus status = (MediaQualityStatus) message.obj; + onMediaQualityChanged(status); + break; + + case EVENT_PACKET_LOSS_TIMER_EXPIRED: + onPacketLossTimerExpired(message.arg1); + break; + + case EVENT_HYSTERESIS_FOR_NORMAL_QUALITY: + exitLowQualityState(); + break; + + case EVENT_POLLING_CHECK_LOW_QUALITY: + checkLowQuality(); + break; + + default: + Log.d(mLogTag, "unHandleMessage : " + message.what); + break; + } + } + + private void onMediaQualityChanged(MediaQualityStatus status) { + Log.d(mTag, "onMediaQualityChanged " + status); + int reason = thresholdBreached(status); + boolean needNotify = false; + if (reason == 0) { + // Threshold not breached. + mMediaQualityStatus = status; + if (mState == STATE_NORMAL_QUALITY) { + Log.d(mTag, "keeps normal quality."); + mMediaQualityStatus = status; + return; + } else { + // check normal quality is stable or not. + this.sendEmptyMessageDelayed(EVENT_HYSTERESIS_FOR_NORMAL_QUALITY, + HYSTERESIS_TIME_NORMAL_QUALITY_MILLIS); + } + } else { + // Threshold breached. + this.removeMessages(EVENT_HYSTERESIS_FOR_NORMAL_QUALITY); + switch (mState) { + case STATE_NORMAL_QUALITY: + case STATE_SUSPECT_LOW_QUALITY: + if (reason == (1 << QnsConstants.RTP_LOW_QUALITY_REASON_PACKET_LOSS)) { + int delayMillis = (mConfigManager.getRTPMetricsData()).mPktLossTime; + if (delayMillis > 0) { + if (mState == STATE_NORMAL_QUALITY) { + enterSuspectLowQualityState(delayMillis); + } + } else if (delayMillis == 0) { + needNotify = true; + } + } else { + removeMessages(EVENT_PACKET_LOSS_TIMER_EXPIRED); + enterLowQualityState(status); + needNotify = true; + } + break; + + case STATE_LOW_QUALITY: + if (mMediaQualityStatus.getTransportType() == status.getTransportType() + && thresholdBreached(mMediaQualityStatus) + != thresholdBreached(status)) { + needNotify = true; + } + break; + } + mMediaQualityStatus = status; + } + if (needNotify) { + enterLowQualityState(status); + notifyLowMediaQuality(reason); + } + + } + + @VisibleForTesting + void enterLowQualityState(MediaQualityStatus status) { + Log.d(mTag, "enterLowQualityState " + status); + mState = STATE_LOW_QUALITY; + this.sendEmptyMessageDelayed( + EVENT_POLLING_CHECK_LOW_QUALITY, LOW_QUALITY_CHECK_INTERVAL_MILLIS); + } + + void enterSuspectLowQualityState(int delayMillis) { + Log.d(mTag, "enterSuspectLowQualityState."); + if (!this.hasMessages(EVENT_PACKET_LOSS_TIMER_EXPIRED)) { + this.removeMessages(EVENT_PACKET_LOSS_TIMER_EXPIRED); + } + Log.d(mTag, "Packet loss timer start. " + delayMillis); + Message msg = this.obtainMessage( + EVENT_PACKET_LOSS_TIMER_EXPIRED, mTransportType, 0); + this.sendMessageDelayed(msg, delayMillis); + mState = STATE_SUSPECT_LOW_QUALITY; + } + + void exitLowQualityState() { + mState = STATE_NORMAL_QUALITY; + for (int i = EVENT_PACKET_LOSS_TIMER_EXPIRED; + i < EVENT_LOW_QUALITY_HANDLER_MAX; i++) { + this.removeMessages(i); + } + notifyLowMediaQuality(0); + } + + void checkLowQuality() { + if (mState == STATE_NORMAL_QUALITY) { + Log.w(mTag, "checkLowQuality on unexpected state(normal state)."); + } else { + Log.d(mTag, "checkLowQuality"); + int reason = thresholdBreached(mMediaQualityStatus); + if (reason > 0) { + notifyLowMediaQuality(thresholdBreached(mMediaQualityStatus)); + } else if (this.hasMessages(EVENT_HYSTERESIS_FOR_NORMAL_QUALITY)) { + // hysteresis time to be normal state is running. let's check after that. + this.sendEmptyMessageDelayed(EVENT_POLLING_CHECK_LOW_QUALITY, + HYSTERESIS_TIME_NORMAL_QUALITY_MILLIS); + } else { + Log.w(mTag, "Unexpected case."); + } + } + } + + void updateForHandover(int transportType) { + // restart timers that they need to be restarted on new transport type. + if (mState == STATE_SUSPECT_LOW_QUALITY) { + this.removeMessages(EVENT_PACKET_LOSS_TIMER_EXPIRED); + Message msg = this.obtainMessage( + EVENT_PACKET_LOSS_TIMER_EXPIRED, transportType, 0); + this.sendMessageDelayed(msg, (mConfigManager.getRTPMetricsData()).mPktLossTime); + } + if (this.hasMessages(EVENT_HYSTERESIS_FOR_NORMAL_QUALITY)) { + this.removeMessages(EVENT_HYSTERESIS_FOR_NORMAL_QUALITY); + this.sendEmptyMessageDelayed(EVENT_HYSTERESIS_FOR_NORMAL_QUALITY, + HYSTERESIS_TIME_NORMAL_QUALITY_MILLIS); + } + if (mState == STATE_LOW_QUALITY) { + this.removeMessages(EVENT_POLLING_CHECK_LOW_QUALITY); + this.sendEmptyMessageDelayed(EVENT_POLLING_CHECK_LOW_QUALITY, + LOW_QUALITY_CHECK_AFTER_HO_MILLIS); + } + } + + private void onPacketLossTimerExpired(int transportType) { + if (mTransportType != transportType) { + Log.d(mTag, "onPacketLossTimerExpired transport type mismatched."); + if (mState == STATE_SUSPECT_LOW_QUALITY) { + mState = STATE_NORMAL_QUALITY; + } + return; + } + if (thresholdBreached(mMediaQualityStatus) + == (1 << QnsConstants.RTP_LOW_QUALITY_REASON_PACKET_LOSS)) { + enterLowQualityState(mMediaQualityStatus); + notifyLowMediaQuality(1 << QnsConstants.RTP_LOW_QUALITY_REASON_PACKET_LOSS); + } + } + + private void notifyLowMediaQuality(int reason) { + long now = QnsUtils.getSystemElapsedRealTime(); + TransportQuality tq = getLastTransportQuality(mTransportType); + if (tq != null) { + if (reason > 0) { + tq.mLowRtpQualityReportedTime = now; + } else { + tq.mLowRtpQualityReportedTime = LOW_QUALITY_REPORTED_TIME_INITIAL_VALUE; + } + } + Log.d(mTag, "notifyLowMediaQuality reason:" + reason + " transport type:" + + AccessNetworkConstants.transportTypeToString(mTransportType)); + mLowMediaQualityListeners.notifyResult(reason); + } + } + + class TransportQuality { + int mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; + long mLowRtpQualityReportedTime = + MediaLowQualityHandler.LOW_QUALITY_REPORTED_TIME_INITIAL_VALUE; + List<CallQualityBlock> mCallQualityBlockList; + + TransportQuality(int transportType) { + mTransportType = transportType; + mCallQualityBlockList = new ArrayList<>(); + } + + boolean isLowRtpQualityReported() { + return mLowRtpQualityReportedTime + != MediaLowQualityHandler.LOW_QUALITY_REPORTED_TIME_INITIAL_VALUE; + } + + CallQualityBlock getLastCallQualityBlock() { + int length = mCallQualityBlockList.size(); + if (length > 0) { + return mCallQualityBlockList.get(length - 1); + } else { + return null; + } + } + } + + ActiveCallTracker(int slotIndex, Looper looper) { + mLogTag = ActiveCallTracker.class.getSimpleName() + "_" + slotIndex; + if (looper == null) { + mHandlerThread = new HandlerThread(ActiveCallTracker.class.getSimpleName()); + mHandlerThread.start(); + mActiveCallHandler = new ActiveCallTrackerHandler(mHandlerThread.getLooper()); + mLowQualityHandler = new MediaLowQualityHandler(mHandlerThread.getLooper()); + } else { + mActiveCallHandler = new ActiveCallTrackerHandler(looper); + mLowQualityHandler = new MediaLowQualityHandler(looper); + } + mTelephonyListener.addMediaQualityStatusCallback(mMediaQualityStatusConsumer); + mTransportQualityArray.put( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, new ArrayList<>()); + mTransportQualityArray.put( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, new ArrayList<>()); + } + + void close() { + mTelephonyListener.removeMediaQualityStatusCallback(mMediaQualityStatusConsumer); + mTelephonyListener.unregisterPreciseDataConnectionStateChanged( + mNetCapability, mActiveCallHandler); + if (mHandlerThread != null) { + mHandlerThread.quitSafely(); + } + } + + @VisibleForTesting + void onDataConnectionStatusChanged(PreciseDataConnectionState state) { + if (state == null) { + Log.d(mLogTag, "onDataConnectionStatusChanged with null info"); + return; + } + if (state.getState() == TelephonyManager.DATA_CONNECTED) { + int transportType = state.getTransportType(); + if (transportType == AccessNetworkConstants.TRANSPORT_TYPE_INVALID) { + Log.w(mLogTag, "Unexpected transport type on connected DataNetwork."); + return; + } + if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_INVALID) { + Log.d(mLogTag, "Call started with " + + AccessNetworkConstants.transportTypeToString(transportType)); + mTransportType = transportType; + startTrackingTransportQuality(transportType); + } else if (mTransportType != transportType) { + Log.d(mLogTag, "Call Handed over to " + + AccessNetworkConstants.transportTypeToString(transportType)); + mTransportType = transportType; + onHandoverCompleted(transportType); + } + } + } + + private void onHandoverCompleted( + @AccessNetworkConstants.TransportType int dstTransportType) { + long now = QnsUtils.getSystemElapsedRealTime(); + // complete to update TransportQuality for prev transport type + CallQualityBlock last = null; + int prevTransportType = QnsUtils.getOtherTransportType(dstTransportType); + TransportQuality prev = getLastTransportQuality(prevTransportType); + if (prev != null) { + last = prev.getLastCallQualityBlock(); + } + // add a new TransportQuality for new transport type + mTransportQualityArray.get(dstTransportType) + .add(new TransportQuality(dstTransportType)); + TransportQuality current = getLastTransportQuality(dstTransportType); + if (last != null) { + last.mDurationMillis = now - last.mCreatedElapsedTime; + current.mCallQualityBlockList + .add(new CallQualityBlock(last.mUpLinkLevel, last.mDownLinkLevel, now)); + } + mLowQualityHandler.updateForHandover(dstTransportType); + } + + private void startTrackingTransportQuality(int transportType) { + mTransportQualityArray.get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).clear(); + mTransportQualityArray.get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).clear(); + mTransportQualityArray.get(transportType) + .add(new TransportQuality(transportType)); + } + + void callStarted(@QnsConstants.QnsCallType int callType, int netCapability) { + if (mCallType != QnsConstants.CALL_TYPE_IDLE) { + if (mCallType != callType) { + callTypeUpdated(callType); + } else { + Log.w(mLogTag, "call type:" + callType + " already started."); + } + } + Log.d(mLogTag, "callStarted callType: " + callType + " netCapa:" + + QnsUtils.getNameOfNetCapability(netCapability)); + mCallType = callType; + mNetCapability = netCapability; + //Transport type will be updated when EVENT_DATA_CONNECTION_STATUS_CHANGED occurs. + PreciseDataConnectionState dataState = + mTelephonyListener.getLastPreciseDataConnectionState(netCapability); + if (dataState != null && dataState.getTransportType() + != AccessNetworkConstants.TRANSPORT_TYPE_INVALID) { + mTransportType = dataState.getTransportType(); + startTrackingTransportQuality(mTransportType); + } + mTelephonyListener.registerPreciseDataConnectionStateChanged(mNetCapability, + mActiveCallHandler, EVENT_DATA_CONNECTION_STATUS_CHANGED, null, true); + } + + private void callTypeUpdated(@QnsConstants.QnsCallType int callType) { + Log.d(mLogTag, "callTypeUpdated from " + mCallType + " to " + callType); + mCallType = callType; + } + + void callEnded() { + mLowQualityHandler.exitLowQualityState(); + long now = QnsUtils.getSystemElapsedRealTime(); + // complete to update TransportQuality for prev transport type + CallQualityBlock last = null; + TransportQuality prev = getLastTransportQuality(mTransportType); + if (prev != null) { + last = prev.getLastCallQualityBlock(); + } + if (last != null) { + last.mDurationMillis = now - last.mCreatedElapsedTime; + } + long upLinkQualityOverWwan = mActiveCallTracker + .getUpLinkQualityLevelDuringCall(AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + long upLinkQualityOverWlan = mActiveCallTracker + .getUpLinkQualityLevelDuringCall(AccessNetworkConstants.TRANSPORT_TYPE_WLAN); + long downLinkQualityOverWwan = mActiveCallTracker + .getDownLinkQualityLevelDuringCall(AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + long downLinkQualityOverWlan = mActiveCallTracker + .getDownLinkQualityLevelDuringCall(AccessNetworkConstants.TRANSPORT_TYPE_WLAN); + StringBuilder sb = new StringBuilder(); + sb.append("CallQuality [WWAN:"); + if (upLinkQualityOverWwan == QnsConstants.INVALID_VALUE + || downLinkQualityOverWwan == QnsConstants.INVALID_VALUE) { + sb.append("Not available] "); + } else { + sb.append("upLinkQualityOverWwan = ").append(upLinkQualityOverWwan) + .append(", downLinkQualityOverWwan = ").append(downLinkQualityOverWwan) + .append("] "); + } + sb.append("[WLAN:"); + if (upLinkQualityOverWlan == QnsConstants.INVALID_VALUE + || downLinkQualityOverWlan == QnsConstants.INVALID_VALUE) { + sb.append("Not available] "); + } else { + sb.append("upLinkQualityOverWlan = ").append(upLinkQualityOverWwan) + .append(", downLinkQualityOverWlan = ").append(downLinkQualityOverWwan) + .append("] "); + } + Log.d(mLogTag, "callEnded callType: " + mCallType + " netCapa:" + + QnsUtils.getNameOfNetCapability(mNetCapability) + " " + sb.toString()); + mCallType = QnsConstants.CALL_TYPE_IDLE; + mNetCapability = 0; + mAccessNetwork = AccessNetworkConstants.AccessNetworkType.UNKNOWN; + mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; + mTelephonyListener.unregisterPreciseDataConnectionStateChanged( + mNetCapability, mActiveCallHandler); + } + + void onMediaQualityStatusChanged(MediaQualityStatus status) { + if (status == null) { + Log.e(mLogTag, "null MediaQualityStatus received."); + return; + } + Message msg = mLowQualityHandler + .obtainMessage(MediaLowQualityHandler.EVENT_MEDIA_QUALITY_CHANGED, status); + mLowQualityHandler.sendMessage(msg); + } + + int getTransportType() { + return this.mTransportType; + } + + int getCallType() { + return this.mCallType; + } + + int getNetCapability() { + return this.mNetCapability; + } + + @VisibleForTesting + TransportQuality getLastTransportQuality(int transportType) { + if (transportType == AccessNetworkConstants.TRANSPORT_TYPE_INVALID) { + Log.w(mLogTag, "getLastTransportQuality with invalid transport type."); + return null; + } + int size = mTransportQualityArray.get(transportType).size(); + if (size > 0) { + return mTransportQualityArray.get(transportType).get(size - 1); + } else { + return null; + } + } + + @VisibleForTesting + List<TransportQuality> getTransportQualityList(int transportType) { + return mTransportQualityArray.get(transportType); + } + + long getUpLinkQualityLevelDuringCall(int transportType) { + List<TransportQuality> tqList = getTransportQualityList(transportType); + long sumUplinkQualityLevelVolume = 0; + long totalDuration = 0; + for (int i = 0; i < tqList.size(); i++) { + List<CallQualityBlock> callQualityBlockList = tqList.get(i).mCallQualityBlockList; + for (int j = 0; j < callQualityBlockList.size(); j++) { + CallQualityBlock cq = callQualityBlockList.get(j); + sumUplinkQualityLevelVolume += cq.getUpLinkQualityVolume(); + long durationMillis = cq.mDurationMillis; + if (i == tqList.size() - 1 && j == callQualityBlockList.size() - 1) { + if (durationMillis == 0) { + durationMillis = QnsUtils.getSystemElapsedRealTime() + - cq.mCreatedElapsedTime; + } + } + if (durationMillis > 0) { + totalDuration += durationMillis; + } else { + return -1; + } + } + } + if (totalDuration <= 0) { + return QnsConstants.INVALID_VALUE; + } + long qualityLevel = sumUplinkQualityLevelVolume / totalDuration; + Log.d(mLogTag, "getUplinkQualityLevel for [" + AccessNetworkConstants + .transportTypeToString(transportType) + "] totalQualityVolume: " + + sumUplinkQualityLevelVolume + ", totalDuration: " + totalDuration + + " level:" + qualityLevel); + return qualityLevel; + } + + long getDownLinkQualityLevelDuringCall(int transportType) { + List<TransportQuality> tqList = getTransportQualityList(transportType); + long sumDownLinkQualityLevelVolume = 0; + long totalDuration = 0; + for (int i = 0; i < tqList.size(); i++) { + List<CallQualityBlock> callQualityBlockList = tqList.get(i).mCallQualityBlockList; + for (int j = 0; j < callQualityBlockList.size(); j++) { + CallQualityBlock cq = callQualityBlockList.get(j); + sumDownLinkQualityLevelVolume += cq.getDownLinkQualityVolume(); + long durationMillis = cq.mDurationMillis; + if (i == tqList.size() - 1 && j == callQualityBlockList.size() - 1) { + if (durationMillis == 0) { + durationMillis = QnsUtils.getSystemElapsedRealTime() + - cq.mCreatedElapsedTime; + } + } + if (durationMillis > 0) { + totalDuration += durationMillis; + } else { + return QnsConstants.INVALID_VALUE; + } + } + } + if (totalDuration <= 0) { + return QnsConstants.INVALID_VALUE; + } + long qualityLevel = sumDownLinkQualityLevelVolume / totalDuration; + Log.d(mLogTag, "getDownLinkQualityLevel for [" + AccessNetworkConstants + .transportTypeToString(transportType) + "] totalQualityVolume: " + + sumDownLinkQualityLevelVolume + ", totalDuration: " + totalDuration + + " level:" + qualityLevel); + return qualityLevel; + } + + void updateCallQuality(CallState state) { + if (state == null) { + Log.w(mLogTag, "updateCallQuality Null CallState."); + return; + } + CallQuality cq = state.getCallQuality(); + if (cq == null || isDummyCallQuality(cq)) { + return; + } + mActiveCallHandler.post(() -> onUpdateCallQuality(cq)); + } + + private void onUpdateCallQuality(CallQuality cq) { + long now = QnsUtils.getSystemElapsedRealTime(); + CallQualityBlock prev = null; + TransportQuality transportQuality = getLastTransportQuality(mTransportType); + if (transportQuality != null) { + prev = transportQuality.getLastCallQualityBlock(); + } + if (prev != null) { + prev.mDurationMillis = now - prev.mCreatedElapsedTime; + } + transportQuality.mCallQualityBlockList.add( + new CallQualityBlock( + cq.getUplinkCallQualityLevel(), cq.getDownlinkCallQualityLevel(), now)); + } + + private boolean isDummyCallQuality(CallQuality cq) { + return (cq.getNumRtpPacketsTransmitted() == 0 + && cq.getNumRtpPacketsReceived() == 0 + && cq.getUplinkCallQualityLevel() == 0 + && cq.getDownlinkCallQualityLevel() == 0); + } + /** + * Register an event for low media quality report. + * + * @param h the Handler to get event. + * @param what the event. + * @param userObj user object. + */ + void registerLowMediaQualityListener( + Handler h, int what, Object userObj) { + Log.d(mLogTag, "registerLowMediaQualityListener"); + if (h != null) { + QnsRegistrant r = new QnsRegistrant(h, what, userObj); + mLowMediaQualityListeners.add(r); + } + } + + /** + * Unregister an event for low media quality report. + * + * @param h the handler to get event. + */ + void unregisterLowMediaQualityListener(Handler h) { + if (h != null) { + mLowMediaQualityListeners.remove(h); + } + } + + int thresholdBreached(MediaQualityStatus status) { + int breachedReason = 0; + QnsCarrierConfigManager.RtpMetricsConfig rtpConfig = mConfigManager.getRTPMetricsData(); + if (status.getRtpPacketLossRate() > 0 + && status.getRtpPacketLossRate() > rtpConfig.mPktLossRate) { + breachedReason |= 1 << QnsConstants.RTP_LOW_QUALITY_REASON_PACKET_LOSS; + } + if (status.getRtpJitterMillis() > 0 + && status.getRtpJitterMillis() > rtpConfig.mJitter) { + breachedReason |= 1 << QnsConstants.RTP_LOW_QUALITY_REASON_JITTER; + } + if (status.getRtpInactivityMillis() > 0 + && status.getRtpInactivityMillis() > rtpConfig.mNoRtpInterval) { + breachedReason |= 1 << QnsConstants.RTP_LOW_QUALITY_REASON_NO_RTP; + } + return breachedReason; + } + + boolean worseThanBefore(MediaQualityStatus before, MediaQualityStatus now) { + return thresholdBreached(now) > thresholdBreached(before); + } + } + + QnsCallStatusTracker(QnsTelephonyListener telephonyListener, + QnsCarrierConfigManager configManager, int slotIndex) { + this(telephonyListener, configManager, slotIndex, null); + } + + /** Only for test */ + @VisibleForTesting + QnsCallStatusTracker(QnsTelephonyListener telephonyListener, + QnsCarrierConfigManager configManager, int slotIndex, Looper looper) { mLogTag = QnsCallStatusTracker.class.getSimpleName() + "_" + slotIndex; mTelephonyListener = telephonyListener; + mConfigManager = configManager; + mActiveCallTracker = new ActiveCallTracker(slotIndex, looper); mTelephonyListener.addCallStatesChangedCallback(mCallStatesConsumer); mTelephonyListener.addSrvccStateChangedCallback(mSrvccStateConsumer); } @@ -58,6 +742,9 @@ public class QnsCallStatusTracker { void close() { mTelephonyListener.removeCallStatesChangedCallback(mCallStatesConsumer); mTelephonyListener.removeSrvccStateChangedCallback(mSrvccStateConsumer); + if (mActiveCallTracker != null) { + mActiveCallTracker.close(); + } } void updateCallState(List<CallState> callStateList) { @@ -82,7 +769,7 @@ public class QnsCallStatusTracker { if (mLastNormalCallType != QnsConstants.CALL_TYPE_IDLE) { mLastNormalCallType = QnsConstants.CALL_TYPE_IDLE; if (mCallTypeChangedEventListener != null) { - mCallTypeChangedEventListener.notifyResult(mLastNormalCallType); + notifyCallType(NetworkCapabilities.NET_CAPABILITY_IMS, mLastNormalCallType); } } if (mLastEmergencyCallType != QnsConstants.CALL_TYPE_IDLE) { @@ -90,11 +777,13 @@ public class QnsCallStatusTracker { if (mEmergencyOverIms) { mEmergencyOverIms = false; if (mCallTypeChangedEventListener != null) { - mCallTypeChangedEventListener.notifyResult(mLastNormalCallType); + notifyCallType( + NetworkCapabilities.NET_CAPABILITY_IMS, mLastEmergencyCallType); } } else { if (mEmergencyCallTypeChangedEventListener != null) { - mEmergencyCallTypeChangedEventListener.notifyResult(mLastEmergencyCallType); + notifyCallType( + NetworkCapabilities.NET_CAPABILITY_EIMS, mLastEmergencyCallType); } } } @@ -104,7 +793,7 @@ public class QnsCallStatusTracker { && !hasVideoCall() && !hasVoiceCall()) { mLastNormalCallType = QnsConstants.CALL_TYPE_IDLE; if (mCallTypeChangedEventListener != null) { - mCallTypeChangedEventListener.notifyResult(mLastNormalCallType); + notifyCallType(NetworkCapabilities.NET_CAPABILITY_IMS, mLastNormalCallType); } } if (mLastEmergencyCallType != QnsConstants.CALL_TYPE_IDLE && !hasEmergencyCall()) { @@ -112,11 +801,13 @@ public class QnsCallStatusTracker { if (mEmergencyOverIms) { mEmergencyOverIms = false; if (mCallTypeChangedEventListener != null) { - mCallTypeChangedEventListener.notifyResult(mLastEmergencyCallType); + notifyCallType( + NetworkCapabilities.NET_CAPABILITY_IMS, mLastEmergencyCallType); } } else { if (mEmergencyCallTypeChangedEventListener != null) { - mEmergencyCallTypeChangedEventListener.notifyResult(mLastEmergencyCallType); + notifyCallType( + NetworkCapabilities.NET_CAPABILITY_EIMS, mLastEmergencyCallType); } } } @@ -126,25 +817,44 @@ public class QnsCallStatusTracker { if (!isDataNetworkConnected(NetworkCapabilities.NET_CAPABILITY_EIMS) && isDataNetworkConnected(NetworkCapabilities.NET_CAPABILITY_IMS)) { if (mCallTypeChangedEventListener != null) { - mCallTypeChangedEventListener.notifyResult(mLastEmergencyCallType); + notifyCallType( + NetworkCapabilities.NET_CAPABILITY_IMS, mLastEmergencyCallType); mEmergencyOverIms = true; } } else { if (mEmergencyCallTypeChangedEventListener != null) { - mEmergencyCallTypeChangedEventListener.notifyResult(mLastEmergencyCallType); + notifyCallType( + NetworkCapabilities.NET_CAPABILITY_EIMS, mLastEmergencyCallType); } } } else if (hasVideoCall()) { if (mLastNormalCallType != QnsConstants.CALL_TYPE_VIDEO) { mLastNormalCallType = QnsConstants.CALL_TYPE_VIDEO; - mCallTypeChangedEventListener.notifyResult(mLastNormalCallType); + notifyCallType(NetworkCapabilities.NET_CAPABILITY_IMS, mLastNormalCallType); } } else if (hasVoiceCall()) { if (mLastNormalCallType != QnsConstants.CALL_TYPE_VOICE) { mLastNormalCallType = QnsConstants.CALL_TYPE_VOICE; - mCallTypeChangedEventListener.notifyResult(mLastNormalCallType); + notifyCallType(NetworkCapabilities.NET_CAPABILITY_IMS, mLastNormalCallType); } } + if (mActiveCallTracker.getCallType() != QnsConstants.CALL_TYPE_IDLE) { + mActiveCallTracker.updateCallQuality(getActiveCall()); + } + } + } + + private void notifyCallType(int netCapability, int callType) { + Log.d(mLogTag, "notifyCallType:" + netCapability + ", callType:" + callType); + if (netCapability == NetworkCapabilities.NET_CAPABILITY_IMS) { + mCallTypeChangedEventListener.notifyResult(callType); + } else if (netCapability == NetworkCapabilities.NET_CAPABILITY_EIMS) { + mEmergencyCallTypeChangedEventListener.notifyResult(callType); + } + if (callType == QnsConstants.CALL_TYPE_IDLE) { + mActiveCallTracker.callEnded(); + } else { + mActiveCallTracker.callStarted(callType, netCapability); } } @@ -161,6 +871,15 @@ public class QnsCallStatusTracker { return false; } + CallState getActiveCall() { + for (CallState cs : mCallStates) { + if (cs.getCallState() == PreciseCallState.PRECISE_CALL_STATE_ACTIVE) { + return cs; + } + } + return null; + } + boolean hasVideoCall() { for (CallState cs : mCallStates) { if (cs.getImsCallServiceType() == ImsCallProfile.SERVICE_TYPE_NORMAL @@ -234,6 +953,10 @@ public class QnsCallStatusTracker { } } + ActiveCallTracker getActiveCallTracker() { + return mActiveCallTracker; + } + @VisibleForTesting void onSrvccStateChangedInternal(int srvccState) { if (srvccState == TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED) { @@ -260,6 +983,7 @@ public class QnsCallStatusTracker { } } + private boolean isDataNetworkConnected(int netCapability) { PreciseDataConnectionState preciseDataStatus = mTelephonyListener.getLastPreciseDataConnectionState(netCapability); diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsCarrierConfigManager.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsCarrierConfigManager.java index b0d1cf6..318e031 100644 --- a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsCarrierConfigManager.java +++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsCarrierConfigManager.java @@ -495,28 +495,13 @@ class QnsCarrierConfigManager { "qns.minimum_handover_guarding_timer_ms_int"; /** - * List of 4 items indicating the RTP media metrics criteria to be set ,to make HO decision - * during Call. The three items part of integer array is jitter , Packet loss rate & RTP - * interval. + * This indicates time duration for packet loss rate sustained. * - * <p>{@link QnsConstants}. The values are set as below: - * - * <ul> - * <li>0: {@link QnsConstants#KEY_DEFAULT_JITTER} - * <li>1: {@link QnsConstants#KEY_DEFAULT_PACKET_LOSS_RATE} - * <li>2: {@link QnsConstants#KEY_DEFAULT_PACKET_LOSS_TIME_MILLIS} - * <li>3: {@link QnsConstants#KEY_DEFAULT_NO_RTP_INTERVAL_MILLIS} - * </ul> - * - * {@code QnsConstants#KEY_DEFAULT_JITTER , QnsConstants#KEY_DEFAULT_PACKET_LOSS_RATE , - * QnsConstants#KEY_DEFAULT_NO_RTP_INTERVAL }: If set , indicates the jitter : Indicating the - * latentcy , Packet loss rate : Indicating the percentage loss & RTP interval indicating the - * interval in secs criteria to determine the HO decision . The default value for this key is - * {@code QnsConstants#KEY_DEFAULT_JITTER, QnsConstants#KEY_DEFAULT_PACKET_LOSS_RATE, - * QnsConstants#KEY_DEFAULT_PACKET_LOSS_TIME_MILLIS, - * QnsConstants#KEY_DEFAULT_NO_RTP_INTERVAL_MILLIS} + * <p/> The default value for this key is {@code + * QnsConstants#KEY_DEFAULT_PACKET_LOSS_TIME_MILLIS} */ - static final String KEY_QNS_RTP_METRICS_INT_ARRAY = "qns.rtp_metrics_int_array"; + static final String KEY_QNS_MEDIA_THRESHOLD_RTP_PACKET_LOSS_TIME_MILLIS_INT = + "qns.media_threshold_rtp_packet_loss_time_millis"; /** * Specify a list of the waiting time(millisecond) for the preferred transport type when power @@ -752,7 +737,7 @@ class QnsCarrierConfigManager { private int[] mWlanHysteresisTimer; private int[] mNonImsWwanHysteresisTimer; private int[] mNonImsWlanHysteresisTimer; - private int[] mRTPMetricsData; + private int[] mRTPMetricsData = new int[4]; private int[] mWaitingTimerForPreferredTransport; private int[] mAllowMaxIwlanHoCountOnReason; private int[] mHoRestrictTimeOnRtpQuality; @@ -1173,6 +1158,8 @@ class QnsCarrierConfigManager { loadDirectFromCarrierConfigManagerKey(carrierConfigBundle); loadWfcConfigurations(carrierConfigBundle, assetConfigBundle); + + loadMediaThreshold(carrierConfigBundle, assetConfigBundle); } /** @@ -1364,7 +1351,6 @@ class QnsCarrierConfigManager { bundleCarrier, bundleAsset, KEY_QNS_HO_RESTRICT_TIME_WITH_LOW_RTP_QUALITY_MILLIS_INT_ARRAY); - mRTPMetricsData = getConfig(bundleCarrier, bundleAsset, KEY_QNS_RTP_METRICS_INT_ARRAY); mWlanRttBackhaulCheckConfigsOnPing = getConfig( bundleCarrier, @@ -1526,6 +1512,26 @@ class QnsCarrierConfigManager { } } + void loadMediaThreshold(PersistableBundle bundleCarrier, PersistableBundle assetConfigBundle) { + //read Jitter + mRTPMetricsData[0] = getConfig( + bundleCarrier, null, + CarrierConfigManager.ImsVoice.KEY_VOICE_RTP_JITTER_THRESHOLD_MILLIS_INT); + //read Packet Loss Rate + mRTPMetricsData[1] = getConfig( + bundleCarrier, null, + CarrierConfigManager.ImsVoice.KEY_VOICE_RTP_PACKET_LOSS_RATE_THRESHOLD_INT); + //read Inactivity Time + long inactivityTime = getConfig( + bundleCarrier, null, + CarrierConfigManager.ImsVoice.KEY_VOICE_RTP_INACTIVITY_TIME_THRESHOLD_MILLIS_LONG); + mRTPMetricsData[3] = (int) inactivityTime; + //read Packet Loss Duration + mRTPMetricsData[2] = getConfig( + bundleCarrier, assetConfigBundle, + KEY_QNS_MEDIA_THRESHOLD_RTP_PACKET_LOSS_TIME_MILLIS_INT); + } + /** Updated handover rules from carrier config. */ @VisibleForTesting List<HandoverRule> updateHandoverRules( @@ -2683,10 +2689,10 @@ class QnsCarrierConfigManager { /** RTP packet loss rate in percentage */ final int mPktLossRate; - /** Time interval of RTP packet loss rate */ + /** Time interval(milliseconds) of RTP packet loss rate */ final int mPktLossTime; - /** No RTP interval in seconds */ + /** No RTP interval in milliseconds */ final int mNoRtpInterval; RtpMetricsConfig(int jitter, int pktLossRate, int pktLossTime, int noRtpInterval) { diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsComponents.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsComponents.java index ad9a2e0..4d33f5a 100644 --- a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsComponents.java +++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsComponents.java @@ -77,10 +77,6 @@ class QnsComponents { mCellularQualityMonitors.put( slotId, new CellularQualityMonitor(mContext, mQnsTelephonyListeners.get(slotId), slotId)); - mQnsCallStatusTracker.put( - slotId, - new QnsCallStatusTracker(mQnsTelephonyListeners.get(slotId), slotId)); - mQnsProvisioningListeners.put( slotId, new QnsProvisioningListener(mContext, mQnsImsManagers.get(slotId), slotId)); mQnsEventDispatchers.put( @@ -93,6 +89,10 @@ class QnsComponents { mQnsCarrierConfigManagers.put( slotId, new QnsCarrierConfigManager(mContext, mQnsEventDispatchers.get(slotId), slotId)); + mQnsCallStatusTracker.put( + slotId, + new QnsCallStatusTracker(mQnsTelephonyListeners.get(slotId), + mQnsCarrierConfigManagers.get(slotId), slotId)); mWifiBackhaulMonitors.put( slotId, new WifiBackhaulMonitor( @@ -238,6 +238,11 @@ class QnsComponents { mWifiBackhaulMonitors.remove(slotId); wifiBackhaulMonitor.close(); } + QnsCallStatusTracker qnsCallStatusTracker = mQnsCallStatusTracker.get(slotId); + if (qnsCallStatusTracker != null) { + mQnsCallStatusTracker.remove(slotId); + qnsCallStatusTracker.close(); + } QnsCarrierConfigManager qnsCarrierConfigManager = mQnsCarrierConfigManagers.get(slotId); if (qnsCarrierConfigManager != null) { mQnsCarrierConfigManagers.remove(slotId); @@ -253,11 +258,6 @@ class QnsComponents { mQnsProvisioningListeners.remove(slotId); qnsProvisioningListener.close(); } - QnsCallStatusTracker qnsCallStatusTracker = mQnsCallStatusTracker.get(slotId); - if (qnsCallStatusTracker != null) { - mQnsCallStatusTracker.remove(slotId); - qnsCallStatusTracker.close(); - } CellularQualityMonitor cellularQualityMonitor = mCellularQualityMonitors.get(slotId); if (cellularQualityMonitor != null) { mCellularQualityMonitors.remove(slotId); diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsConstants.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsConstants.java index 065aaee..ba2c2f1 100644 --- a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsConstants.java +++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsConstants.java @@ -29,6 +29,7 @@ class QnsConstants { static final String QNS_TAG = "QNS"; + static final int INVALID_VALUE = -1; static final int INVALID_ID = -1; static final int INVALID_SUB_ID = -1; static final int KEY_DEFAULT_VALUE = 0; @@ -37,10 +38,7 @@ class QnsConstants { static final int CONFIG_DEFAULT_MIN_HANDOVER_GUARDING_TIMER = 0; static final int CONFIG_DEFAULT_MIN_HANDOVER_GUARDING_TIMER_LIMIT = 5000; - static final int KEY_DEFAULT_JITTER = 120; - static final int KEY_DEFAULT_PACKET_LOSS_RATE = 40; static final int KEY_DEFAULT_PACKET_LOSS_TIME_MILLIS = 5000; - static final int KEY_DEFAULT_NO_RTP_INTERVAL_MILLIS = 2000; static final int KEY_DEFAULT_IWLAN_AVOID_TIME_LOW_RTP_QUALITY_MILLIS = 60000; static final int KEY_DEFAULT_THRESHOLD_SSRSRP_GOOD = -99; @@ -77,6 +75,8 @@ class QnsConstants { @IntDef(value = {COVERAGE_HOME, COVERAGE_ROAM, COVERAGE_BOTH}) @interface CellularCoverage {} + // These(RTP_LOW_QUALITY_) constants are used to @code worseThanBefore(), + // be caution before change. static final int RTP_LOW_QUALITY_REASON_JITTER = 1; static final int RTP_LOW_QUALITY_REASON_PACKET_LOSS = 2; static final int RTP_LOW_QUALITY_REASON_NO_RTP = 3; diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsTelephonyListener.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsTelephonyListener.java index e890cf1..0945be6 100644 --- a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsTelephonyListener.java +++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsTelephonyListener.java @@ -37,6 +37,7 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.telephony.VopsSupportInfo; +import android.telephony.ims.MediaQualityStatus; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -69,6 +70,7 @@ class QnsTelephonyListener { QnsRegistrantList mIwlanServiceStateListener = new QnsRegistrantList(); List<Consumer<List<CallState>>> mCallStatesConsumerList = new ArrayList<>(); List<Consumer<Integer>> mSrvccStateConsumerList = new ArrayList<>(); + List<Consumer<MediaQualityStatus>> mMediaQualityConsumerList = new ArrayList<>(); protected HashMap<Integer, QnsRegistrantList> mQnsTelephonyInfoRegistrantMap = new HashMap<>(); protected HashMap<Integer, QnsRegistrantList> mNetCapabilityRegistrantMap = new HashMap<>(); protected QnsTelephonyInfo mLastQnsTelephonyInfo = new QnsTelephonyInfo(); @@ -1025,7 +1027,8 @@ class QnsTelephonyListener { TelephonyCallback.BarringInfoListener, TelephonyCallback.CallStateListener, TelephonyCallback.SrvccStateListener, - TelephonyCallback.CallAttributesListener { + TelephonyCallback.CallAttributesListener, + TelephonyCallback.MediaQualityStatusChangedListener { private final Executor mExecutor; private OnServiceStateListener mServiceStateListener; private OnPreciseDataConnectionStateListener mPreciseDataConnectionStateListener; @@ -1149,6 +1152,13 @@ class QnsTelephonyListener { mCallStatesCallback.onCallStatesChanged(callStateList); } } + + @Override + public void onMediaQualityStatusChanged(MediaQualityStatus status) { + for (Consumer<MediaQualityStatus> consumer : mMediaQualityConsumerList) { + consumer.accept(status); + } + } } void addCallStatesChangedCallback(Consumer<List<CallState>> consumer) { @@ -1167,6 +1177,14 @@ class QnsTelephonyListener { mSrvccStateConsumerList.remove(consumer); } + void addMediaQualityStatusCallback(Consumer<MediaQualityStatus> consumer) { + mMediaQualityConsumerList.add(consumer); + } + + void removeMediaQualityStatusCallback(Consumer<MediaQualityStatus> consumer) { + mMediaQualityConsumerList.remove(consumer); + } + /** * Dumps the state of {@link QualityMonitor} * diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsUtils.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsUtils.java index 5535bfa..c9745e4 100644 --- a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsUtils.java +++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsUtils.java @@ -264,6 +264,17 @@ class QnsUtils { return false; } + @AccessNetworkConstants.TransportType + static int getTransportTypeFromAccessNetwork( + @AccessNetworkConstants.RadioAccessNetworkType int accessNetwork) { + if (accessNetwork == AccessNetworkConstants.AccessNetworkType.IWLAN) { + return AccessNetworkConstants.TRANSPORT_TYPE_WLAN; + } else if (accessNetwork != AccessNetworkConstants.AccessNetworkType.UNKNOWN) { + return AccessNetworkConstants.TRANSPORT_TYPE_WWAN; + } + return AccessNetworkConstants.TRANSPORT_TYPE_INVALID; + } + /** * Get Set of network capabilities from string joined by {@code |}, space is ignored. If input * string contains unknown capability or malformatted(e.g. empty string), -1 is included in the @@ -285,6 +296,21 @@ class QnsUtils { } /** + * Returns another transport type. + * @param transportType transport type + * @return another transport type of input parameter + */ + @AccessNetworkConstants.TransportType + static int getOtherTransportType(@AccessNetworkConstants.TransportType int transportType) { + if (transportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { + return AccessNetworkConstants.TRANSPORT_TYPE_WWAN; + } else if (transportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { + return AccessNetworkConstants.TRANSPORT_TYPE_WLAN; + } + return AccessNetworkConstants.TRANSPORT_TYPE_INVALID; + } + + /** * Convert network capabilities to string. * * <p>This is for debugging and logging purposes only. @@ -608,6 +634,13 @@ class QnsUtils { return (T) Integer.valueOf(QnsConstants.RAT_PREFERENCE_DEFAULT); case KEY_QNS_VOWIFI_REGISTATION_TIMER_FOR_VOWIFI_ACTIVATION_INT: return (T) Integer.valueOf(CONFIG_DEFAULT_VOWIFI_REGISTATION_TIMER); + case CarrierConfigManager.ImsVoice.KEY_VOICE_RTP_JITTER_THRESHOLD_MILLIS_INT: + case CarrierConfigManager.ImsVoice.KEY_VOICE_RTP_PACKET_LOSS_RATE_THRESHOLD_INT: + return (T) Integer.valueOf(QnsConstants.INVALID_VALUE); + case QnsCarrierConfigManager.KEY_QNS_MEDIA_THRESHOLD_RTP_PACKET_LOSS_TIME_MILLIS_INT: + return (T) Integer.valueOf(QnsConstants.KEY_DEFAULT_PACKET_LOSS_TIME_MILLIS); + case CarrierConfigManager.ImsVoice.KEY_VOICE_RTP_INACTIVITY_TIME_THRESHOLD_MILLIS_LONG: + return (T) Long.valueOf(QnsConstants.INVALID_VALUE); case QnsCarrierConfigManager .KEY_QNS_IN_CALL_ROVEIN_ALLOWED_COUNT_AND_FALLBACK_REASON_INT_ARRAY: return (T) @@ -639,14 +672,6 @@ class QnsUtils { }; case QnsCarrierConfigManager.KEY_MINIMUM_HANDOVER_GUARDING_TIMER_MS_INT: return (T) Integer.valueOf(QnsConstants.CONFIG_DEFAULT_MIN_HANDOVER_GUARDING_TIMER); - case QnsCarrierConfigManager.KEY_QNS_RTP_METRICS_INT_ARRAY: - return (T) - new int[] { - QnsConstants.KEY_DEFAULT_JITTER, - QnsConstants.KEY_DEFAULT_PACKET_LOSS_RATE, - QnsConstants.KEY_DEFAULT_PACKET_LOSS_TIME_MILLIS, - QnsConstants.KEY_DEFAULT_NO_RTP_INTERVAL_MILLIS - }; case QnsCarrierConfigManager .KEY_CHOOSE_WFC_PREFERRED_TRANSPORT_IN_BOTH_BAD_CONDITION_INT_ARRAY: return (T) new int[] {}; diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/RestrictManager.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/RestrictManager.java index c49f674..0837f00 100644 --- a/services/QualifiedNetworksService/src/com/android/telephony/qns/RestrictManager.java +++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/RestrictManager.java @@ -96,9 +96,8 @@ class RestrictManager { private static final int EVENT_IMS_REGISTRATION_STATE_CHANGED = 3004; private static final int EVENT_LOW_RTP_QUALITY_REPORTED = 3006; private static final int EVENT_RELEASE_RESTRICTION = 3008; - private static final int EVENT_REGISTER_LOW_RTP_QUALITY = 3009; - protected static final int EVENT_INITIAL_DATA_CONNECTION_FAIL_RETRY_TIMER_EXPIRED = 3010; - private static final int EVENT_WIFI_RTT_BACKHAUL_CHECK_STATUS = 3011; + protected static final int EVENT_INITIAL_DATA_CONNECTION_FAIL_RETRY_TIMER_EXPIRED = 3009; + private static final int EVENT_WIFI_RTT_BACKHAUL_CHECK_STATUS = 3010; @VisibleForTesting static final int GUARDING_TIMER_HANDOVER_INIT = 30000; @@ -159,7 +158,8 @@ class RestrictManager { @VisibleForTesting QnsRegistrant mRestrictInfoRegistrant; private DataConnectionStatusTracker mDataConnectionStatusTracker; private CellularNetworkStatusTracker mCellularNetworkStatusTracker; - private AlternativeEventListener mAltEventListener; + private QnsCallStatusTracker mQnsCallStatusTracker; + private QnsCallStatusTracker.ActiveCallTracker mActiveCallTracker; private QnsImsManager mQnsImsManager; private WifiBackhaulMonitor mWifiBackhaulMonitor; private int mNetCapability; @@ -215,7 +215,7 @@ class RestrictManager { ar = (QnsAsyncResult) message.obj; int reason = (int) ar.mResult; Log.d(mLogTag, "EVENT_LOW_RTP_QUALITY_REPORTED reason: " + reason); - onLowRtpQualityEvent(); + onLowRtpQualityEvent(reason); break; case EVENT_IMS_REGISTRATION_STATE_CHANGED: @@ -241,13 +241,6 @@ class RestrictManager { } break; - case EVENT_REGISTER_LOW_RTP_QUALITY: - int currTransportType = message.arg1; - if (currTransportType == mTransportType) { - registerLowRtpQualityEvent(); - } - break; - case EVENT_INITIAL_DATA_CONNECTION_FAIL_RETRY_TIMER_EXPIRED: Log.d( mLogTag, @@ -255,7 +248,7 @@ class RestrictManager { + mIsTimerRunningOnDataConnectionFail); if (mIsTimerRunningOnDataConnectionFail) { - currTransportType = message.arg1; + int currTransportType = message.arg1; fallbackToOtherTransportOnDataConnectionFail(currTransportType); } break; @@ -316,6 +309,17 @@ class RestrictManager { } } + class LowRtpQualityRestriction extends Restriction{ + private int mReason; + LowRtpQualityRestriction(int type, int[] releaseEvents, int restrictTime, int reason) { + super(type, releaseEvents, restrictTime); + mReason = reason; + } + int getReason() { + return mReason; + } + } + class Restriction { private final int mRestrictType; final ArrayList<Integer> mReleaseEventList; @@ -444,7 +448,8 @@ class RestrictManager { mHandler = new RestrictManagerHandler(loop); mNetCapability = netCapability; mDataConnectionStatusTracker = dcst; - mAltEventListener = qnsComponents.getAlternativeEventListener(mSlotId); + mQnsCallStatusTracker = qnsComponents.getQnsCallStatusTracker(mSlotId); + mActiveCallTracker = qnsComponents.getQnsCallStatusTracker(mSlotId).getActiveCallTracker(); mDataConnectionStatusTracker.registerDataConnectionStatusChanged( mHandler, EVENT_DATA_CONNECTION_CHANGED); if (mNetCapability == NetworkCapabilities.NET_CAPABILITY_IMS) { @@ -500,7 +505,9 @@ class RestrictManager { mQnsEventDispatcher.unregisterEvent(mHandler); if (mNetCapability == NetworkCapabilities.NET_CAPABILITY_IMS || mNetCapability == NetworkCapabilities.NET_CAPABILITY_EIMS) { - mAltEventListener.unregisterLowRtpQualityEvent(mNetCapability, mHandler); + if (mActiveCallTracker != null) { + mActiveCallTracker.unregisterLowMediaQualityListener(mHandler); + } } if (mNetCapability == NetworkCapabilities.NET_CAPABILITY_IMS) { mQnsImsManager.unregisterImsRegistrationStatusChanged(mHandler); @@ -539,7 +546,7 @@ class RestrictManager { mQnsCarrierConfigManager.getWaitingTimerForPreferredTransportOnPowerOn( transportType); if (waitingTimer != QnsConstants.KEY_DEFAULT_VALUE) { - int preventTransportType = getOtherTransport(transportType); + int preventTransportType = QnsUtils.getOtherTransportType(transportType); Log.d( mLogTag, "prevent " @@ -628,25 +635,53 @@ class RestrictManager { } @VisibleForTesting - void onLowRtpQualityEvent() { + void onLowRtpQualityEvent(@QnsConstants.RtpLowQualityReason int reason) { int lowRtpQualityRestrictTime = mQnsCarrierConfigManager.getHoRestrictedTimeOnLowRTPQuality(mTransportType); if ((mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN || mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) && lowRtpQualityRestrictTime > 0 - && mImsCallType != QnsConstants.CALL_TYPE_IDLE) { - addRestriction( - mTransportType, - RESTRICT_TYPE_RTP_LOW_QUALITY, - sReleaseEventMap.get(RESTRICT_TYPE_RTP_LOW_QUALITY), - lowRtpQualityRestrictTime); - unregisterLowRtpQualityEvent(); - - if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { - int fallbackReason = mQnsCarrierConfigManager.getQnsIwlanHoRestrictReason(); - if (fallbackReason == QnsConstants.FALLBACK_REASON_RTP_OR_WIFI - || fallbackReason == QnsConstants.FALLBACK_REASON_RTP_ONLY) { - increaseCounterToRestrictIwlanInCall(); + && (mImsCallType == QnsConstants.CALL_TYPE_VOICE + || mImsCallType == QnsConstants.CALL_TYPE_EMERGENCY)) { + if (reason > 0) { + Restriction restriction = + new LowRtpQualityRestriction(RESTRICT_TYPE_RTP_LOW_QUALITY, + sReleaseEventMap.get(RESTRICT_TYPE_RTP_LOW_QUALITY), + lowRtpQualityRestrictTime, + reason); + // If current report has 'no RTP reason' and previous report at previous + // transport type doesn't have 'no RTP reason', let's move back to previous + // transport type. + if ((reason & 1 << QnsConstants.RTP_LOW_QUALITY_REASON_NO_RTP) != 0) { + HashMap<Integer, Restriction> restrictionMap = mRestrictInfos + .get(QnsUtils.getOtherTransportType(mTransportType)) + .getRestrictionMap(); + Restriction restrictionOtherSide = restrictionMap.get( + RESTRICT_TYPE_RTP_LOW_QUALITY); + if (restrictionOtherSide != null + && restrictionOtherSide instanceof LowRtpQualityRestriction) { + int reasonOtherSide = + ((LowRtpQualityRestriction) restrictionOtherSide).getReason(); + if ((reasonOtherSide & 1 << QnsConstants.RTP_LOW_QUALITY_REASON_NO_RTP) + == 0) { + releaseRestriction(QnsUtils.getOtherTransportType(mTransportType), + RESTRICT_TYPE_RTP_LOW_QUALITY, true); + } + } + } + // If both transport have low RTP quality restriction, let ANE do final decision. + addRestriction(mTransportType, restriction, lowRtpQualityRestrictTime); + + if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { + int fallbackReason = mQnsCarrierConfigManager.getQnsIwlanHoRestrictReason(); + if (fallbackReason == QnsConstants.FALLBACK_REASON_RTP_OR_WIFI + || fallbackReason == QnsConstants.FALLBACK_REASON_RTP_ONLY) { + increaseCounterToRestrictIwlanInCall(); + } + } + } else { + if (hasRestrictionType(mTransportType, RESTRICT_TYPE_RTP_LOW_QUALITY)) { + releaseRestriction(mTransportType, RESTRICT_TYPE_RTP_LOW_QUALITY); } } } @@ -695,7 +730,7 @@ class RestrictManager { checkToCancelInitialPdnConnectionFailFallback(); clearInitialPdnConnectionFailFallbackRestriction(); - checkIfCancelNonPreferredRestriction(getOtherTransport(transportType)); + checkIfCancelNonPreferredRestriction(QnsUtils.getOtherTransportType(transportType)); if (mNetCapability == NetworkCapabilities.NET_CAPABILITY_IMS) { if (mLastEvaluatedTransportType == AccessNetworkConstants.TRANSPORT_TYPE_INVALID || transportType == mLastEvaluatedTransportType) { @@ -794,12 +829,6 @@ class RestrictManager { // Return to the transport type restricted by low RTP. It may be singleRAT case, release // the restriction. releaseRestriction(mTransportType, RESTRICT_TYPE_RTP_LOW_QUALITY); - Log.d(mLogTag, "Unregister & Register Low RTP quality for " + mTransportType); - unregisterLowRtpQualityEvent(); - - Message msg = - mHandler.obtainMessage(EVENT_REGISTER_LOW_RTP_QUALITY, mTransportType, 0, null); - mHandler.sendMessageDelayed(msg, (long) QnsConstants.DEFAULT_MSG_DELAY_TIMER); } } @@ -808,7 +837,7 @@ class RestrictManager { } private void processHandoverGuardingOperation(int transportType) { - int guardingTransport = getOtherTransport(transportType); + int guardingTransport = QnsUtils.getOtherTransportType(transportType); int delayMillis = getGuardingTimeMillis(guardingTransport, mImsCallType); int minimumGuardingTimer = mQnsCarrierConfigManager.getMinimumHandoverGuardingTimer(); if (delayMillis == 0 && minimumGuardingTimer > 0) { @@ -1031,7 +1060,7 @@ class RestrictManager { int transportType, int restrictType, int fallbackTimeMillis) { Log.d(mLogTag, "release ignorable restrictions on WWAN to fallback."); for (int restriction : ignorableRestrictionsOnSingleRat) { - releaseRestriction(getOtherTransport(transportType), restriction, false); + releaseRestriction(QnsUtils.getOtherTransportType(transportType), restriction, false); } addRestriction( transportType, @@ -1050,7 +1079,8 @@ class RestrictManager { } mLastEvaluatedTransportType = transportType; if (mDataConnectionStatusTracker.isActiveState() && mTransportType != transportType) { - startGuarding(GUARDING_TIMER_HANDOVER_INIT, getOtherTransport(transportType)); + startGuarding(GUARDING_TIMER_HANDOVER_INIT, + QnsUtils.getOtherTransportType(transportType)); } } @@ -1082,7 +1112,7 @@ class RestrictManager { } private void updateGuardingTimerConditionOnCallState(int prevCallType, int newCallType) { - int currGuardingTransport = getOtherTransport(mTransportType); + int currGuardingTransport = QnsUtils.getOtherTransportType(mTransportType); if (mRestrictInfos.get(currGuardingTransport) == null) return; HashMap<Integer, Restriction> restrictionMap = @@ -1141,6 +1171,49 @@ class RestrictManager { } } + void addRestriction(int transport, Restriction restrictObj, int timeMillis) { + boolean needNotify = false; + HashMap<Integer, Restriction> restrictionMap = + mRestrictInfos.get(transport).getRestrictionMap(); + Restriction restriction = restrictionMap.get(restrictObj.mRestrictType); + Log.d( + mLogTag, + "addRestriction[" + + QnsConstants.transportTypeToString(transport) + + "] " + + restrictTypeToString(restrictObj.mRestrictType) + + " was restrict:" + + (restriction != null)); + if (restriction == null) { + restriction = restrictObj; + restrictionMap.put(restrictObj.mRestrictType, restriction); + Log.d( + mLogTag, + "addRestriction[" + + QnsConstants.transportTypeToString(transport) + + "] " + + restriction); + needNotify = true; + } else { + if (timeMillis > 0) { + restriction.updateRestrictTime(timeMillis); + removeReleaseRestrictionMessage(restriction); + } + Log.d( + mLogTag, + "updateRestriction[" + + QnsConstants.transportTypeToString(transport) + + "] " + + restriction); + } + if (timeMillis > 0) { + sendReleaseRestrictionMessage(transport, restriction); + } + if (needNotify) { + notifyRestrictInfoChanged(); + } + } + void addRestriction(int transport, int type, int[] releaseEvents, int timeMillis) { boolean needNotify = false; HashMap<Integer, Restriction> restrictionMap = @@ -1261,14 +1334,6 @@ class RestrictManager { mHandler.removeMessages(EVENT_RELEASE_RESTRICTION, restriction); } - protected int getOtherTransport(int transportType) { - if (transportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { - return AccessNetworkConstants.TRANSPORT_TYPE_WWAN; - } else { - return AccessNetworkConstants.TRANSPORT_TYPE_WLAN; - } - } - void registerRestrictInfoChanged(Handler h, int what) { mRestrictInfoRegistrant = new QnsRegistrant(h, what, null); } @@ -1375,17 +1440,13 @@ class RestrictManager { || mImsCallType == QnsConstants.CALL_TYPE_EMERGENCY) && (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN || mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) - && mAltEventListener != null) { + && mActiveCallTracker != null) { int hoRestrictTimeOnLowRtpQuality = mQnsCarrierConfigManager.getHoRestrictedTimeOnLowRTPQuality(mTransportType); if (hoRestrictTimeOnLowRtpQuality > 0) { Log.d(mLogTag, "registerLowRtpQualityEvent"); - mAltEventListener.registerLowRtpQualityEvent( - mNetCapability, - mHandler, - EVENT_LOW_RTP_QUALITY_REPORTED, - null, - mQnsCarrierConfigManager.getRTPMetricsData()); + mActiveCallTracker.registerLowMediaQualityListener( + mHandler, EVENT_LOW_RTP_QUALITY_REPORTED, null); } } } @@ -1393,7 +1454,9 @@ class RestrictManager { private void unregisterLowRtpQualityEvent() { if (mNetCapability == NetworkCapabilities.NET_CAPABILITY_IMS || mNetCapability == NetworkCapabilities.NET_CAPABILITY_EIMS) { - mAltEventListener.unregisterLowRtpQualityEvent(mNetCapability, mHandler); + if (mActiveCallTracker != null) { + mActiveCallTracker.unregisterLowMediaQualityListener(mHandler); + } } } @@ -1444,9 +1507,7 @@ class RestrictManager { case NetworkCapabilities.NET_CAPABILITY_MMS: case NetworkCapabilities.NET_CAPABILITY_XCAP: case NetworkCapabilities.NET_CAPABILITY_CBS: - callType = - mAltEventListener.isIdleState() - ? QnsConstants.CALL_TYPE_IDLE + callType = mQnsCallStatusTracker.isCallIdle() ? QnsConstants.CALL_TYPE_IDLE : QnsConstants.CALL_TYPE_VOICE; if (transportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { delayMillis = @@ -1484,14 +1545,15 @@ class RestrictManager { // Transport // Type if (transportType != AccessNetworkConstants.TRANSPORT_TYPE_INVALID - && hasRestrictionType( - getOtherTransport(transportType), RestrictManager.RESTRICT_TYPE_GUARDING)) { + && hasRestrictionType(QnsUtils.getOtherTransportType(transportType), + RestrictManager.RESTRICT_TYPE_GUARDING)) { Log.d( mLogTag, "RESTRICT_TYPE_GUARDING cleared from Guarding for:" + QnsConstants.transportTypeToString(mTransportType)); // addRestriction() will take care to notify the ANE of Restrict Info status - releaseRestriction(getOtherTransport(transportType), RESTRICT_TYPE_GUARDING, true); + releaseRestriction( + QnsUtils.getOtherTransportType(transportType), RESTRICT_TYPE_GUARDING, true); } addRestriction( diff --git a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsCallStatusTrackerTest.java b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsCallStatusTrackerTest.java index c55481e..2841373 100644 --- a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsCallStatusTrackerTest.java +++ b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsCallStatusTrackerTest.java @@ -16,12 +16,15 @@ package com.android.telephony.qns; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.when; import android.net.LinkProperties; @@ -30,12 +33,17 @@ import android.os.Handler; import android.os.Message; import android.os.test.TestLooper; import android.telephony.AccessNetworkConstants; +import android.telephony.CallQuality; import android.telephony.CallState; import android.telephony.PreciseCallState; import android.telephony.PreciseDataConnectionState; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; import android.telephony.ims.ImsCallProfile; +import android.telephony.ims.MediaQualityStatus; + +import com.android.telephony.qns.QnsCallStatusTracker.ActiveCallTracker.TransportQuality; +import com.android.telephony.qns.QnsCallStatusTracker.CallQualityBlock; import org.junit.After; import org.junit.Before; @@ -44,35 +52,51 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CountDownLatch; @RunWith(JUnit4.class) public class QnsCallStatusTrackerTest extends QnsTest { QnsCallStatusTracker mCallTracker; TestLooper mTestLooper; + TestLooper mTestLooperListener; + TestLooper mLowQualityListenerLooper; private Handler mImsHandler; private Handler mEmergencyHandler; - CountDownLatch mLatch; - int[] mThresholds; + private Handler mLowQualityHandler; + private MockitoSession mMockSession; List<CallState> mTestCallStateList = new ArrayList<>(); @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); super.setUp(); + mMockSession = mockitoSession().mockStatic(QnsUtils.class).startMocking(); mTestLooper = new TestLooper(); + mTestLooperListener = new TestLooper(); + mLowQualityListenerLooper = new TestLooper(); Mockito.when(sMockContext.getMainLooper()).thenReturn(mTestLooper.getLooper()); - mImsHandler = new Handler(mTestLooper.getLooper()); - mEmergencyHandler = new Handler(mTestLooper.getLooper()); - mCallTracker = new QnsCallStatusTracker(mMockQnsTelephonyListener, 0); + mImsHandler = new Handler(mTestLooperListener.getLooper()); + mEmergencyHandler = new Handler(mTestLooperListener.getLooper()); + mLowQualityHandler = new Handler(mLowQualityListenerLooper.getLooper()); + mCallTracker = new QnsCallStatusTracker( + mMockQnsTelephonyListener, mMockQnsConfigManager, 0, mTestLooper.getLooper()); mCallTracker.registerCallTypeChangedListener( NetworkCapabilities.NET_CAPABILITY_IMS, mImsHandler, 1, null); mCallTracker.registerCallTypeChangedListener( NetworkCapabilities.NET_CAPABILITY_EIMS, mEmergencyHandler, 1, null); + mCallTracker.getActiveCallTracker() + .registerLowMediaQualityListener(mLowQualityHandler, 1, null); + lenient().when(QnsUtils.getOtherTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)) + .thenReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + lenient().when(QnsUtils.getOtherTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) + .thenReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN); + lenient() + .when(QnsUtils.getOtherTransportType(AccessNetworkConstants.TRANSPORT_TYPE_INVALID)) + .thenReturn(AccessNetworkConstants.TRANSPORT_TYPE_INVALID); } @After @@ -82,6 +106,12 @@ public class QnsCallStatusTrackerTest extends QnsTest { NetworkCapabilities.NET_CAPABILITY_IMS, mImsHandler); mCallTracker.unregisterCallTypeChangedListener( NetworkCapabilities.NET_CAPABILITY_EIMS, mEmergencyHandler); + mCallTracker.getActiveCallTracker() + .unregisterLowMediaQualityListener(mLowQualityHandler); + if (mMockSession != null) { + mMockSession.finishMocking(); + mMockSession = null; + } } @Test @@ -92,7 +122,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); mCallTracker.updateCallState(mTestCallStateList); - Message msg = mTestLooper.nextMessage(); + Message msg = mTestLooperListener.nextMessage(); assertEquals(mImsHandler, msg.getTarget()); assertNotEquals(mEmergencyHandler, msg.getTarget()); assertNotNull(msg); @@ -107,14 +137,14 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); // Should not notify if call type is not changed assertNull(msg); // Test3: mTestCallStateList.clear(); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNotNull(msg); result = (QnsAsyncResult) msg.obj; assertNotNull(result.mResult); @@ -129,7 +159,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); mCallTracker.updateCallState(mTestCallStateList); - Message msg = mTestLooper.nextMessage(); + Message msg = mTestLooperListener.nextMessage(); assertEquals(mImsHandler, msg.getTarget()); assertNotNull(msg); QnsAsyncResult result = (QnsAsyncResult) msg.obj; @@ -142,7 +172,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); // Should not notify if call type is not changed assertNull(msg); @@ -155,7 +185,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNotNull(msg); assertEquals(mEmergencyHandler, msg.getTarget()); result = (QnsAsyncResult) msg.obj; @@ -168,7 +198,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNotNull(msg); assertEquals(mImsHandler, msg.getTarget()); result = (QnsAsyncResult) msg.obj; @@ -181,13 +211,13 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNotNull(msg); assertEquals(mEmergencyHandler, msg.getTarget()); result = (QnsAsyncResult) msg.obj; assertNotNull(result.mResult); assertEquals(QnsConstants.CALL_TYPE_IDLE, (int) result.mResult); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNotNull(msg); assertEquals(mImsHandler, msg.getTarget()); result = (QnsAsyncResult) msg.obj; @@ -205,7 +235,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VT) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); mCallTracker.updateCallState(mTestCallStateList); - Message msg = mTestLooper.nextMessage(); + Message msg = mTestLooperListener.nextMessage(); assertNotNull(msg); QnsAsyncResult result = (QnsAsyncResult) msg.obj; assertNotNull(result.mResult); @@ -220,7 +250,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VT) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNotNull(msg); result = (QnsAsyncResult) msg.obj; assertNotNull(result.mResult); @@ -235,7 +265,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNull(msg); assertFalse(mCallTracker.isCallIdle()); // for IMS calls only @@ -244,7 +274,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNotNull(msg); assertEquals(mImsHandler, msg.getTarget()); result = (QnsAsyncResult) msg.obj; @@ -260,7 +290,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VT) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNull(msg); assertFalse(mCallTracker.isCallIdle()); @@ -269,7 +299,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VT) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNotNull(msg); assertEquals(mImsHandler, msg.getTarget()); result = (QnsAsyncResult) msg.obj; @@ -282,7 +312,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VT) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNotNull(msg); assertEquals(mImsHandler, msg.getTarget()); result = (QnsAsyncResult) msg.obj; @@ -292,7 +322,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { mTestCallStateList.clear(); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNull(msg); assertTrue(mCallTracker.isCallIdle()); } @@ -306,7 +336,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VT) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); mCallTracker.updateCallState(mTestCallStateList); - Message msg = mTestLooper.nextMessage(); + Message msg = mTestLooperListener.nextMessage(); assertNotNull(msg); QnsAsyncResult result = (QnsAsyncResult) msg.obj; assertNotNull(result.mResult); @@ -320,14 +350,14 @@ public class QnsCallStatusTrackerTest extends QnsTest { mTestCallStateList.clear(); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNull(msg); mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNull(msg); } @@ -370,7 +400,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); mCallTracker.updateCallState(mTestCallStateList); - Message msg = mTestLooper.nextMessage(); + Message msg = mTestLooperListener.nextMessage(); assertNotNull(msg); assertEquals(mImsHandler, msg.getTarget()); QnsAsyncResult result = (QnsAsyncResult) msg.obj; @@ -383,7 +413,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); // Should not notify if call type is not changed assertNull(msg); @@ -407,7 +437,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { mTestCallStateList.clear(); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNotNull(msg); assertEquals(mImsHandler, msg.getTarget()); result = (QnsAsyncResult) msg.obj; @@ -452,7 +482,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNotNull(msg); assertEquals(mEmergencyHandler, msg.getTarget()); result = (QnsAsyncResult) msg.obj; @@ -461,7 +491,7 @@ public class QnsCallStatusTrackerTest extends QnsTest { mTestCallStateList.clear(); mCallTracker.updateCallState(mTestCallStateList); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNotNull(msg); assertEquals(mEmergencyHandler, msg.getTarget()); result = (QnsAsyncResult) msg.obj; @@ -469,27 +499,807 @@ public class QnsCallStatusTrackerTest extends QnsTest { assertEquals(QnsConstants.CALL_TYPE_IDLE, (int) result.mResult); } + + @Test + public void testActiveCallTrackerOnOff() { + // Test1: + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_INCOMING) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); + mCallTracker.updateCallState(mTestCallStateList); + QnsCallStatusTracker.ActiveCallTracker activeCallTracker = + mCallTracker.getActiveCallTracker(); + assertNotNull(activeCallTracker); + assertEquals(QnsConstants.CALL_TYPE_VOICE, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_IMS, activeCallTracker.getNetCapability()); + + // Test2: + mTestCallStateList.clear(); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_VOICE, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_IMS, activeCallTracker.getNetCapability()); + + // Test3: + mTestCallStateList.clear(); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_IDLE, activeCallTracker.getCallType()); + assertEquals(0, activeCallTracker.getNetCapability()); + + // Test4: + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_DIALING) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_EMERGENCY, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_EIMS, activeCallTracker.getNetCapability()); + + // Test5: + mTestCallStateList.clear(); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_EMERGENCY, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_EIMS, activeCallTracker.getNetCapability()); + + // Test6: + mTestCallStateList.clear(); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_IDLE, activeCallTracker.getCallType()); + assertEquals(0, activeCallTracker.getNetCapability()); + } + + @Test + public void testActiveCallTrackerCallTypeUpdate() { + // Test1: + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_INCOMING) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); + mCallTracker.updateCallState(mTestCallStateList); + QnsCallStatusTracker.ActiveCallTracker activeCallTracker = + mCallTracker.getActiveCallTracker(); + assertNotNull(activeCallTracker); + assertEquals(QnsConstants.CALL_TYPE_VOICE, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_IMS, activeCallTracker.getNetCapability()); + + // Test2: + mTestCallStateList.clear(); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_VOICE, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_IMS, activeCallTracker.getNetCapability()); + + // Test3: + mTestCallStateList.clear(); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) + .setImsCallType(ImsCallProfile.CALL_TYPE_VT) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_VIDEO, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_IMS, activeCallTracker.getNetCapability()); + + // Test4: + mTestCallStateList.clear(); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_IDLE, activeCallTracker.getCallType()); + assertEquals(0, activeCallTracker.getNetCapability()); + } + + @Test + public void testForActiveCallTrackerGetQualityLevel() { + PreciseDataConnectionState imsDataStatusOnWwan = + new PreciseDataConnectionState.Builder() + .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) + .setState(TelephonyManager.DATA_CONNECTED) + .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE) + .setApnSetting( + new ApnSetting.Builder() + .setApnTypeBitmask(ApnSetting.TYPE_IMS) + .setApnName("ims") + .setEntryName("ims") + .build()) + .build(); + + PreciseDataConnectionState imsDataStatusOnWlan = + new PreciseDataConnectionState.Builder() + .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setState(TelephonyManager.DATA_CONNECTED) + .setNetworkType(TelephonyManager.NETWORK_TYPE_IWLAN) + .setApnSetting( + new ApnSetting.Builder() + .setApnTypeBitmask(ApnSetting.TYPE_IMS) + .setApnName("ims") + .setEntryName("ims") + .build()) + .build(); + + when(mMockQnsTelephonyListener.getLastPreciseDataConnectionState( + NetworkCapabilities.NET_CAPABILITY_IMS)) + .thenReturn(imsDataStatusOnWlan); + + // Test1: + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_INCOMING) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); + mCallTracker.updateCallState(mTestCallStateList); + QnsCallStatusTracker.ActiveCallTracker activeCallTracker = + mCallTracker.getActiveCallTracker(); + assertNotNull(activeCallTracker); + assertEquals(QnsConstants.CALL_TYPE_VOICE, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_IMS, activeCallTracker.getNetCapability()); + + // Test2: + mTestCallStateList.clear(); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_VOICE, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_IMS, activeCallTracker.getNetCapability()); + assertEquals( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, activeCallTracker.getTransportType()); + // check TransportQuality is assigned for the current transport type. + TransportQuality transportQuality = + activeCallTracker.getLastTransportQuality(activeCallTracker.getTransportType()); + assertEquals(activeCallTracker.getTransportType(), transportQuality.mTransportType); + assertNotNull(transportQuality.mCallQualityBlockList); + transportQuality.mLowRtpQualityReportedTime = -1; + + mTestCallStateList.clear(); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL) + .setCallQuality(new CallQuality.Builder() + .setUplinkCallQualityLevel(CallQuality.CALL_QUALITY_FAIR) + .setDownlinkCallQualityLevel(CallQuality.CALL_QUALITY_GOOD).build()) + .build()); + long firstCallQaulityUpdateTime = 10000; + lenient().when(QnsUtils.getSystemElapsedRealTime()).thenReturn(firstCallQaulityUpdateTime); + mCallTracker.updateCallState(mTestCallStateList); + mTestLooper.dispatchAll(); + + transportQuality = + activeCallTracker.getLastTransportQuality(activeCallTracker.getTransportType()); + assertNull(activeCallTracker + .getLastTransportQuality(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)); + assertEquals(1, transportQuality.mCallQualityBlockList.size()); + CallQualityBlock qualityBlock = transportQuality.mCallQualityBlockList.get(0); + assertEquals(firstCallQaulityUpdateTime, qualityBlock.mCreatedElapsedTime); + assertEquals(CallQuality.CALL_QUALITY_FAIR, qualityBlock.mUpLinkLevel); + assertEquals(CallQuality.CALL_QUALITY_GOOD, qualityBlock.mDownLinkLevel); + + long testTime = 15000; + lenient().when(QnsUtils.getSystemElapsedRealTime()).thenReturn(testTime); + assertEquals((testTime - firstCallQaulityUpdateTime) * qualityBlock.mUpLinkLevel, + qualityBlock.getUpLinkQualityVolume()); + assertEquals((testTime - firstCallQaulityUpdateTime) * qualityBlock.mDownLinkLevel, + qualityBlock.getDownLinkQualityVolume()); + + long handoverTime = 20000; + lenient().when(QnsUtils.getSystemElapsedRealTime()).thenReturn(handoverTime); + activeCallTracker.onDataConnectionStatusChanged(imsDataStatusOnWwan); + TransportQuality oldTransportQuality = + activeCallTracker.getLastTransportQuality( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN); + TransportQuality newTransportQuality = + activeCallTracker.getLastTransportQuality(activeCallTracker.getTransportType()); + assertEquals(1, oldTransportQuality.mCallQualityBlockList.size()); + CallQualityBlock oldQualityBlock = oldTransportQuality.getLastCallQualityBlock(); + CallQualityBlock newQualityBlock = newTransportQuality.getLastCallQualityBlock(); + assertEquals(firstCallQaulityUpdateTime, oldQualityBlock.mCreatedElapsedTime); + assertEquals( + handoverTime - firstCallQaulityUpdateTime, oldQualityBlock.mDurationMillis); + assertEquals(CallQuality.CALL_QUALITY_FAIR, oldQualityBlock.mUpLinkLevel); + assertEquals(CallQuality.CALL_QUALITY_GOOD, oldQualityBlock.mDownLinkLevel); + + assertEquals(1, newTransportQuality.mCallQualityBlockList.size()); + assertEquals(handoverTime, newQualityBlock.mCreatedElapsedTime); + assertEquals(0, newQualityBlock.mDurationMillis); + assertEquals(CallQuality.CALL_QUALITY_FAIR, newQualityBlock.mUpLinkLevel); + assertEquals(CallQuality.CALL_QUALITY_GOOD, newQualityBlock.mDownLinkLevel); + + long testTime2 = 22000; + lenient().when(QnsUtils.getSystemElapsedRealTime()).thenReturn(testTime2); + assertEquals((handoverTime - firstCallQaulityUpdateTime) + * oldQualityBlock.mUpLinkLevel, oldQualityBlock.getUpLinkQualityVolume()); + assertEquals((handoverTime - firstCallQaulityUpdateTime) + * oldQualityBlock.mDownLinkLevel, oldQualityBlock.getDownLinkQualityVolume()); + assertEquals((testTime2 - handoverTime) * newQualityBlock.mUpLinkLevel, + newQualityBlock.getUpLinkQualityVolume()); + assertEquals((testTime2 - handoverTime) * newQualityBlock.mDownLinkLevel, + newQualityBlock.getDownLinkQualityVolume()); + + long secondCallQualityUpdateTime = 25000; + lenient().when(QnsUtils.getSystemElapsedRealTime()).thenReturn(secondCallQualityUpdateTime); + mTestCallStateList.clear(); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL) + .setCallQuality(new CallQuality.Builder() + .setUplinkCallQualityLevel(CallQuality.CALL_QUALITY_GOOD) + .setDownlinkCallQualityLevel(CallQuality.CALL_QUALITY_FAIR).build()) + .build()); + mCallTracker.updateCallState(mTestCallStateList); + mTestLooper.dispatchAll(); + TransportQuality testTransportQuality = + activeCallTracker.getLastTransportQuality(activeCallTracker.getTransportType()); + assertEquals(2, newTransportQuality.mCallQualityBlockList.size()); + CallQualityBlock prev = testTransportQuality.mCallQualityBlockList.get(0); + assertEquals(CallQuality.CALL_QUALITY_FAIR, prev.mUpLinkLevel); + assertEquals(CallQuality.CALL_QUALITY_GOOD, prev.mDownLinkLevel); + assertEquals(handoverTime, prev.mCreatedElapsedTime); + assertEquals(secondCallQualityUpdateTime - handoverTime, prev.mDurationMillis); + + newQualityBlock = testTransportQuality.getLastCallQualityBlock(); + assertEquals(secondCallQualityUpdateTime, newQualityBlock.mCreatedElapsedTime); + assertEquals(0, newQualityBlock.mDurationMillis); + assertEquals(CallQuality.CALL_QUALITY_GOOD, newQualityBlock.mUpLinkLevel); + assertEquals(CallQuality.CALL_QUALITY_FAIR, newQualityBlock.mDownLinkLevel); + + long handoverTime2 = 30000; + lenient().when(QnsUtils.getSystemElapsedRealTime()).thenReturn(handoverTime2); + activeCallTracker.onDataConnectionStatusChanged(imsDataStatusOnWlan); + + oldTransportQuality = + activeCallTracker.getLastTransportQuality( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + newTransportQuality = + activeCallTracker.getLastTransportQuality(activeCallTracker.getTransportType()); + assertEquals(2, oldTransportQuality.mCallQualityBlockList.size()); + oldQualityBlock = oldTransportQuality.getLastCallQualityBlock(); + newQualityBlock = newTransportQuality.getLastCallQualityBlock(); + assertEquals(secondCallQualityUpdateTime, oldQualityBlock.mCreatedElapsedTime); + assertEquals( + handoverTime2 - secondCallQualityUpdateTime, oldQualityBlock.mDurationMillis); + assertEquals(CallQuality.CALL_QUALITY_GOOD, oldQualityBlock.mUpLinkLevel); + assertEquals(CallQuality.CALL_QUALITY_FAIR, oldQualityBlock.mDownLinkLevel); + + assertEquals(1, newTransportQuality.mCallQualityBlockList.size()); + assertEquals(handoverTime2, newQualityBlock.mCreatedElapsedTime); + assertEquals(0, newQualityBlock.mDurationMillis); + assertEquals(CallQuality.CALL_QUALITY_GOOD, newQualityBlock.mUpLinkLevel); + assertEquals(CallQuality.CALL_QUALITY_FAIR, newQualityBlock.mDownLinkLevel); + + // Test3: + long callEndTime = 40000; + lenient().when(QnsUtils.getSystemElapsedRealTime()).thenReturn(callEndTime); + mTestCallStateList.clear(); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_IDLE, activeCallTracker.getCallType()); + assertEquals(0, activeCallTracker.getNetCapability()); + + oldTransportQuality = activeCallTracker.getLastTransportQuality( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN); + assertEquals(1, oldTransportQuality.mCallQualityBlockList.size()); + oldQualityBlock = oldTransportQuality.getLastCallQualityBlock(); + assertEquals(handoverTime2, oldQualityBlock.mCreatedElapsedTime); + assertEquals( + callEndTime - handoverTime2, oldQualityBlock.mDurationMillis); + assertEquals(CallQuality.CALL_QUALITY_GOOD, oldQualityBlock.mUpLinkLevel); + assertEquals(CallQuality.CALL_QUALITY_FAIR, oldQualityBlock.mDownLinkLevel); + + List<TransportQuality> wlanTransportQualityList = + activeCallTracker.getTransportQualityList( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN); + List<TransportQuality> wwanTransportQualityList = + activeCallTracker.getTransportQualityList( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + assertEquals(2, wlanTransportQualityList.size()); + assertEquals(1, wwanTransportQualityList.size()); + long expectedUplinkQualityLevelWlan = + ((handoverTime - firstCallQaulityUpdateTime) * CallQuality.CALL_QUALITY_FAIR + + (callEndTime - handoverTime2) * CallQuality.CALL_QUALITY_GOOD) + / ((handoverTime - firstCallQaulityUpdateTime) + + (callEndTime - handoverTime2)); + assertEquals(expectedUplinkQualityLevelWlan, activeCallTracker + .getUpLinkQualityLevelDuringCall(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)); + long expectedDownLinkQualityLevelWlan = + ((handoverTime - firstCallQaulityUpdateTime) * CallQuality.CALL_QUALITY_GOOD + + (callEndTime - handoverTime2) * CallQuality.CALL_QUALITY_FAIR) + / ((handoverTime - firstCallQaulityUpdateTime) + + (callEndTime - handoverTime2)); + assertEquals(expectedDownLinkQualityLevelWlan, activeCallTracker + .getDownLinkQualityLevelDuringCall(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)); + } + + @Test + public void testMediaQualityBreachedWithJitter() { + QnsCarrierConfigManager.RtpMetricsConfig config = + new QnsCarrierConfigManager.RtpMetricsConfig(120, 30, 5, 10); + when(mMockQnsConfigManager.getRTPMetricsData()).thenReturn(config); + + PreciseDataConnectionState imsDataStatusOnWlan = + new PreciseDataConnectionState.Builder() + .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setState(TelephonyManager.DATA_CONNECTED) + .setNetworkType(TelephonyManager.NETWORK_TYPE_IWLAN) + .setApnSetting( + new ApnSetting.Builder() + .setApnTypeBitmask(ApnSetting.TYPE_IMS) + .setApnName("ims") + .setEntryName("ims") + .build()) + .build(); + + when(mMockQnsTelephonyListener.getLastPreciseDataConnectionState( + NetworkCapabilities.NET_CAPABILITY_IMS)) + .thenReturn(imsDataStatusOnWlan); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_INCOMING) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); + mCallTracker.updateCallState(mTestCallStateList); + QnsCallStatusTracker.ActiveCallTracker activeCallTracker = + mCallTracker.getActiveCallTracker(); + assertNotNull(activeCallTracker); + assertEquals(QnsConstants.CALL_TYPE_VOICE, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_IMS, activeCallTracker.getNetCapability()); + mTestCallStateList.clear(); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_VOICE, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_IMS, activeCallTracker.getNetCapability()); + + MediaQualityStatus status = + new MediaQualityStatus.Builder( + "1", MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setRtpJitterMillis(130) + .setRtpPacketLossRate(10) + .setRtpInactivityMillis(0).build(); + activeCallTracker.onMediaQualityStatusChanged(status); + mTestLooper.dispatchAll(); + Message msg = mLowQualityListenerLooper.nextMessage(); + assertNotNull(msg); + assertEquals(mLowQualityHandler, msg.getTarget()); + QnsAsyncResult result = (QnsAsyncResult) msg.obj; + assertNotNull(result.mResult); + assertEquals(1 << QnsConstants.RTP_LOW_QUALITY_REASON_JITTER, (int) result.mResult); + } + + @Test + public void testMediaQualityBreachedWithNoRtp() { + QnsCarrierConfigManager.RtpMetricsConfig config = + new QnsCarrierConfigManager.RtpMetricsConfig(120, 30, 5, 10000); + when(mMockQnsConfigManager.getRTPMetricsData()).thenReturn(config); + + PreciseDataConnectionState emergencyDataStatusOnWwan = + new PreciseDataConnectionState.Builder() + .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) + .setState(TelephonyManager.DATA_CONNECTED) + .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE) + .setApnSetting( + new ApnSetting.Builder() + .setApnTypeBitmask(ApnSetting.TYPE_EMERGENCY) + .setApnName("sos") + .setEntryName("sos") + .build()) + .build(); + + when(mMockQnsTelephonyListener.getLastPreciseDataConnectionState( + NetworkCapabilities.NET_CAPABILITY_EIMS)) + .thenReturn(emergencyDataStatusOnWwan); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ALERTING) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); + mCallTracker.updateCallState(mTestCallStateList); + QnsCallStatusTracker.ActiveCallTracker activeCallTracker = + mCallTracker.getActiveCallTracker(); + assertNotNull(activeCallTracker); + assertEquals(QnsConstants.CALL_TYPE_EMERGENCY, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_EIMS, activeCallTracker.getNetCapability()); + mTestCallStateList.clear(); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_EMERGENCY, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_EIMS, activeCallTracker.getNetCapability()); + + MediaQualityStatus status = + new MediaQualityStatus.Builder( + "1", MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setRtpJitterMillis(70) + .setRtpPacketLossRate(10) + .setRtpInactivityMillis(11000).build(); + activeCallTracker.onMediaQualityStatusChanged(status); + mTestLooper.dispatchAll(); + Message msg = mLowQualityListenerLooper.nextMessage(); + assertNotNull(msg); + assertEquals(mLowQualityHandler, msg.getTarget()); + QnsAsyncResult result = (QnsAsyncResult) msg.obj; + assertNotNull(result.mResult); + assertEquals(1 << QnsConstants.RTP_LOW_QUALITY_REASON_NO_RTP, (int) result.mResult); + } + + @Test + public void testMediaQualityBreachedWithPacketLoss() { + QnsCarrierConfigManager.RtpMetricsConfig config = + new QnsCarrierConfigManager.RtpMetricsConfig(120, 30, 5000, 10000); + when(mMockQnsConfigManager.getRTPMetricsData()).thenReturn(config); + + PreciseDataConnectionState emergencyDataStatusOnWwan = + new PreciseDataConnectionState.Builder() + .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) + .setState(TelephonyManager.DATA_CONNECTED) + .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE) + .setApnSetting( + new ApnSetting.Builder() + .setApnTypeBitmask(ApnSetting.TYPE_EMERGENCY) + .setApnName("sos") + .setEntryName("sos") + .build()) + .build(); + + when(mMockQnsTelephonyListener.getLastPreciseDataConnectionState( + NetworkCapabilities.NET_CAPABILITY_EIMS)) + .thenReturn(emergencyDataStatusOnWwan); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ALERTING) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); + mCallTracker.updateCallState(mTestCallStateList); + QnsCallStatusTracker.ActiveCallTracker activeCallTracker = + mCallTracker.getActiveCallTracker(); + assertNotNull(activeCallTracker); + assertEquals(QnsConstants.CALL_TYPE_EMERGENCY, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_EIMS, activeCallTracker.getNetCapability()); + mTestCallStateList.clear(); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_EMERGENCY, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_EIMS, activeCallTracker.getNetCapability()); + + MediaQualityStatus status = + new MediaQualityStatus.Builder( + "1", MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setRtpJitterMillis(70) + .setRtpPacketLossRate(35) + .setRtpInactivityMillis(7000).build(); + activeCallTracker.onMediaQualityStatusChanged(status); + mTestLooper.dispatchAll(); + Message msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + mTestLooper.moveTimeForward(config.mPktLossTime - 1000); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + mTestLooper.moveTimeForward(2000); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNotNull(msg); + assertEquals(mLowQualityHandler, msg.getTarget()); + QnsAsyncResult result = (QnsAsyncResult) msg.obj; + assertNotNull(result.mResult); + assertEquals( + 1 << QnsConstants.RTP_LOW_QUALITY_REASON_PACKET_LOSS, (int) result.mResult); + } + + @Test + public void testMediaQualityBreachedWithPacketLossAdvanced() { + QnsCarrierConfigManager.RtpMetricsConfig config = + new QnsCarrierConfigManager.RtpMetricsConfig(120, 30, 12000, 10000); + when(mMockQnsConfigManager.getRTPMetricsData()).thenReturn(config); + + PreciseDataConnectionState emergencyDataStatusOnWwan = + new PreciseDataConnectionState.Builder() + .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) + .setState(TelephonyManager.DATA_CONNECTED) + .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE) + .setApnSetting( + new ApnSetting.Builder() + .setApnTypeBitmask(ApnSetting.TYPE_EMERGENCY) + .setApnName("sos") + .setEntryName("sos") + .build()) + .build(); + + when(mMockQnsTelephonyListener.getLastPreciseDataConnectionState( + NetworkCapabilities.NET_CAPABILITY_EIMS)) + .thenReturn(emergencyDataStatusOnWwan); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ALERTING) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); + mCallTracker.updateCallState(mTestCallStateList); + QnsCallStatusTracker.ActiveCallTracker activeCallTracker = + mCallTracker.getActiveCallTracker(); + assertNotNull(activeCallTracker); + assertEquals(QnsConstants.CALL_TYPE_EMERGENCY, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_EIMS, activeCallTracker.getNetCapability()); + mTestCallStateList.clear(); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_EMERGENCY, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_EIMS, activeCallTracker.getNetCapability()); + + MediaQualityStatus status = + new MediaQualityStatus.Builder( + "1", MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setRtpJitterMillis(70) + .setRtpPacketLossRate(35) + .setRtpInactivityMillis(7000).build(); + activeCallTracker.onMediaQualityStatusChanged(status); + mTestLooper.dispatchAll(); + Message msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + mTestLooper.moveTimeForward(config.mPktLossTime / 3); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + + status = + new MediaQualityStatus.Builder( + "1", MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setRtpJitterMillis(70) + .setRtpPacketLossRate(10) + .setRtpInactivityMillis(0).build(); + activeCallTracker.onMediaQualityStatusChanged(status); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + mTestLooper.moveTimeForward(4000); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNotNull(msg); + assertEquals(mLowQualityHandler, msg.getTarget()); + QnsAsyncResult result = (QnsAsyncResult) msg.obj; + assertNotNull(result.mResult); + assertEquals(0, (int) result.mResult); + + status = + new MediaQualityStatus.Builder( + "1", MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setRtpJitterMillis(70) + .setRtpPacketLossRate(33) + .setRtpInactivityMillis(7000).build(); + activeCallTracker.onMediaQualityStatusChanged(status); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + mTestLooper.moveTimeForward(config.mPktLossTime / 3); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + + status = + new MediaQualityStatus.Builder( + "1", MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setRtpJitterMillis(70) + .setRtpPacketLossRate(10) + .setRtpInactivityMillis(0).build(); + activeCallTracker.onMediaQualityStatusChanged(status); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + mTestLooper.moveTimeForward(2000); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + + status = + new MediaQualityStatus.Builder( + "1", MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setRtpJitterMillis(70) + .setRtpPacketLossRate(33) + .setRtpInactivityMillis(7000).build(); + activeCallTracker.onMediaQualityStatusChanged(status); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + mTestLooper.moveTimeForward(config.mPktLossTime * 2 / 3); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNotNull(msg); + assertEquals(mLowQualityHandler, msg.getTarget()); + result = (QnsAsyncResult) msg.obj; + assertNotNull(result.mResult); + assertEquals( + 1 << QnsConstants.RTP_LOW_QUALITY_REASON_PACKET_LOSS, (int) result.mResult); + } + + @Test + public void testHandlingLowQualityEventAtHandover() { + QnsCarrierConfigManager.RtpMetricsConfig config = + new QnsCarrierConfigManager.RtpMetricsConfig(120, 30, 12000, 10000); + when(mMockQnsConfigManager.getRTPMetricsData()).thenReturn(config); + PreciseDataConnectionState imsDataStatusOnWlan = + new PreciseDataConnectionState.Builder() + .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setState(TelephonyManager.DATA_CONNECTED) + .setNetworkType(TelephonyManager.NETWORK_TYPE_IWLAN) + .setApnSetting( + new ApnSetting.Builder() + .setApnTypeBitmask(ApnSetting.TYPE_EMERGENCY) + .setApnName("ims") + .setEntryName("ims") + .build()) + .build(); + PreciseDataConnectionState imsDataStatusOnWwan = + new PreciseDataConnectionState.Builder() + .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) + .setState(TelephonyManager.DATA_CONNECTED) + .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE) + .setApnSetting( + new ApnSetting.Builder() + .setApnTypeBitmask(ApnSetting.TYPE_EMERGENCY) + .setApnName("ims") + .setEntryName("ims") + .build()) + .build(); + when(mMockQnsTelephonyListener.getLastPreciseDataConnectionState( + NetworkCapabilities.NET_CAPABILITY_IMS)) + .thenReturn(imsDataStatusOnWwan); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_DIALING) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); + mCallTracker.updateCallState(mTestCallStateList); + QnsCallStatusTracker.ActiveCallTracker activeCallTracker = + mCallTracker.getActiveCallTracker(); + assertNotNull(activeCallTracker); + assertEquals(QnsConstants.CALL_TYPE_VOICE, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_IMS, activeCallTracker.getNetCapability()); + mTestCallStateList.clear(); + mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); + mCallTracker.updateCallState(mTestCallStateList); + assertEquals(QnsConstants.CALL_TYPE_VOICE, activeCallTracker.getCallType()); + assertEquals(NetworkCapabilities.NET_CAPABILITY_IMS, activeCallTracker.getNetCapability()); + + MediaQualityStatus status = + new MediaQualityStatus.Builder( + "1", MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN) + .setRtpJitterMillis(70) + .setRtpPacketLossRate(35) + .setRtpInactivityMillis(0).build(); + activeCallTracker.onMediaQualityStatusChanged(status); + mTestLooper.dispatchAll(); + Message msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + mTestLooper.moveTimeForward(4000); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + + activeCallTracker.onDataConnectionStatusChanged(imsDataStatusOnWlan); + + mTestLooper.moveTimeForward(10000); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + + mTestLooper.moveTimeForward(2500); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNotNull(msg); + assertEquals(mLowQualityHandler, msg.getTarget()); + QnsAsyncResult result = (QnsAsyncResult) msg.obj; + assertNotNull(result.mResult); + assertEquals( + 1 << QnsConstants.RTP_LOW_QUALITY_REASON_PACKET_LOSS, (int) result.mResult); + + mTestLooper.moveTimeForward(1000); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + activeCallTracker.onDataConnectionStatusChanged(imsDataStatusOnWlan); + mTestLooper.moveTimeForward(1000); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + + status = + new MediaQualityStatus.Builder( + "1", MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setRtpJitterMillis(20) + .setRtpPacketLossRate(5) + .setRtpInactivityMillis(0).build(); + activeCallTracker.onMediaQualityStatusChanged(status); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + mTestLooper.moveTimeForward(2100); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + mTestLooper.moveTimeForward(3100); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNotNull(msg); + assertEquals(mLowQualityHandler, msg.getTarget()); + result = (QnsAsyncResult) msg.obj; + assertNotNull(result.mResult); + assertEquals(0, (int) result.mResult); + + mTestLooper.moveTimeForward(10000); + mTestLooper.dispatchAll(); + status = + new MediaQualityStatus.Builder( + "1", MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setRtpJitterMillis(20) + .setRtpPacketLossRate(100) + .setRtpInactivityMillis(11000).build(); + activeCallTracker.onMediaQualityStatusChanged(status); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNotNull(msg); + assertEquals(mLowQualityHandler, msg.getTarget()); + result = (QnsAsyncResult) msg.obj; + assertNotNull(result.mResult); + assertEquals((1 << QnsConstants.RTP_LOW_QUALITY_REASON_NO_RTP) + + (1 << QnsConstants.RTP_LOW_QUALITY_REASON_PACKET_LOSS), (int) result.mResult); + + status = + new MediaQualityStatus.Builder( + "1", MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setRtpJitterMillis(20) + .setRtpPacketLossRate(12) + .setRtpInactivityMillis(0).build(); + activeCallTracker.onMediaQualityStatusChanged(status); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + mTestLooper.moveTimeForward(2000); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + + activeCallTracker.onDataConnectionStatusChanged(imsDataStatusOnWwan); + mTestLooper.moveTimeForward(2000); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNull(msg); + mTestLooper.moveTimeForward(1100); + mTestLooper.dispatchAll(); + msg = mLowQualityListenerLooper.nextMessage(); + assertNotNull(msg); + assertEquals(mLowQualityHandler, msg.getTarget()); + result = (QnsAsyncResult) msg.obj; + assertNotNull(result.mResult); + assertEquals(0, (int) result.mResult); + } + @Test public void testOnSrvccStateChanged() { mTestCallStateList.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_EMERGENCY).build()); mCallTracker.updateCallState(mTestCallStateList); - Message msg = mTestLooper.nextMessage(); + Message msg = mTestLooperListener.nextMessage(); assertNotNull(msg); assertEquals(mEmergencyHandler, msg.getTarget()); QnsAsyncResult result = (QnsAsyncResult) msg.obj; assertNotNull(result.mResult); assertEquals(QnsConstants.CALL_TYPE_EMERGENCY, (int) result.mResult); mCallTracker.onSrvccStateChangedInternal(TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNotNull(msg); result = (QnsAsyncResult) msg.obj; assertNotNull(result.mResult); assertEquals(QnsConstants.CALL_TYPE_IDLE, (int) result.mResult); mCallTracker.onSrvccStateChangedInternal(TelephonyManager.SRVCC_STATE_HANDOVER_STARTED); - msg = mTestLooper.nextMessage(); + msg = mTestLooperListener.nextMessage(); assertNull(msg); } diff --git a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsCarrierConfigManagerTest.java b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsCarrierConfigManagerTest.java index 3e5a858..bffc7c5 100644 --- a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsCarrierConfigManagerTest.java +++ b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsCarrierConfigManagerTest.java @@ -1480,12 +1480,12 @@ public class QnsCarrierConfigManagerTest extends QnsTest { QnsCarrierConfigManager.RtpMetricsConfig rtpMetricsData = mConfigManager.getRTPMetricsData(); - Assert.assertEquals(QnsConstants.KEY_DEFAULT_JITTER, rtpMetricsData.mJitter); - Assert.assertEquals(QnsConstants.KEY_DEFAULT_PACKET_LOSS_RATE, rtpMetricsData.mPktLossRate); + Assert.assertEquals(QnsConstants.INVALID_VALUE, rtpMetricsData.mJitter); + Assert.assertEquals(QnsConstants.INVALID_VALUE, rtpMetricsData.mPktLossRate); Assert.assertEquals( QnsConstants.KEY_DEFAULT_PACKET_LOSS_TIME_MILLIS, rtpMetricsData.mPktLossTime); Assert.assertEquals( - QnsConstants.KEY_DEFAULT_NO_RTP_INTERVAL_MILLIS, rtpMetricsData.mNoRtpInterval); + QnsConstants.INVALID_VALUE, rtpMetricsData.mNoRtpInterval); Assert.assertNotEquals(QnsConstants.KEY_DEFAULT_VALUE, rtpMetricsData.mJitter); Assert.assertNotEquals(QnsConstants.KEY_DEFAULT_VALUE, rtpMetricsData.mPktLossRate); diff --git a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTelephonyListenerTest.java b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTelephonyListenerTest.java index 693a028..ecab8ea 100644 --- a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTelephonyListenerTest.java +++ b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTelephonyListenerTest.java @@ -34,9 +34,11 @@ import android.os.Message; import android.os.test.TestLooper; import android.telephony.AccessNetworkConstants; import android.telephony.BarringInfo; +import android.telephony.CallState; import android.telephony.LteVopsSupportInfo; import android.telephony.NetworkRegistrationInfo; import android.telephony.NrVopsSupportInfo; +import android.telephony.PreciseCallState; import android.telephony.PreciseDataConnectionState; import android.telephony.ServiceState; import android.telephony.SubscriptionManager; @@ -44,6 +46,8 @@ import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.telephony.VopsSupportInfo; import android.telephony.data.ApnSetting; +import android.telephony.ims.ImsCallProfile; +import android.telephony.ims.MediaQualityStatus; import android.util.SparseArray; import org.junit.After; @@ -59,6 +63,7 @@ import org.mockito.MockitoSession; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; +import java.util.function.Consumer; @RunWith(JUnit4.class) public final class QnsTelephonyListenerTest extends QnsTest { @@ -72,6 +77,18 @@ public final class QnsTelephonyListenerTest extends QnsTest { Handler mHandler; TestLooper mTestLooper; + private MediaQualityStatus mTestMediaQuality; + private List<CallState> mTestCallStateList; + private Consumer<List<CallState>> mTestCallStateConsumer = + callStateList -> onTestCallStateChanged(callStateList); + void onTestCallStateChanged(List<CallState> callStateList) { + mTestCallStateList = callStateList; + } + private Consumer<MediaQualityStatus> mTestMediaQualityConsumer = + status -> onTestMediaQualityStatusChanged(status); + void onTestMediaQualityStatusChanged(MediaQualityStatus status) { + mTestMediaQuality = status; + } @Before public void setUp() throws Exception { @@ -91,7 +108,8 @@ public final class QnsTelephonyListenerTest extends QnsTest { @After public void tearDown() { mStaticMockSession.finishMocking(); - + mTestCallStateList = null; + mTestMediaQuality = null; if (mQtListener != null) mQtListener.close(); } @@ -984,6 +1002,41 @@ public final class QnsTelephonyListenerTest extends QnsTest { } @Test + public void testOnCallStateListChanged() { + mQtListener.addCallStatesChangedCallback(mTestCallStateConsumer); + List<CallState> testCallStates = new ArrayList<>(); + testCallStates.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_ACTIVE) + .setImsCallType(ImsCallProfile.CALL_TYPE_VT) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); + testCallStates.add(new CallState.Builder(PreciseCallState.PRECISE_CALL_STATE_HOLDING) + .setImsCallType(ImsCallProfile.CALL_TYPE_VOICE) + .setImsCallServiceType(ImsCallProfile.SERVICE_TYPE_NORMAL).build()); + mQtListener.mTelephonyListener.onCallStatesChanged(testCallStates); + + assertEquals(2, mTestCallStateList.size()); + int index = 0; + for (CallState cs : testCallStates) { + assertEquals(cs, mTestCallStateList.get(index)); + index++; + } + } + + @Test + public void testOnMediaQualityStatusChanged() { + mQtListener.addMediaQualityStatusCallback(mTestMediaQualityConsumer); + MediaQualityStatus testMediaQuality = + new MediaQualityStatus.Builder( + "1", MediaQualityStatus.MEDIA_SESSION_TYPE_AUDIO, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN) + .setRtpJitterMillis(130) + .setRtpPacketLossRate(10) + .setRtpInactivityMillis(7000).build(); + mQtListener.mTelephonyListener.onMediaQualityStatusChanged(testMediaQuality); + + assertEquals(testMediaQuality, mTestMediaQuality); + } + + @Test public void testNullTelephonyListener() { mQtListener.close(); setReady(false); diff --git a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsUtilsTest.java b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsUtilsTest.java index 5834be1..76042bb 100644 --- a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsUtilsTest.java +++ b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsUtilsTest.java @@ -246,7 +246,6 @@ public class QnsUtilsTest extends QnsTest { doReturn(mTestBundle).when(mMockCarrierConfigManager).getConfigForSubId(anyInt()); int[] defaultIntArray = new int[] {1, 2}; int[] defaultNoVopsArray = new int[] {1, 0}; - int[] defaultRtpMetricsIntArray = new int[] {150, 50, 10000, 3000}; int[] defaultWwanHystTimerIntArray = new int[] {50000, 50000, 50000}; int[] defaultWlanHystTimerIntArray = new int[] {50000, 50000, 50000}; int[] defaultNonImsWwanHystTimerIntArray = new int[] {50000, 50000}; @@ -415,6 +414,14 @@ public class QnsUtilsTest extends QnsTest { null, QnsCarrierConfigManager .KEY_QNS_IMS_NETWORK_ENABLE_HO_HYSTERESIS_TIMER_INT)); + assertEquals( + 3000 /* test value */, + (int) + QnsUtils.getConfig( + mTestBundle, + null, + QnsCarrierConfigManager + .KEY_QNS_MEDIA_THRESHOLD_RTP_PACKET_LOSS_TIME_MILLIS_INT)); assertArrayEquals( defaultIwlanMaxHoCountAndFallback, QnsUtils.getConfig( @@ -447,10 +454,6 @@ public class QnsUtilsTest extends QnsTest { null, QnsCarrierConfigManager.KEY_NON_IMS_WLAN_HYSTERESIS_TIMER_MS_INT_ARRAY)); assertArrayEquals( - defaultRtpMetricsIntArray, - QnsUtils.getConfig( - mTestBundle, null, QnsCarrierConfigManager.KEY_QNS_RTP_METRICS_INT_ARRAY)); - assertArrayEquals( defaultWaitingTimerIntArray, QnsUtils.getConfig( mTestBundle, @@ -575,6 +578,9 @@ public class QnsUtilsTest extends QnsTest { mTestBundle.putInt( QnsCarrierConfigManager.KEY_QNS_IMS_NETWORK_ENABLE_HO_HYSTERESIS_TIMER_INT, QnsConstants.COVERAGE_HOME); + mTestBundle.putInt( + QnsCarrierConfigManager.KEY_QNS_MEDIA_THRESHOLD_RTP_PACKET_LOSS_TIME_MILLIS_INT, + 3000); mTestBundle.putIntArray( QnsCarrierConfigManager .KEY_QNS_IN_CALL_ROVEIN_ALLOWED_COUNT_AND_FALLBACK_REASON_INT_ARRAY, @@ -592,9 +598,6 @@ public class QnsUtilsTest extends QnsTest { QnsCarrierConfigManager.KEY_NON_IMS_WLAN_HYSTERESIS_TIMER_MS_INT_ARRAY, new int[] {50000, 50000}); mTestBundle.putIntArray( - QnsCarrierConfigManager.KEY_QNS_RTP_METRICS_INT_ARRAY, - new int[] {150, 50, 10000, 3000}); - mTestBundle.putIntArray( QnsCarrierConfigManager .KEY_WAITING_TIME_FOR_PREFERRED_TRANSPORT_WHEN_POWER_ON_INT_ARRAY, new int[] {45000, 45000}); @@ -785,6 +788,14 @@ public class QnsUtilsTest extends QnsTest { null, QnsCarrierConfigManager .KEY_QNS_IMS_NETWORK_ENABLE_HO_HYSTERESIS_TIMER_INT)); + assertEquals( + QnsConstants.KEY_DEFAULT_PACKET_LOSS_TIME_MILLIS, + (int) + QnsUtils.getConfig( + null, + null, + QnsCarrierConfigManager + .KEY_QNS_MEDIA_THRESHOLD_RTP_PACKET_LOSS_TIME_MILLIS_INT)); assertArrayEquals( new int[] {QnsConstants.MAX_COUNT_INVALID, QnsConstants.FALLBACK_REASON_INVALID}, QnsUtils.getConfig( @@ -825,15 +836,6 @@ public class QnsUtilsTest extends QnsTest { null, QnsCarrierConfigManager.KEY_NON_IMS_WLAN_HYSTERESIS_TIMER_MS_INT_ARRAY)); assertArrayEquals( - new int[] { - QnsConstants.KEY_DEFAULT_JITTER, - QnsConstants.KEY_DEFAULT_PACKET_LOSS_RATE, - QnsConstants.KEY_DEFAULT_PACKET_LOSS_TIME_MILLIS, - QnsConstants.KEY_DEFAULT_NO_RTP_INTERVAL_MILLIS - }, - QnsUtils.getConfig( - null, null, QnsCarrierConfigManager.KEY_QNS_RTP_METRICS_INT_ARRAY)); - assertArrayEquals( new int[] {QnsConstants.KEY_DEFAULT_VALUE, QnsConstants.KEY_DEFAULT_VALUE}, QnsUtils.getConfig( null, diff --git a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/RestrictManagerTest.java b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/RestrictManagerTest.java index d8eea73..c03b5fe 100644 --- a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/RestrictManagerTest.java +++ b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/RestrictManagerTest.java @@ -104,6 +104,13 @@ public class RestrictManagerTest extends QnsTest { MockitoAnnotations.initMocks(this); super.setUp(); mMockSession = mockitoSession().mockStatic(QnsUtils.class).startMocking(); + lenient().when(QnsUtils.getOtherTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)) + .thenReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + lenient().when(QnsUtils.getOtherTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) + .thenReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN); + lenient() + .when(QnsUtils.getOtherTransportType(AccessNetworkConstants.TRANSPORT_TYPE_INVALID)) + .thenReturn(AccessNetworkConstants.TRANSPORT_TYPE_INVALID); when(mMockQnsConfigManager.getWaitingTimerForPreferredTransportOnPowerOn( AccessNetworkConstants.TRANSPORT_TYPE_WLAN)) .thenReturn(0); @@ -478,8 +485,8 @@ public class RestrictManagerTest extends QnsTest { RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); mTestLooper.moveTimeForward(1000); mTestLooper.dispatchAll(); - mAltListener.notifyRtpLowQuality(QnsConstants.CALL_TYPE_VOICE, 1); - mTestLooper.dispatchAll(); + mRestrictManager.onLowRtpQualityEvent( + 1 << QnsConstants.RTP_LOW_QUALITY_REASON_PACKET_LOSS); assertTrue( mRestrictManager.hasRestrictionType( AccessNetworkConstants.TRANSPORT_TYPE_WLAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); @@ -509,9 +516,6 @@ public class RestrictManagerTest extends QnsTest { when(mMockQnsConfigManager.getHoRestrictedTimeOnLowRTPQuality( AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) .thenReturn(30000); - when(mMockQnsConfigManager.getQnsMaxIwlanHoCountDuringCall()).thenReturn(1); - when(mMockQnsConfigManager.getQnsIwlanHoRestrictReason()) - .thenReturn(QnsConstants.FALLBACK_REASON_RTP_ONLY); QnsCarrierConfigManager.RtpMetricsConfig config = new QnsCarrierConfigManager.RtpMetricsConfig(0, 0, 0, 0); when(mMockQnsConfigManager.getRTPMetricsData()).thenReturn(config); @@ -532,8 +536,7 @@ public class RestrictManagerTest extends QnsTest { assertFalse(mRestrictManager.isRestricted(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)); mTestLooper.moveTimeForward(1000); mTestLooper.dispatchAll(); - mAltListener.notifyRtpLowQuality(QnsConstants.CALL_TYPE_VOICE, 1); - mTestLooper.dispatchAll(); + mRestrictManager.onLowRtpQualityEvent(1 << QnsConstants.RTP_LOW_QUALITY_REASON_JITTER); assertTrue( mRestrictManager.hasRestrictionType( AccessNetworkConstants.TRANSPORT_TYPE_WWAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); @@ -579,8 +582,8 @@ public class RestrictManagerTest extends QnsTest { assertFalse(mRestrictManager.isRestricted(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)); mTestLooper.moveTimeForward(1000); mTestLooper.dispatchAll(); - mAltListener.notifyRtpLowQuality(QnsConstants.CALL_TYPE_VOICE, 1); - mTestLooper.dispatchAll(); + mRestrictManager.onLowRtpQualityEvent( + 1 << QnsConstants.RTP_LOW_QUALITY_REASON_NO_RTP); assertTrue( mRestrictManager.hasRestrictionType( AccessNetworkConstants.TRANSPORT_TYPE_WWAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); @@ -608,6 +611,262 @@ public class RestrictManagerTest extends QnsTest { } @Test + public void testLowRtpQualityEventOnBothTransport() { + when(mMockQnsConfigManager.isHysteresisTimerEnabled(QnsConstants.COVERAGE_HOME)) + .thenReturn(true); + when(mMockQnsConfigManager.getHoRestrictedTimeOnLowRTPQuality( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN)) + .thenReturn(30000); + when(mMockQnsConfigManager.getHoRestrictedTimeOnLowRTPQuality( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) + .thenReturn(30000); + when(mMockQnsConfigManager.getQnsMaxIwlanHoCountDuringCall()).thenReturn(1); + when(mMockQnsConfigManager.getQnsIwlanHoRestrictReason()) + .thenReturn(QnsConstants.FALLBACK_REASON_RTP_ONLY); + QnsCarrierConfigManager.RtpMetricsConfig config = + new QnsCarrierConfigManager.RtpMetricsConfig(0, 0, 0, 0); + when(mMockQnsConfigManager.getRTPMetricsData()).thenReturn(config); + when(mMockQnsConfigManager.getWwanHysteresisTimer( + NetworkCapabilities.NET_CAPABILITY_IMS, QnsConstants.CALL_TYPE_VOICE)) + .thenReturn(0); + mRestrictManager.setCellularCoverage(QnsConstants.COVERAGE_HOME); + mRestrictManager.setQnsCallType(QnsConstants.CALL_TYPE_VOICE); + DataConnectionStatusTracker.DataConnectionChangedInfo dcInfo = + new DataConnectionStatusTracker.DataConnectionChangedInfo( + EVENT_DATA_CONNECTION_HANDOVER_SUCCESS, + DataConnectionStatusTracker.STATE_CONNECTED, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN); + mRestrictManager.onDataConnectionChanged(dcInfo); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + mTestLooper.moveTimeForward(1000); + mTestLooper.dispatchAll(); + mRestrictManager.onLowRtpQualityEvent( + 1 << QnsConstants.RTP_LOW_QUALITY_REASON_JITTER); + assertTrue( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertTrue( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + assertFalse(mRestrictManager.isRestricted(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)); + + dcInfo = + new DataConnectionStatusTracker.DataConnectionChangedInfo( + EVENT_DATA_CONNECTION_HANDOVER_SUCCESS, + DataConnectionStatusTracker.STATE_CONNECTED, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + mRestrictManager.onDataConnectionChanged(dcInfo); + assertTrue( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertTrue( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + mRestrictManager.onLowRtpQualityEvent( + 1 << QnsConstants.RTP_LOW_QUALITY_REASON_PACKET_LOSS); + assertTrue( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertTrue( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + assertTrue( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + mTestLooper.moveTimeForward(30000); + mTestLooper.dispatchAll(); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertTrue( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + mRestrictManager.setQnsCallType(QnsConstants.CALL_TYPE_IDLE); + assertFalse(mRestrictManager.isRestricted(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)); + assertFalse(mRestrictManager.isRestricted(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)); + } + + @Test + public void testLowRtpQualityWorseThanPreviousTransportType() { + when(mMockQnsConfigManager.isHysteresisTimerEnabled(QnsConstants.COVERAGE_HOME)) + .thenReturn(true); + when(mMockQnsConfigManager.getHoRestrictedTimeOnLowRTPQuality( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) + .thenReturn(30000); + when(mMockQnsConfigManager.getHoRestrictedTimeOnLowRTPQuality( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN)) + .thenReturn(30000); + when(mMockQnsConfigManager.getQnsMaxIwlanHoCountDuringCall()).thenReturn(10); + when(mMockQnsConfigManager.getQnsIwlanHoRestrictReason()) + .thenReturn(QnsConstants.FALLBACK_REASON_RTP_ONLY); + when(mMockQnsConfigManager.getWwanHysteresisTimer( + NetworkCapabilities.NET_CAPABILITY_IMS, QnsConstants.CALL_TYPE_VOICE)) + .thenReturn(0); + mRestrictManager.setCellularCoverage(QnsConstants.COVERAGE_HOME); + mRestrictManager.setQnsCallType(QnsConstants.CALL_TYPE_VOICE); + DataConnectionStatusTracker.DataConnectionChangedInfo dcInfo = + new DataConnectionStatusTracker.DataConnectionChangedInfo( + EVENT_DATA_CONNECTION_HANDOVER_SUCCESS, + DataConnectionStatusTracker.STATE_CONNECTED, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN); + mRestrictManager.onDataConnectionChanged(dcInfo); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + mTestLooper.moveTimeForward(1000); + mTestLooper.dispatchAll(); + mRestrictManager.onLowRtpQualityEvent( + (1 << QnsConstants.RTP_LOW_QUALITY_REASON_PACKET_LOSS) + + (1 << QnsConstants.RTP_LOW_QUALITY_REASON_JITTER)); + assertTrue( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + assertFalse(mRestrictManager.isRestricted(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)); + + dcInfo = + new DataConnectionStatusTracker.DataConnectionChangedInfo( + EVENT_DATA_CONNECTION_HANDOVER_SUCCESS, + DataConnectionStatusTracker.STATE_CONNECTED, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + mRestrictManager.onDataConnectionChanged(dcInfo); + assertTrue( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + mRestrictManager.onLowRtpQualityEvent( + 1 << QnsConstants.RTP_LOW_QUALITY_REASON_NO_RTP); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + assertTrue( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + mTestLooper.moveTimeForward(30000); + mTestLooper.dispatchAll(); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, + RESTRICT_TYPE_RESTRICT_IWLAN_IN_CALL)); + mRestrictManager.setQnsCallType(QnsConstants.CALL_TYPE_IDLE); + assertFalse(mRestrictManager.isRestricted(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)); + assertFalse(mRestrictManager.isRestricted(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)); + } + + @Test + public void testWwanOnLowRtpQualityReleaseRestriction() { + when(mMockQnsConfigManager.isHysteresisTimerEnabled(QnsConstants.COVERAGE_HOME)) + .thenReturn(true); + when(mMockQnsConfigManager.getHoRestrictedTimeOnLowRTPQuality( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) + .thenReturn(30000); + QnsCarrierConfigManager.RtpMetricsConfig config = + new QnsCarrierConfigManager.RtpMetricsConfig(0, 0, 0, 0); + when(mMockQnsConfigManager.getRTPMetricsData()).thenReturn(config); + when(mMockQnsConfigManager.getWlanHysteresisTimer( + NetworkCapabilities.NET_CAPABILITY_IMS, QnsConstants.CALL_TYPE_VOICE)) + .thenReturn(0); + mRestrictManager.setCellularCoverage(QnsConstants.COVERAGE_HOME); + mRestrictManager.setQnsCallType(QnsConstants.CALL_TYPE_VOICE); + DataConnectionStatusTracker.DataConnectionChangedInfo dcInfo = + new DataConnectionStatusTracker.DataConnectionChangedInfo( + EVENT_DATA_CONNECTION_HANDOVER_SUCCESS, + DataConnectionStatusTracker.STATE_CONNECTED, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + mRestrictManager.onDataConnectionChanged(dcInfo); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse(mRestrictManager.isRestricted(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)); + mTestLooper.moveTimeForward(1000); + mTestLooper.dispatchAll(); + mRestrictManager.onLowRtpQualityEvent(1 << QnsConstants.RTP_LOW_QUALITY_REASON_JITTER); + assertTrue( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse(mRestrictManager.isRestricted(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)); + mTestLooper.moveTimeForward(10000); + mTestLooper.dispatchAll(); + assertTrue( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse(mRestrictManager.isRestricted(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)); + mRestrictManager.onLowRtpQualityEvent(0); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + assertFalse( + mRestrictManager.hasRestrictionType( + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, RESTRICT_TYPE_RTP_LOW_QUALITY)); + mRestrictManager.setQnsCallType(QnsConstants.CALL_TYPE_IDLE); + assertFalse(mRestrictManager.isRestricted(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)); + assertFalse(mRestrictManager.isRestricted(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)); + } + + @Test public void testRestrictWithThrottling() { long throttleTime = SystemClock.elapsedRealtime() + 12000; when(mMockQnsConfigManager.isHysteresisTimerEnabled(QnsConstants.COVERAGE_HOME)) @@ -1485,7 +1744,7 @@ public class RestrictManagerTest extends QnsTest { assertFalse(mRestrictManager.hasRestrictionType(transportType, restrictType)); setConnectionStartedToConnectionFailedState(transportType); assertFalse(mRestrictManager.hasRestrictionType(transportType, restrictType)); - assertTrue(mRestrictManager.mHandler.hasMessages(3010)); + assertTrue(mRestrictManager.mHandler.hasMessages(3009)); setConnectionStartedToConnectionFailedState(transportType); assertFalse(mRestrictManager.hasRestrictionType(transportType, restrictType)); setConnectionStartedToConnectionFailedState(transportType); @@ -1546,7 +1805,7 @@ public class RestrictManagerTest extends QnsTest { mTestLooper.dispatchAll(); assertTrue( mRestrictManager.hasRestrictionType( - mRestrictManager.getOtherTransport(transportType), restrictType)); + QnsUtils.getOtherTransportType(transportType), restrictType)); DataConnectionStatusTracker.DataConnectionChangedInfo dcInfo = new DataConnectionStatusTracker.DataConnectionChangedInfo( EVENT_DATA_CONNECTION_STARTED, @@ -1555,7 +1814,7 @@ public class RestrictManagerTest extends QnsTest { mRestrictManager.onDataConnectionChanged(dcInfo); assertTrue( mRestrictManager.hasRestrictionType( - mRestrictManager.getOtherTransport(transportType), restrictType)); + QnsUtils.getOtherTransportType(transportType), restrictType)); dcInfo = new DataConnectionStatusTracker.DataConnectionChangedInfo( EVENT_DATA_CONNECTION_CONNECTED, @@ -1564,7 +1823,7 @@ public class RestrictManagerTest extends QnsTest { mRestrictManager.onDataConnectionChanged(dcInfo); assertFalse( mRestrictManager.hasRestrictionType( - mRestrictManager.getOtherTransport(transportType), restrictType)); + QnsUtils.getOtherTransportType(transportType), restrictType)); } private void cancelPdnFallbackRunningOnDataConnectedStateOverTransportType( @@ -1986,13 +2245,13 @@ public class RestrictManagerTest extends QnsTest { mRestrictManager.restrictNonPreferredTransport(); assertTrue( mRestrictManager.hasRestrictionType( - mRestrictManager.getOtherTransport(transportType), + QnsUtils.getOtherTransportType(transportType), RESTRICT_TYPE_NON_PREFERRED_TRANSPORT)); mTestLooper.moveTimeForward(10000); mTestLooper.dispatchAll(); assertFalse( mRestrictManager.hasRestrictionType( - mRestrictManager.getOtherTransport(transportType), + QnsUtils.getOtherTransportType(transportType), RESTRICT_TYPE_NON_PREFERRED_TRANSPORT)); } @@ -2098,7 +2357,7 @@ public class RestrictManagerTest extends QnsTest { mRestrictManager.onDataConnectionChanged(dcInfo); assertFalse( mRestrictManager.hasRestrictionType( - mRestrictManager.getOtherTransport(transportType), RESTRICT_TYPE_GUARDING)); + QnsUtils.getOtherTransportType(transportType), RESTRICT_TYPE_GUARDING)); dcInfo = new DataConnectionStatusTracker.DataConnectionChangedInfo( @@ -2110,7 +2369,7 @@ public class RestrictManagerTest extends QnsTest { new DataConnectionStatusTracker.DataConnectionChangedInfo( EVENT_DATA_CONNECTION_HANDOVER_SUCCESS, DataConnectionStatusTracker.STATE_CONNECTED, - mRestrictManager.getOtherTransport(transportType)); + QnsUtils.getOtherTransportType(transportType)); mRestrictManager.onDataConnectionChanged(dcInfo); assertTrue(mRestrictManager.hasRestrictionType(transportType, RESTRICT_TYPE_GUARDING)); } @@ -2293,7 +2552,7 @@ public class RestrictManagerTest extends QnsTest { AccessNetworkConstants.TRANSPORT_TYPE_WWAN, null); mTestLooper.dispatchAll(); - Mockito.verify(mMockWifiBm).registerForRttStatusChange(mRestrictManager.mHandler, 3011); + Mockito.verify(mMockWifiBm).registerForRttStatusChange(mRestrictManager.mHandler, 3010); } @Test |