Loading src/java/com/android/internal/telephony/metrics/CallQualityMetrics.java +95 −45 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import com.android.internal.telephony.ServiceStateTracker; import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession; import java.util.ArrayList; import java.util.Collections; /** * CallQualityMetrics is a utility for tracking the CallQuality during an ongoing call session. It Loading Loading @@ -63,6 +64,12 @@ public class CallQualityMetrics { // the first MAX_SNAPSHOTS transitions between good and bad quality private ArrayList<Pair<CallQuality, Integer>> mDlSnapshots = new ArrayList<>(); // holds lightweight history of call quality and durations, used for calculating total time // spent with bad and good quality for metrics and bugreports. This is separate from the // snapshots because those are capped at MAX_SNAPSHOTS to avoid excessive memory use. private ArrayList<TimestampedQualitySnapshot> mFullUplinkQuality = new ArrayList<>(); private ArrayList<TimestampedQualitySnapshot> mFullDownlinkQuality = new ArrayList<>(); // Current downlink call quality private int mDlCallQualityState = GOOD_QUALITY; Loading @@ -82,12 +89,6 @@ public class CallQualityMetrics { private Pair<CallQuality, Integer> mWorstSsWithBadUlQuality; private Pair<CallQuality, Integer> mBestSsWithBadUlQuality; /** Total durations of good and bad quality time for uplink and downlink */ private int mTotalDlGoodQualityTimeMs = 0; private int mTotalDlBadQualityTimeMs = 0; private int mTotalUlGoodQualityTimeMs = 0; private int mTotalUlBadQualityTimeMs = 0; /** * Construct a CallQualityMetrics object to be used to keep track of call quality for a single * call session. Loading Loading @@ -118,21 +119,31 @@ public class CallQualityMetrics { if (USERDEBUG_MODE) { if (newUlCallQualityState != mUlCallQualityState) { mUlSnapshots = addSnapshot(cq, mUlSnapshots); addSnapshot(cq, mUlSnapshots); } if (newDlCallQualityState != mDlCallQualityState) { mDlSnapshots = addSnapshot(cq, mDlSnapshots); addSnapshot(cq, mDlSnapshots); } } updateTotalDurations(newDlCallQualityState, newUlCallQualityState, cq); updateTotalDurations(cq); updateMinAndMaxSignalStrengthSnapshots(newDlCallQualityState, newUlCallQualityState, cq); mUlCallQualityState = newUlCallQualityState; mDlCallQualityState = newDlCallQualityState; // call duration updates sometimes come out of order if (cq.getCallDuration() > mLastCallQuality.getCallDuration()) { mLastCallQuality = cq; } } private void updateTotalDurations(CallQuality cq) { mFullDownlinkQuality.add(new TimestampedQualitySnapshot(cq.getCallDuration(), cq.getDownlinkCallQualityLevel())); mFullUplinkQuality.add(new TimestampedQualitySnapshot(cq.getCallDuration(), cq.getUplinkCallQualityLevel())); } private static boolean isGoodQuality(int callQualityLevel) { return callQualityLevel < CallQuality.CALL_QUALITY_BAD; Loading @@ -142,32 +153,11 @@ public class CallQualityMetrics { * Save a snapshot of the call quality and signal strength. This can be called with uplink or * downlink call quality level. */ private ArrayList<Pair<CallQuality, Integer>> addSnapshot(CallQuality cq, ArrayList<Pair<CallQuality, Integer>> snapshots) { private void addSnapshot(CallQuality cq, ArrayList<Pair<CallQuality, Integer>> snapshots) { if (snapshots.size() < MAX_SNAPSHOTS) { Integer ss = getLteSnr(); snapshots.add(Pair.create(cq, ss)); } return snapshots; } /** * Updates the running total duration of good and bad call quality for uplink and downlink. */ private void updateTotalDurations(int newDlCallQualityState, int newUlCallQualityState, CallQuality cq) { int timePassed = cq.getCallDuration() - mLastCallQuality.getCallDuration(); if (newDlCallQualityState == GOOD_QUALITY) { mTotalDlGoodQualityTimeMs += timePassed; } else { mTotalDlBadQualityTimeMs += timePassed; } if (newUlCallQualityState == GOOD_QUALITY) { mTotalUlGoodQualityTimeMs += timePassed; } else { mTotalUlBadQualityTimeMs += timePassed; } } /** Loading Loading @@ -261,8 +251,10 @@ public class CallQualityMetrics { public TelephonyCallSession.Event.CallQualitySummary getCallQualitySummaryDl() { TelephonyCallSession.Event.CallQualitySummary summary = new TelephonyCallSession.Event.CallQualitySummary(); summary.totalGoodQualityDurationInSeconds = mTotalDlGoodQualityTimeMs / 1000; summary.totalBadQualityDurationInSeconds = mTotalDlBadQualityTimeMs / 1000; Pair<Integer, Integer> totalGoodAndBadDurations = getTotalGoodAndBadQualityTimeMs( mFullDownlinkQuality); summary.totalGoodQualityDurationInSeconds = totalGoodAndBadDurations.first / 1000; summary.totalBadQualityDurationInSeconds = totalGoodAndBadDurations.second / 1000; // This value could be different from mLastCallQuality.getCallDuration if we support // handover from IMS->CS->IMS, but this is currently not possible // TODO(b/130302396) this also may be possible when we put a call on hold and continue with Loading Loading @@ -299,8 +291,10 @@ public class CallQualityMetrics { public TelephonyCallSession.Event.CallQualitySummary getCallQualitySummaryUl() { TelephonyCallSession.Event.CallQualitySummary summary = new TelephonyCallSession.Event.CallQualitySummary(); summary.totalGoodQualityDurationInSeconds = mTotalUlGoodQualityTimeMs / 1000; summary.totalBadQualityDurationInSeconds = mTotalUlBadQualityTimeMs / 1000; Pair<Integer, Integer> totalGoodAndBadDurations = getTotalGoodAndBadQualityTimeMs( mFullUplinkQuality); summary.totalGoodQualityDurationInSeconds = totalGoodAndBadDurations.first / 1000; summary.totalBadQualityDurationInSeconds = totalGoodAndBadDurations.second / 1000; // This value could be different from mLastCallQuality.getCallDuration if we support // handover from IMS->CS->IMS, but this is currently not possible // TODO(b/130302396) this also may be possible when we put a call on hold and continue with Loading Loading @@ -331,6 +325,60 @@ public class CallQualityMetrics { return summary; } /** * Container class for call quality level and signal strength at the time of snapshot. This * class implements compareTo so that it can be sorted by timestamp */ private class TimestampedQualitySnapshot implements Comparable<TimestampedQualitySnapshot> { int mTimestampMs; int mCallQualityLevel; TimestampedQualitySnapshot(int timestamp, int cq) { mTimestampMs = timestamp; mCallQualityLevel = cq; } @Override public int compareTo(TimestampedQualitySnapshot o) { return this.mTimestampMs - o.mTimestampMs; } @Override public String toString() { return "mTimestampMs=" + mTimestampMs + " mCallQualityLevel=" + mCallQualityLevel; } } /** * Use a list of snapshots to calculate and return the total time spent in a call with good * quality and bad quality. * This is slightly expensive since it involves sorting the snapshots by timestamp. * * @param snapshots a list of uplink or downlink snapshots * @return a pair where the first element is the total good quality time and the second element * is the total bad quality time */ private Pair<Integer, Integer> getTotalGoodAndBadQualityTimeMs( ArrayList<TimestampedQualitySnapshot> snapshots) { int totalGoodQualityTime = 0; int totalBadQualityTime = 0; int lastTimestamp = 0; // sort by timestamp using TimestampedQualitySnapshot.compareTo Collections.sort(snapshots); for (TimestampedQualitySnapshot snapshot : snapshots) { int timeSinceLastSnapshot = snapshot.mTimestampMs - lastTimestamp; if (isGoodQuality(snapshot.mCallQualityLevel)) { totalGoodQualityTime += timeSinceLastSnapshot; } else { totalBadQualityTime += timeSinceLastSnapshot; } lastTimestamp = snapshot.mTimestampMs; } return Pair.create(totalGoodQualityTime, totalBadQualityTime); } @Override public String toString() { StringBuilder sb = new StringBuilder(); Loading @@ -355,14 +403,16 @@ public class CallQualityMetrics { } sb.append("}"); sb.append(" "); sb.append(" mTotalDlGoodQualityTimeMs: "); sb.append(mTotalDlGoodQualityTimeMs); sb.append(" mTotalDlBadQualityTimeMs: "); sb.append(mTotalDlBadQualityTimeMs); sb.append(" mTotalUlGoodQualityTimeMs: "); sb.append(mTotalUlGoodQualityTimeMs); sb.append(" mTotalUlBadQualityTimeMs: "); sb.append(mTotalUlBadQualityTimeMs); Pair<Integer, Integer> dlTotals = getTotalGoodAndBadQualityTimeMs(mFullDownlinkQuality); Pair<Integer, Integer> ulTotals = getTotalGoodAndBadQualityTimeMs(mFullUplinkQuality); sb.append(" TotalDlGoodQualityTimeMs: "); sb.append(dlTotals.first); sb.append(" TotalDlBadQualityTimeMs: "); sb.append(dlTotals.second); sb.append(" TotalUlGoodQualityTimeMs: "); sb.append(ulTotals.first); sb.append(" TotalUlBadQualityTimeMs: "); sb.append(ulTotals.second); sb.append("]"); return sb.toString(); } Loading tests/telephonytests/src/com/android/internal/telephony/metrics/CallQualityMetricsTest.java +125 −6 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import android.telephony.CellSignalStrengthNr; import android.telephony.CellSignalStrengthTdscdma; import android.telephony.CellSignalStrengthWcdma; import android.telephony.SignalStrength; import android.test.suitebuilder.annotation.SmallTest; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.CallQualitySummary; Loading Loading @@ -68,7 +67,6 @@ public class CallQualityMetricsTest extends TelephonyTest { * Verify that good/bad quality and total duration stats are correct. */ @Test @SmallTest public void testTotalDurations() { // Call quality in the following sequence: // Loading Loading @@ -105,12 +103,109 @@ public class CallQualityMetricsTest extends TelephonyTest { assertEquals(14, ulSummary.totalDurationWithQualityInformationInSeconds); } /** * Verify that good/bad quality and total duration stats are correct. * * Similar to testTotalDurations, but getCallQualitySummaryUl/Dl will be called multiple times, * so verify that it continues to work after the first call. */ @Test public void testTotalDurations_MultipleChecks() { // Call quality in the following sequence: // // DL: GOOD GOOD BAD // UL: GOOD BAD GOOD // |----------|----------|--------| // 0 5 10 14 // // 0s = Start of call. Assumed to be good quality // 5s = Switches to UL bad quality // 10s = Switches to UL good quality, DL bad quality // 14s = End of call. Switches to UL bad quality, DL good quality CallQuality cq1 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, CallQuality.CALL_QUALITY_BAD, 5000); CallQuality cq2 = constructCallQuality(CallQuality.CALL_QUALITY_BAD, CallQuality.CALL_QUALITY_EXCELLENT, 10000); CallQuality cq3 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, CallQuality.CALL_QUALITY_BAD, 14000); mCallQualityMetrics.saveCallQuality(cq1); mCallQualityMetrics.saveCallQuality(cq2); mCallQualityMetrics.saveCallQuality(cq3); // verify UL quality durations CallQualitySummary dlSummary = mCallQualityMetrics.getCallQualitySummaryDl(); assertEquals(9, dlSummary.totalGoodQualityDurationInSeconds); assertEquals(5, dlSummary.totalBadQualityDurationInSeconds); assertEquals(14, dlSummary.totalDurationWithQualityInformationInSeconds); // verify DL quality durations CallQualitySummary ulSummary = mCallQualityMetrics.getCallQualitySummaryUl(); assertEquals(5, ulSummary.totalGoodQualityDurationInSeconds); assertEquals(9, ulSummary.totalBadQualityDurationInSeconds); assertEquals(14, ulSummary.totalDurationWithQualityInformationInSeconds); // verify UL quality durations dlSummary = mCallQualityMetrics.getCallQualitySummaryDl(); assertEquals(9, dlSummary.totalGoodQualityDurationInSeconds); assertEquals(5, dlSummary.totalBadQualityDurationInSeconds); assertEquals(14, dlSummary.totalDurationWithQualityInformationInSeconds); // verify DL quality durations ulSummary = mCallQualityMetrics.getCallQualitySummaryUl(); assertEquals(5, ulSummary.totalGoodQualityDurationInSeconds); assertEquals(9, ulSummary.totalBadQualityDurationInSeconds); assertEquals(14, ulSummary.totalDurationWithQualityInformationInSeconds); } /** * Verify that good/bad quality and total duration stats are correct. * * Similar to testTotalDurations but we report the call quality out of order. */ @Test public void testTotalDurations_ReportedOutOfOrder() { // Call quality in the following sequence: // // DL: GOOD GOOD BAD // UL: GOOD BAD GOOD // |----------|----------|--------| // 0 5 10 14 // // 0s = Start of call. Assumed to be good quality // 5s = Switches to UL bad quality // 10s = Switches to UL good quality, DL bad quality // 14s = End of call. Switches to UL bad quality, DL good quality CallQuality cq1 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, CallQuality.CALL_QUALITY_BAD, 5000); CallQuality cq2 = constructCallQuality(CallQuality.CALL_QUALITY_BAD, CallQuality.CALL_QUALITY_EXCELLENT, 10000); CallQuality cq3 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, CallQuality.CALL_QUALITY_BAD, 14000); mCallQualityMetrics.saveCallQuality(cq1); mCallQualityMetrics.saveCallQuality(cq3); mCallQualityMetrics.saveCallQuality(cq2); // verify UL quality durations CallQualitySummary dlSummary = mCallQualityMetrics.getCallQualitySummaryDl(); assertEquals(9, dlSummary.totalGoodQualityDurationInSeconds); assertEquals(5, dlSummary.totalBadQualityDurationInSeconds); assertEquals(14, dlSummary.totalDurationWithQualityInformationInSeconds); // verify DL quality durations CallQualitySummary ulSummary = mCallQualityMetrics.getCallQualitySummaryUl(); assertEquals(5, ulSummary.totalGoodQualityDurationInSeconds); assertEquals(9, ulSummary.totalBadQualityDurationInSeconds); assertEquals(14, ulSummary.totalDurationWithQualityInformationInSeconds); } /** * Verify that a new CallQualityMetrics object is able to return empty summaries if no * CallQuality is reported for the duration of a call. */ @Test @SmallTest public void testNoQualityReported() { // getting the summary for a new CallQualityMetrics object should not fail, and all // durations should be 0 Loading @@ -129,7 +224,6 @@ public class CallQualityMetricsTest extends TelephonyTest { * ignored. */ @Test @SmallTest public void testNotAvailableIsIgnored() { // CallQuality updates from the IMS service with CALL_QUALITY_NOT_AVAILABLE should be // ignored Loading @@ -156,7 +250,6 @@ public class CallQualityMetricsTest extends TelephonyTest { * this just tests for good quality since the logic is the same. */ @Test @SmallTest public void testBestAndWorstSs() { // save good quality with high rssnr CallQuality cq1 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, Loading Loading @@ -201,7 +294,6 @@ public class CallQualityMetricsTest extends TelephonyTest { * likely that one field would be preserved and others would be lost. */ @Test @SmallTest public void testSnapshotOfEndDuration() { CallQuality cq1 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, CallQuality.CALL_QUALITY_BAD, 5000); Loading @@ -220,4 +312,31 @@ public class CallQualityMetricsTest extends TelephonyTest { CallQualitySummary ulSummary = mCallQualityMetrics.getCallQualitySummaryUl(); assertEquals(14, ulSummary.snapshotOfEnd.durationInSeconds); } /** * Verifies that the snapshot of the end (the last reported call quality) is correct. * Currently this just checks the duration since the logic is all the same and it doesn't seem * likely that one field would be preserved and others would be lost. * * Similar to testSnapshotOfEndDuration but we report the call quality out of order */ @Test public void testSnapshotOfEndDuration_ReportedOutOfOrder() { CallQuality cq1 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, CallQuality.CALL_QUALITY_BAD, 5000); CallQuality cq2 = constructCallQuality(CallQuality.CALL_QUALITY_BAD, CallQuality.CALL_QUALITY_EXCELLENT, 10000); CallQuality cq3 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, CallQuality.CALL_QUALITY_BAD, 14000); mCallQualityMetrics.saveCallQuality(cq1); mCallQualityMetrics.saveCallQuality(cq3); mCallQualityMetrics.saveCallQuality(cq2); // verify snapshot of end CallQualitySummary dlSummary = mCallQualityMetrics.getCallQualitySummaryDl(); assertEquals(14, dlSummary.snapshotOfEnd.durationInSeconds); CallQualitySummary ulSummary = mCallQualityMetrics.getCallQualitySummaryUl(); assertEquals(14, ulSummary.snapshotOfEnd.durationInSeconds); } } Loading
src/java/com/android/internal/telephony/metrics/CallQualityMetrics.java +95 −45 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import com.android.internal.telephony.ServiceStateTracker; import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession; import java.util.ArrayList; import java.util.Collections; /** * CallQualityMetrics is a utility for tracking the CallQuality during an ongoing call session. It Loading Loading @@ -63,6 +64,12 @@ public class CallQualityMetrics { // the first MAX_SNAPSHOTS transitions between good and bad quality private ArrayList<Pair<CallQuality, Integer>> mDlSnapshots = new ArrayList<>(); // holds lightweight history of call quality and durations, used for calculating total time // spent with bad and good quality for metrics and bugreports. This is separate from the // snapshots because those are capped at MAX_SNAPSHOTS to avoid excessive memory use. private ArrayList<TimestampedQualitySnapshot> mFullUplinkQuality = new ArrayList<>(); private ArrayList<TimestampedQualitySnapshot> mFullDownlinkQuality = new ArrayList<>(); // Current downlink call quality private int mDlCallQualityState = GOOD_QUALITY; Loading @@ -82,12 +89,6 @@ public class CallQualityMetrics { private Pair<CallQuality, Integer> mWorstSsWithBadUlQuality; private Pair<CallQuality, Integer> mBestSsWithBadUlQuality; /** Total durations of good and bad quality time for uplink and downlink */ private int mTotalDlGoodQualityTimeMs = 0; private int mTotalDlBadQualityTimeMs = 0; private int mTotalUlGoodQualityTimeMs = 0; private int mTotalUlBadQualityTimeMs = 0; /** * Construct a CallQualityMetrics object to be used to keep track of call quality for a single * call session. Loading Loading @@ -118,21 +119,31 @@ public class CallQualityMetrics { if (USERDEBUG_MODE) { if (newUlCallQualityState != mUlCallQualityState) { mUlSnapshots = addSnapshot(cq, mUlSnapshots); addSnapshot(cq, mUlSnapshots); } if (newDlCallQualityState != mDlCallQualityState) { mDlSnapshots = addSnapshot(cq, mDlSnapshots); addSnapshot(cq, mDlSnapshots); } } updateTotalDurations(newDlCallQualityState, newUlCallQualityState, cq); updateTotalDurations(cq); updateMinAndMaxSignalStrengthSnapshots(newDlCallQualityState, newUlCallQualityState, cq); mUlCallQualityState = newUlCallQualityState; mDlCallQualityState = newDlCallQualityState; // call duration updates sometimes come out of order if (cq.getCallDuration() > mLastCallQuality.getCallDuration()) { mLastCallQuality = cq; } } private void updateTotalDurations(CallQuality cq) { mFullDownlinkQuality.add(new TimestampedQualitySnapshot(cq.getCallDuration(), cq.getDownlinkCallQualityLevel())); mFullUplinkQuality.add(new TimestampedQualitySnapshot(cq.getCallDuration(), cq.getUplinkCallQualityLevel())); } private static boolean isGoodQuality(int callQualityLevel) { return callQualityLevel < CallQuality.CALL_QUALITY_BAD; Loading @@ -142,32 +153,11 @@ public class CallQualityMetrics { * Save a snapshot of the call quality and signal strength. This can be called with uplink or * downlink call quality level. */ private ArrayList<Pair<CallQuality, Integer>> addSnapshot(CallQuality cq, ArrayList<Pair<CallQuality, Integer>> snapshots) { private void addSnapshot(CallQuality cq, ArrayList<Pair<CallQuality, Integer>> snapshots) { if (snapshots.size() < MAX_SNAPSHOTS) { Integer ss = getLteSnr(); snapshots.add(Pair.create(cq, ss)); } return snapshots; } /** * Updates the running total duration of good and bad call quality for uplink and downlink. */ private void updateTotalDurations(int newDlCallQualityState, int newUlCallQualityState, CallQuality cq) { int timePassed = cq.getCallDuration() - mLastCallQuality.getCallDuration(); if (newDlCallQualityState == GOOD_QUALITY) { mTotalDlGoodQualityTimeMs += timePassed; } else { mTotalDlBadQualityTimeMs += timePassed; } if (newUlCallQualityState == GOOD_QUALITY) { mTotalUlGoodQualityTimeMs += timePassed; } else { mTotalUlBadQualityTimeMs += timePassed; } } /** Loading Loading @@ -261,8 +251,10 @@ public class CallQualityMetrics { public TelephonyCallSession.Event.CallQualitySummary getCallQualitySummaryDl() { TelephonyCallSession.Event.CallQualitySummary summary = new TelephonyCallSession.Event.CallQualitySummary(); summary.totalGoodQualityDurationInSeconds = mTotalDlGoodQualityTimeMs / 1000; summary.totalBadQualityDurationInSeconds = mTotalDlBadQualityTimeMs / 1000; Pair<Integer, Integer> totalGoodAndBadDurations = getTotalGoodAndBadQualityTimeMs( mFullDownlinkQuality); summary.totalGoodQualityDurationInSeconds = totalGoodAndBadDurations.first / 1000; summary.totalBadQualityDurationInSeconds = totalGoodAndBadDurations.second / 1000; // This value could be different from mLastCallQuality.getCallDuration if we support // handover from IMS->CS->IMS, but this is currently not possible // TODO(b/130302396) this also may be possible when we put a call on hold and continue with Loading Loading @@ -299,8 +291,10 @@ public class CallQualityMetrics { public TelephonyCallSession.Event.CallQualitySummary getCallQualitySummaryUl() { TelephonyCallSession.Event.CallQualitySummary summary = new TelephonyCallSession.Event.CallQualitySummary(); summary.totalGoodQualityDurationInSeconds = mTotalUlGoodQualityTimeMs / 1000; summary.totalBadQualityDurationInSeconds = mTotalUlBadQualityTimeMs / 1000; Pair<Integer, Integer> totalGoodAndBadDurations = getTotalGoodAndBadQualityTimeMs( mFullUplinkQuality); summary.totalGoodQualityDurationInSeconds = totalGoodAndBadDurations.first / 1000; summary.totalBadQualityDurationInSeconds = totalGoodAndBadDurations.second / 1000; // This value could be different from mLastCallQuality.getCallDuration if we support // handover from IMS->CS->IMS, but this is currently not possible // TODO(b/130302396) this also may be possible when we put a call on hold and continue with Loading Loading @@ -331,6 +325,60 @@ public class CallQualityMetrics { return summary; } /** * Container class for call quality level and signal strength at the time of snapshot. This * class implements compareTo so that it can be sorted by timestamp */ private class TimestampedQualitySnapshot implements Comparable<TimestampedQualitySnapshot> { int mTimestampMs; int mCallQualityLevel; TimestampedQualitySnapshot(int timestamp, int cq) { mTimestampMs = timestamp; mCallQualityLevel = cq; } @Override public int compareTo(TimestampedQualitySnapshot o) { return this.mTimestampMs - o.mTimestampMs; } @Override public String toString() { return "mTimestampMs=" + mTimestampMs + " mCallQualityLevel=" + mCallQualityLevel; } } /** * Use a list of snapshots to calculate and return the total time spent in a call with good * quality and bad quality. * This is slightly expensive since it involves sorting the snapshots by timestamp. * * @param snapshots a list of uplink or downlink snapshots * @return a pair where the first element is the total good quality time and the second element * is the total bad quality time */ private Pair<Integer, Integer> getTotalGoodAndBadQualityTimeMs( ArrayList<TimestampedQualitySnapshot> snapshots) { int totalGoodQualityTime = 0; int totalBadQualityTime = 0; int lastTimestamp = 0; // sort by timestamp using TimestampedQualitySnapshot.compareTo Collections.sort(snapshots); for (TimestampedQualitySnapshot snapshot : snapshots) { int timeSinceLastSnapshot = snapshot.mTimestampMs - lastTimestamp; if (isGoodQuality(snapshot.mCallQualityLevel)) { totalGoodQualityTime += timeSinceLastSnapshot; } else { totalBadQualityTime += timeSinceLastSnapshot; } lastTimestamp = snapshot.mTimestampMs; } return Pair.create(totalGoodQualityTime, totalBadQualityTime); } @Override public String toString() { StringBuilder sb = new StringBuilder(); Loading @@ -355,14 +403,16 @@ public class CallQualityMetrics { } sb.append("}"); sb.append(" "); sb.append(" mTotalDlGoodQualityTimeMs: "); sb.append(mTotalDlGoodQualityTimeMs); sb.append(" mTotalDlBadQualityTimeMs: "); sb.append(mTotalDlBadQualityTimeMs); sb.append(" mTotalUlGoodQualityTimeMs: "); sb.append(mTotalUlGoodQualityTimeMs); sb.append(" mTotalUlBadQualityTimeMs: "); sb.append(mTotalUlBadQualityTimeMs); Pair<Integer, Integer> dlTotals = getTotalGoodAndBadQualityTimeMs(mFullDownlinkQuality); Pair<Integer, Integer> ulTotals = getTotalGoodAndBadQualityTimeMs(mFullUplinkQuality); sb.append(" TotalDlGoodQualityTimeMs: "); sb.append(dlTotals.first); sb.append(" TotalDlBadQualityTimeMs: "); sb.append(dlTotals.second); sb.append(" TotalUlGoodQualityTimeMs: "); sb.append(ulTotals.first); sb.append(" TotalUlBadQualityTimeMs: "); sb.append(ulTotals.second); sb.append("]"); return sb.toString(); } Loading
tests/telephonytests/src/com/android/internal/telephony/metrics/CallQualityMetricsTest.java +125 −6 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import android.telephony.CellSignalStrengthNr; import android.telephony.CellSignalStrengthTdscdma; import android.telephony.CellSignalStrengthWcdma; import android.telephony.SignalStrength; import android.test.suitebuilder.annotation.SmallTest; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.CallQualitySummary; Loading Loading @@ -68,7 +67,6 @@ public class CallQualityMetricsTest extends TelephonyTest { * Verify that good/bad quality and total duration stats are correct. */ @Test @SmallTest public void testTotalDurations() { // Call quality in the following sequence: // Loading Loading @@ -105,12 +103,109 @@ public class CallQualityMetricsTest extends TelephonyTest { assertEquals(14, ulSummary.totalDurationWithQualityInformationInSeconds); } /** * Verify that good/bad quality and total duration stats are correct. * * Similar to testTotalDurations, but getCallQualitySummaryUl/Dl will be called multiple times, * so verify that it continues to work after the first call. */ @Test public void testTotalDurations_MultipleChecks() { // Call quality in the following sequence: // // DL: GOOD GOOD BAD // UL: GOOD BAD GOOD // |----------|----------|--------| // 0 5 10 14 // // 0s = Start of call. Assumed to be good quality // 5s = Switches to UL bad quality // 10s = Switches to UL good quality, DL bad quality // 14s = End of call. Switches to UL bad quality, DL good quality CallQuality cq1 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, CallQuality.CALL_QUALITY_BAD, 5000); CallQuality cq2 = constructCallQuality(CallQuality.CALL_QUALITY_BAD, CallQuality.CALL_QUALITY_EXCELLENT, 10000); CallQuality cq3 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, CallQuality.CALL_QUALITY_BAD, 14000); mCallQualityMetrics.saveCallQuality(cq1); mCallQualityMetrics.saveCallQuality(cq2); mCallQualityMetrics.saveCallQuality(cq3); // verify UL quality durations CallQualitySummary dlSummary = mCallQualityMetrics.getCallQualitySummaryDl(); assertEquals(9, dlSummary.totalGoodQualityDurationInSeconds); assertEquals(5, dlSummary.totalBadQualityDurationInSeconds); assertEquals(14, dlSummary.totalDurationWithQualityInformationInSeconds); // verify DL quality durations CallQualitySummary ulSummary = mCallQualityMetrics.getCallQualitySummaryUl(); assertEquals(5, ulSummary.totalGoodQualityDurationInSeconds); assertEquals(9, ulSummary.totalBadQualityDurationInSeconds); assertEquals(14, ulSummary.totalDurationWithQualityInformationInSeconds); // verify UL quality durations dlSummary = mCallQualityMetrics.getCallQualitySummaryDl(); assertEquals(9, dlSummary.totalGoodQualityDurationInSeconds); assertEquals(5, dlSummary.totalBadQualityDurationInSeconds); assertEquals(14, dlSummary.totalDurationWithQualityInformationInSeconds); // verify DL quality durations ulSummary = mCallQualityMetrics.getCallQualitySummaryUl(); assertEquals(5, ulSummary.totalGoodQualityDurationInSeconds); assertEquals(9, ulSummary.totalBadQualityDurationInSeconds); assertEquals(14, ulSummary.totalDurationWithQualityInformationInSeconds); } /** * Verify that good/bad quality and total duration stats are correct. * * Similar to testTotalDurations but we report the call quality out of order. */ @Test public void testTotalDurations_ReportedOutOfOrder() { // Call quality in the following sequence: // // DL: GOOD GOOD BAD // UL: GOOD BAD GOOD // |----------|----------|--------| // 0 5 10 14 // // 0s = Start of call. Assumed to be good quality // 5s = Switches to UL bad quality // 10s = Switches to UL good quality, DL bad quality // 14s = End of call. Switches to UL bad quality, DL good quality CallQuality cq1 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, CallQuality.CALL_QUALITY_BAD, 5000); CallQuality cq2 = constructCallQuality(CallQuality.CALL_QUALITY_BAD, CallQuality.CALL_QUALITY_EXCELLENT, 10000); CallQuality cq3 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, CallQuality.CALL_QUALITY_BAD, 14000); mCallQualityMetrics.saveCallQuality(cq1); mCallQualityMetrics.saveCallQuality(cq3); mCallQualityMetrics.saveCallQuality(cq2); // verify UL quality durations CallQualitySummary dlSummary = mCallQualityMetrics.getCallQualitySummaryDl(); assertEquals(9, dlSummary.totalGoodQualityDurationInSeconds); assertEquals(5, dlSummary.totalBadQualityDurationInSeconds); assertEquals(14, dlSummary.totalDurationWithQualityInformationInSeconds); // verify DL quality durations CallQualitySummary ulSummary = mCallQualityMetrics.getCallQualitySummaryUl(); assertEquals(5, ulSummary.totalGoodQualityDurationInSeconds); assertEquals(9, ulSummary.totalBadQualityDurationInSeconds); assertEquals(14, ulSummary.totalDurationWithQualityInformationInSeconds); } /** * Verify that a new CallQualityMetrics object is able to return empty summaries if no * CallQuality is reported for the duration of a call. */ @Test @SmallTest public void testNoQualityReported() { // getting the summary for a new CallQualityMetrics object should not fail, and all // durations should be 0 Loading @@ -129,7 +224,6 @@ public class CallQualityMetricsTest extends TelephonyTest { * ignored. */ @Test @SmallTest public void testNotAvailableIsIgnored() { // CallQuality updates from the IMS service with CALL_QUALITY_NOT_AVAILABLE should be // ignored Loading @@ -156,7 +250,6 @@ public class CallQualityMetricsTest extends TelephonyTest { * this just tests for good quality since the logic is the same. */ @Test @SmallTest public void testBestAndWorstSs() { // save good quality with high rssnr CallQuality cq1 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, Loading Loading @@ -201,7 +294,6 @@ public class CallQualityMetricsTest extends TelephonyTest { * likely that one field would be preserved and others would be lost. */ @Test @SmallTest public void testSnapshotOfEndDuration() { CallQuality cq1 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, CallQuality.CALL_QUALITY_BAD, 5000); Loading @@ -220,4 +312,31 @@ public class CallQualityMetricsTest extends TelephonyTest { CallQualitySummary ulSummary = mCallQualityMetrics.getCallQualitySummaryUl(); assertEquals(14, ulSummary.snapshotOfEnd.durationInSeconds); } /** * Verifies that the snapshot of the end (the last reported call quality) is correct. * Currently this just checks the duration since the logic is all the same and it doesn't seem * likely that one field would be preserved and others would be lost. * * Similar to testSnapshotOfEndDuration but we report the call quality out of order */ @Test public void testSnapshotOfEndDuration_ReportedOutOfOrder() { CallQuality cq1 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, CallQuality.CALL_QUALITY_BAD, 5000); CallQuality cq2 = constructCallQuality(CallQuality.CALL_QUALITY_BAD, CallQuality.CALL_QUALITY_EXCELLENT, 10000); CallQuality cq3 = constructCallQuality(CallQuality.CALL_QUALITY_EXCELLENT, CallQuality.CALL_QUALITY_BAD, 14000); mCallQualityMetrics.saveCallQuality(cq1); mCallQualityMetrics.saveCallQuality(cq3); mCallQualityMetrics.saveCallQuality(cq2); // verify snapshot of end CallQualitySummary dlSummary = mCallQualityMetrics.getCallQualitySummaryDl(); assertEquals(14, dlSummary.snapshotOfEnd.durationInSeconds); CallQualitySummary ulSummary = mCallQualityMetrics.getCallQualitySummaryUl(); assertEquals(14, ulSummary.snapshotOfEnd.durationInSeconds); } }