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

Commit 4f20a0bf authored by Jordan Liu's avatar Jordan Liu
Browse files

Send call quality metrics

Bug: 128356481
Test: manual
Change-Id: I7967f7b00e884e694664ec835ab103d7756bbcf4
(cherry picked from commit 3482cf4f)
parent a57700c0
Loading
Loading
Loading
Loading
+27 −1
Original line number Diff line number Diff line
@@ -100,6 +100,7 @@ import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.dataconnection.DataEnabledSettings;
import com.android.internal.telephony.dataconnection.DataEnabledSettings.DataEnabledChangedReason;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.metrics.CallQualityMetrics;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
@@ -114,6 +115,7 @@ import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
@@ -151,6 +153,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
            new MmTelFeature.MmTelCapabilities();

    private TelephonyMetrics mMetrics;
    private final Map<String, CallQualityMetrics> mCallQualityMetrics = new ConcurrentHashMap<>();
    private final ConcurrentLinkedQueue<String> mLeastRecentCallId = new ConcurrentLinkedQueue<>();
    private boolean mCarrierConfigLoaded = false;

    private final MmTelFeatureListener mMmTelFeatureListener = new MmTelFeatureListener();
@@ -278,6 +282,10 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
    static final int MAX_CONNECTIONS = 7;
    static final int MAX_CONNECTIONS_PER_CALL = 5;

    // Max number of calls we will keep call quality history for (the history is saved in-memory and
    // included in bug reports).
    private static final int MAX_CALL_QUALITY_HISTORY = 10;

    private static final int EVENT_HANGUP_PENDINGMO = 18;
    private static final int EVENT_DIAL_PENDINGMO = 20;
    private static final int EVENT_EXIT_ECBM_BEFORE_PENDINGMO = 21;
@@ -2319,8 +2327,10 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
                cause = DisconnectCause.IMS_MERGED_SUCCESSFULLY;
            }

            String callId = imsCall.getSession().getCallId();
            mMetrics.writeOnImsCallTerminated(mPhone.getPhoneId(), imsCall.getCallSession(),
                    reasonInfo);
                    reasonInfo, mCallQualityMetrics.get(callId));
            pruneCallQualityMetricsHistory();
            mPhone.notifyImsReason(reasonInfo);

            if (reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL
@@ -2948,6 +2958,14 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
            // convert ServiceState.radioTech to TelephonyManager.NetworkType constant
            mPhone.onCallQualityChanged(callQuality,
                    ServiceState.rilRadioTechnologyToNetworkType(imsCall.getRadioTechnology()));
            String callId = imsCall.getSession().getCallId();
            CallQualityMetrics cqm = mCallQualityMetrics.get(callId);
            if (cqm == null) {
                cqm = new CallQualityMetrics(mPhone);
                mLeastRecentCallId.add(callId);
            }
            cqm.saveCallQuality(callQuality);
            mCallQualityMetrics.put(callId, cqm);
        }
    };

@@ -3500,6 +3518,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
        pw.println(" mDefaultDialerUid=" + mDefaultDialerUid.get());
        pw.println(" mVtDataUsageSnapshot=" + mVtDataUsageSnapshot);
        pw.println(" mVtDataUsageUidSnapshot=" + mVtDataUsageUidSnapshot);
        pw.println(" mCallQualityMetrics=" + mCallQualityMetrics);

        pw.flush();
        pw.println("++++++++++++++++++++++++++++++++");
