Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0e8881c7 authored by Jordan Liu's avatar Jordan Liu Committed by android-build-merger
Browse files

Merge "Sort quality by timestamp"

am: b0dcb980

Change-Id: I71d16040e97f0dd52f94a6cd3aacc8898dd6f1c6
parents 734f817e b0dcb980
Loading
Loading
Loading
Loading
+95 −45
Original line number Diff line number Diff line
@@ -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
@@ -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;

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

    /**
@@ -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
@@ -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
@@ -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();
@@ -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();
    }
+125 −6
Original line number Diff line number Diff line
@@ -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;
@@ -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:
        //
@@ -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
@@ -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
@@ -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,
@@ -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);
@@ -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);
    }
}