@@ -4076,6 +4095,13 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
        mIsDataEnabled = isDataEnabled;
    }

    // Removes old call quality metrics if mCallQualityMetrics exceeds its max size
    private void pruneCallQualityMetricsHistory() {
        if (mCallQualityMetrics.size() > MAX_CALL_QUALITY_HISTORY) {
            mCallQualityMetrics.remove(mLeastRecentCallId.poll());
        }
    }

    private void handleFeatureCapabilityChanged(ImsFeature.Capabilities capabilities) {
        boolean tmpIsVideoCallEnabled = isVideoCallEnabled();
        // Check enabledFeatures to determine capabilities. We ignore disabledFeatures.
+345 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.telephony.metrics;

import static com.android.internal.telephony.metrics.TelephonyMetrics.toCallQualityProto;

import android.os.Build;
import android.telephony.CallQuality;
import android.telephony.CellInfo;
import android.telephony.CellSignalStrengthLte;
import android.telephony.Rlog;
import android.telephony.SignalStrength;
import android.util.Pair;

import com.android.internal.telephony.Phone;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;

import java.util.ArrayList;

/**
 * CallQualityMetrics is a utility for tracking the CallQuality during an ongoing call session. It
 * processes snapshots throughout the call to keep track of info like the best and worst
 * ServiceStates, durations of good and bad quality, and other summary statistics.
 */
public class CallQualityMetrics {

    private static final String TAG = CallQualityMetrics.class.getSimpleName();

    // certain metrics are only logged on userdebug
    private static final boolean USERDEBUG_MODE = Build.IS_USERDEBUG;

    // We only log the first MAX_SNAPSHOTS changes to CallQuality
    private static final int MAX_SNAPSHOTS = 5;

    // value of mCallQualityState which means the CallQuality is EXCELLENT/GOOD/FAIR
    private static final int GOOD_QUALITY = 0;

    // value of mCallQualityState which means the CallQuality is BAD/POOR
    private static final int BAD_QUALITY = 1;

    private Phone mPhone;

    /** Snapshots of the call quality and SignalStrength (LTE-SNR for IMS calls) */
    // mUlSnapshots holds snapshots from uplink call quality changes. We log take snapshots of the
    // first MAX_SNAPSHOTS transitions between good and bad quality
    private ArrayList<Pair<CallQuality, Integer>> mUlSnapshots = new ArrayList<>();
    // mDlSnapshots holds snapshots from downlink call quality changes. We log take snapshots of
    // the first MAX_SNAPSHOTS transitions between good and bad quality
    private ArrayList<Pair<CallQuality, Integer>> mDlSnapshots = new ArrayList<>();

    // Current downlink call quality
    private int mDlCallQualityState = GOOD_QUALITY;

    // Current uplink call quality
    private int mUlCallQualityState = GOOD_QUALITY;

    // The last logged CallQuality
    private CallQuality mLastCallQuality;

    /** Snapshots taken at best and worst SignalStrengths*/
    private Pair<CallQuality, Integer> mWorstSsWithGoodDlQuality;
    private Pair<CallQuality, Integer> mBestSsWithGoodDlQuality;
    private Pair<CallQuality, Integer> mWorstSsWithBadDlQuality;
    private Pair<CallQuality, Integer> mBestSsWithBadDlQuality;
    private Pair<CallQuality, Integer> mWorstSsWithGoodUlQuality;
    private Pair<CallQuality, Integer> mBestSsWithGoodUlQuality;
    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.
     */
    public CallQualityMetrics(Phone phone) {
        mPhone = phone;
        mLastCallQuality = new CallQuality();
    }

    /**
     * Called when call quality changes.
     */
    public void saveCallQuality(CallQuality cq) {
        if (cq.getUplinkCallQualityLevel() == CallQuality.CALL_QUALITY_NOT_AVAILABLE
                || cq.getDownlinkCallQualityLevel() == CallQuality.CALL_QUALITY_NOT_AVAILABLE) {
            return;
        }

        // uplink and downlink call quality are tracked separately
        int newUlCallQualityState = BAD_QUALITY;
        int newDlCallQualityState = BAD_QUALITY;
        if (isGoodQuality(cq.getUplinkCallQualityLevel())) {
            newUlCallQualityState = GOOD_QUALITY;
        }
        if (isGoodQuality(cq.getDownlinkCallQualityLevel())) {
            newDlCallQualityState = GOOD_QUALITY;
        }

        if (USERDEBUG_MODE) {
            if (newUlCallQualityState != mUlCallQualityState) {
                mUlSnapshots = addSnapshot(cq, mUlSnapshots);
            }
            if (newDlCallQualityState != mDlCallQualityState) {
                mDlSnapshots = addSnapshot(cq, mDlSnapshots);
            }
        }

        updateTotalDurations(newDlCallQualityState, newUlCallQualityState, cq);

        updateMinAndMaxSignalStrengthSnapshots(newDlCallQualityState, newUlCallQualityState, cq);

        mUlCallQualityState = newUlCallQualityState;
        mDlCallQualityState = newDlCallQualityState;
        mLastCallQuality = cq;
    }

    private static boolean isGoodQuality(int callQualityLevel) {
        return callQualityLevel < CallQuality.CALL_QUALITY_BAD;
    }

    /**
     * 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) {
        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;
        }
    }

    /**
     * Updates the snapshots saved when signal strength is highest and lowest while the call quality
     * is good and bad for both uplink and downlink call quality.
     * <p>
     * At the end of the call we should have:
     *  - for both UL and DL:
     *     - snapshot of the best signal strength with bad call quality
     *     - snapshot of the worst signal strength with bad call quality
     *     - snapshot of the best signal strength with good call quality
     *     - snapshot of the worst signal strength with good call quality
     */
    private void updateMinAndMaxSignalStrengthSnapshots(int newDlCallQualityState,
            int newUlCallQualityState, CallQuality cq) {
        Integer ss = getLteSnr();
        if (ss.equals(CellInfo.UNAVAILABLE)) {
            return;
        }

        // downlink
        if (newDlCallQualityState == GOOD_QUALITY) {
            if (mWorstSsWithGoodDlQuality == null || ss < mWorstSsWithGoodDlQuality.second) {
                mWorstSsWithGoodDlQuality = Pair.create(cq, ss);
            }
            if (mBestSsWithGoodDlQuality == null || ss > mBestSsWithGoodDlQuality.second) {
                mBestSsWithGoodDlQuality = Pair.create(cq, ss);
            }
        } else {
            if (mWorstSsWithBadDlQuality == null || ss < mWorstSsWithBadDlQuality.second) {
                mWorstSsWithBadDlQuality = Pair.create(cq, ss);
            }
            if (mBestSsWithBadDlQuality == null || ss > mBestSsWithBadDlQuality.second) {
                mBestSsWithBadDlQuality = Pair.create(cq, ss);
            }
        }

        // uplink
        if (newUlCallQualityState == GOOD_QUALITY) {
            if (mWorstSsWithGoodUlQuality == null || ss < mWorstSsWithGoodUlQuality.second) {
                mWorstSsWithGoodUlQuality = Pair.create(cq, ss);
            }
            if (mBestSsWithGoodUlQuality == null || ss > mBestSsWithGoodUlQuality.second) {
                mBestSsWithGoodUlQuality = Pair.create(cq, ss);
            }
        } else {
            if (mWorstSsWithBadUlQuality == null || ss < mWorstSsWithBadUlQuality.second) {
                mWorstSsWithBadUlQuality = Pair.create(cq, ss);
            }
            if (mBestSsWithBadUlQuality == null || ss > mBestSsWithBadUlQuality.second) {
                mBestSsWithBadUlQuality = Pair.create(cq, ss);
            }
        }
    }

    // Returns the LTE signal to noise ratio, or 0 if unavailable
    private Integer getLteSnr() {
        ServiceStateTracker sst = mPhone.getServiceStateTracker();
        if (sst == null) {
            Rlog.e(TAG, "getLteSnr: unable to get SST for phone " + mPhone.getPhoneId());
            return CellInfo.UNAVAILABLE;
        }

        SignalStrength ss = sst.getSignalStrength();
        if (ss == null) {
            Rlog.e(TAG, "getLteSnr: unable to get SignalStrength for phone " + mPhone.getPhoneId());
            return CellInfo.UNAVAILABLE;
        }

        // There may be multiple CellSignalStrengthLte, so try to use one with available SNR
        for (CellSignalStrengthLte lteSs : ss.getCellSignalStrengths(CellSignalStrengthLte.class)) {
            int snr = lteSs.getRssnr();
            if (snr != CellInfo.UNAVAILABLE) {
                return snr;
            }
        }

        return CellInfo.UNAVAILABLE;
    }

    private static TelephonyCallSession.Event.SignalStrength toProto(int ss) {
        TelephonyCallSession.Event.SignalStrength ret =
                new TelephonyCallSession.Event.SignalStrength();
        ret.lteSnr = ss;
        return ret;
    }

    /**
     * Return the full downlink CallQualitySummary using the saved CallQuality records.
     */
    public TelephonyCallSession.Event.CallQualitySummary getCallQualitySummaryDl() {
        TelephonyCallSession.Event.CallQualitySummary summary =
                new TelephonyCallSession.Event.CallQualitySummary();
        summary.totalGoodQualityDurationInSeconds = mTotalDlGoodQualityTimeMs / 1000;
        summary.totalBadQualityDurationInSeconds = mTotalDlBadQualityTimeMs / 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
        // another call
        summary.totalDurationWithQualityInformationInSeconds = mLastCallQuality.getCallDuration();
        summary.snapshotOfWorstSsWithGoodQuality =
                toCallQualityProto(mWorstSsWithGoodDlQuality.first);
        summary.snapshotOfBestSsWithGoodQuality =
                toCallQualityProto(mBestSsWithGoodDlQuality.first);
        summary.snapshotOfWorstSsWithBadQuality =
                toCallQualityProto(mWorstSsWithBadDlQuality.first);
        summary.snapshotOfBestSsWithBadQuality = toCallQualityProto(mBestSsWithBadDlQuality.first);
        summary.worstSsWithGoodQuality = toProto(mWorstSsWithGoodDlQuality.second);
        summary.bestSsWithGoodQuality = toProto(mBestSsWithGoodDlQuality.second);
        summary.worstSsWithBadQuality = toProto(mWorstSsWithBadDlQuality.second);
        summary.bestSsWithBadQuality = toProto(mBestSsWithBadDlQuality.second);
        summary.snapshotOfEnd = toCallQualityProto(mLastCallQuality);
        return summary;
    }

    /**
     * Return the full uplink CallQualitySummary using the saved CallQuality records.
     */
    public TelephonyCallSession.Event.CallQualitySummary getCallQualitySummaryUl() {
        TelephonyCallSession.Event.CallQualitySummary summary =
                new TelephonyCallSession.Event.CallQualitySummary();
        summary.totalGoodQualityDurationInSeconds = mTotalUlGoodQualityTimeMs / 1000;
        summary.totalBadQualityDurationInSeconds = mTotalUlBadQualityTimeMs / 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
        // another call
        summary.totalDurationWithQualityInformationInSeconds = mLastCallQuality.getCallDuration();
        summary.snapshotOfWorstSsWithGoodQuality =
                toCallQualityProto(mWorstSsWithGoodUlQuality.first);
        summary.snapshotOfBestSsWithGoodQuality =
                toCallQualityProto(mBestSsWithGoodUlQuality.first);
        summary.snapshotOfWorstSsWithBadQuality =
                toCallQualityProto(mWorstSsWithBadUlQuality.first);
        summary.snapshotOfBestSsWithBadQuality = toCallQualityProto(mBestSsWithBadUlQuality.first);
        summary.worstSsWithGoodQuality = toProto(mWorstSsWithGoodUlQuality.second);
        summary.bestSsWithGoodQuality = toProto(mBestSsWithGoodUlQuality.second);
        summary.worstSsWithBadQuality = toProto(mWorstSsWithBadUlQuality.second);
        summary.bestSsWithBadQuality = toProto(mBestSsWithBadUlQuality.second);
        summary.snapshotOfEnd = toCallQualityProto(mLastCallQuality);
        return summary;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[CallQualityMetrics phone ");
        sb.append(mPhone.getPhoneId());
        sb.append(" mUlSnapshots: {");
        for (Pair<CallQuality, Integer> snapshot : mUlSnapshots) {
            sb.append(" {");
            sb.append(snapshot.first);
            sb.append("}");
        }
        sb.append("}");
        sb.append(" mDlSnapshots:{");
        for (Pair<CallQuality, Integer> snapshot : mDlSnapshots) {
            sb.append(" {");
            sb.append(snapshot.first);
            sb.append("}");
        }
        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);
        sb.append("]");
        return sb.toString();
    }
}
+65 −6
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.os.Build;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Telephony.Sms.Intents;
import android.telephony.CallQuality;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SmsManager;
@@ -1988,6 +1989,53 @@ public class TelephonyMetrics {
        return ri;
    }

    /**
     * Convert CallQuality to proto.
     *
     * @param callQuality call quality to convert
     * @return Coverted proto
     */
    public static TelephonyCallSession.Event.CallQuality toCallQualityProto(
            CallQuality callQuality) {
        TelephonyCallSession.Event.CallQuality cq = new TelephonyCallSession.Event.CallQuality();
        if (callQuality != null) {
            cq.downlinkLevel = callQualityLevelToProtoEnum(callQuality
                    .getDownlinkCallQualityLevel());
            cq.uplinkLevel = callQualityLevelToProtoEnum(callQuality.getUplinkCallQualityLevel());
            // callDuration is reported in millis, so convert to seconds
            cq.durationInSeconds = callQuality.getCallDuration() / 1000;
            cq.rtpPacketsTransmitted = callQuality.getNumRtpPacketsTransmitted();
            cq.rtpPacketsReceived = callQuality.getNumRtpPacketsReceived();
            cq.rtpPacketsTransmittedLost = callQuality.getNumRtpPacketsTransmittedLost();
            cq.rtpPacketsNotReceived = callQuality.getNumRtpPacketsNotReceived();
            cq.averageRelativeJitterMillis = callQuality.getAverageRelativeJitter();
            cq.maxRelativeJitterMillis = callQuality.getMaxRelativeJitter();
            cq.codecType = convertImsCodec(callQuality.getCodecType());
        }
        return cq;
    }

    /**
     * Convert Call quality level into proto defined value.
     */
    private static int callQualityLevelToProtoEnum(int level) {
        if (level == CallQuality.CALL_QUALITY_EXCELLENT) {
            return TelephonyCallSession.Event.CallQuality.CallQualityLevel.EXCELLENT;
        } else if (level == CallQuality.CALL_QUALITY_GOOD) {
            return TelephonyCallSession.Event.CallQuality.CallQualityLevel.GOOD;
        } else if (level == CallQuality.CALL_QUALITY_FAIR) {
            return TelephonyCallSession.Event.CallQuality.CallQualityLevel.FAIR;
        } else if (level == CallQuality.CALL_QUALITY_POOR) {
            return TelephonyCallSession.Event.CallQuality.CallQualityLevel.POOR;
        } else if (level == CallQuality.CALL_QUALITY_BAD) {
            return TelephonyCallSession.Event.CallQuality.CallQualityLevel.BAD;
        } else if (level == CallQuality.CALL_QUALITY_NOT_AVAILABLE) {
            return TelephonyCallSession.Event.CallQuality.CallQualityLevel.NOT_AVAILABLE;
        } else {
            return TelephonyCallSession.Event.CallQuality.CallQualityLevel.UNDEFINED;
        }
    }

    /**
     * Write IMS call end event
     *
@@ -1996,17 +2044,28 @@ public class TelephonyMetrics {
     * @param reasonInfo Call end reason
     */
    public void writeOnImsCallTerminated(int phoneId, ImsCallSession session,
                                         ImsReasonInfo reasonInfo) {
                                         ImsReasonInfo reasonInfo, CallQualityMetrics cqm) {
        InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
        if (callSession == null) {
            Rlog.e(TAG, "Call session is missing");
        } else {
            if (cqm != null) {
                callSession.addEvent(
                    new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED)
                        new CallSessionEventBuilder(
                                TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED)
                        .setCallIndex(getCallId(session))
                        .setImsReasonInfo(toImsReasonInfoProto(reasonInfo))
                        .setCallQualitySummaryDl(cqm.getCallQualitySummaryDl())
                        .setCallQualitySummaryUl(cqm.getCallQualitySummaryUl()));
            } else {
                callSession.addEvent(
                        new CallSessionEventBuilder(
                                TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED)
                        .setCallIndex(getCallId(session))
                        .setImsReasonInfo(toImsReasonInfoProto(reasonInfo)));
            }
        }
    }

    /**
     * Write IMS call hangover event
@@ -2394,7 +2453,7 @@ public class TelephonyMetrics {
     * @param c IMS codec value
     * @return Codec value defined in call session proto
     */
    private int convertImsCodec(int c) {
    private static int convertImsCodec(int c) {
        switch (c) {
            case ImsStreamMediaProfile.AUDIO_QUALITY_AMR:
                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR;
+1 −0
Original line number Diff line number Diff line
@@ -174,6 +174,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
        }).when(imsCall).hold();

        imsCall.attachSession(mImsCallSession);
        doReturn("1").when(mImsCallSession).getCallId();
    }

    @Before