Add metrics for audio codec during voice calls

Expand existing Telephony metrics to include information on the codec
used during voice calls, including also all renegotiation of the audio
codec during a voice call.

Bug: 119871674
Test: Verified dumping metrics after several calls on CS and IMS
Change-Id: I5de7910e1c1eb69cd9f4351ada040f180b1d6d4a
Merged-In: Ia1c87d8890cf00055f09b883daea08ab84cf9c80
diff --git a/proto/src/telephony.proto b/proto/src/telephony.proto
index 36f1883..600d92e 100644
--- a/proto/src/telephony.proto
+++ b/proto/src/telephony.proto
@@ -1070,6 +1070,9 @@
 
       // System time overwritten by NITZ (Network time)
       NITZ_TIME = 21;
+
+      // Change of audio codec
+      AUDIO_CODEC = 22;
     }
 
     enum RilRequest {
@@ -1162,6 +1165,53 @@
       CALL_DISCONNECTING = 9;
     }
 
+    // Audio codecs
+    enum AudioCodec {
+
+      // Unknown codec
+      AUDIO_CODEC_UNKNOWN = 0;
+
+      AUDIO_CODEC_AMR = 1;
+
+      AUDIO_CODEC_AMR_WB = 2;
+
+      AUDIO_CODEC_QCELP13K = 3;
+
+      AUDIO_CODEC_EVRC = 4;
+
+      AUDIO_CODEC_EVRC_B = 5;
+
+      AUDIO_CODEC_EVRC_WB = 6;
+
+      AUDIO_CODEC_EVRC_NW = 7;
+
+      AUDIO_CODEC_GSM_EFR = 8;
+
+      AUDIO_CODEC_GSM_FR = 9;
+
+      AUDIO_CODEC_GSM_HR = 10;
+
+      AUDIO_CODEC_G711U = 11;
+
+      AUDIO_CODEC_G723 = 12;
+
+      AUDIO_CODEC_G711A = 13;
+
+      AUDIO_CODEC_G722 = 14;
+
+      AUDIO_CODEC_G711AB = 15;
+
+      AUDIO_CODEC_G729 = 16;
+
+      AUDIO_CODEC_EVS_NB = 17;
+
+      AUDIO_CODEC_EVS_WB = 18;
+
+      AUDIO_CODEC_EVS_SWB = 19;
+
+      AUDIO_CODEC_EVS_FB = 20;
+    }
+
     // The information about a voice call
     message RilCall {
 
@@ -1269,6 +1319,9 @@
 
     // NITZ time in milliseconds
     optional int64 nitz_timestamp_millis = 21;
+
+    // Audio codec at the beginning of the session or when changed
+    optional AudioCodec audio_codec = 22;
   }
 
   // Time when call has started, in minutes since epoch,
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index c0f67e2..753c0b6 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -33,6 +33,7 @@
 
 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
 import com.android.internal.telephony.uicc.UiccCardApplication;
 
@@ -75,6 +76,11 @@
     // The cached delay to be used between DTMF tones fetched from carrier config.
     private int mDtmfToneDelay = 0;
 
+    // Store the current audio codec
+    private int mAudioCodec = DriverCall.AUDIO_QUALITY_UNSPECIFIED;
+
+    private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
+
     //***** Event Constants
     static final int EVENT_DTMF_DONE = 1;
     static final int EVENT_PAUSE_DONE = 2;
@@ -654,6 +660,12 @@
             changed = true;
         }
 
+        // Metrics for audio codec
+        if (dc.audioQuality != mAudioCodec) {
+            mAudioCodec = dc.audioQuality;
+            mMetrics.writeAudioCodecGsmCdma(mOwner.getPhone().getPhoneId(), dc.audioQuality);
+        }
+
         // A null cnapName should be the same as ""
         if (TextUtils.isEmpty(dc.name)) {
             if (!TextUtils.isEmpty(mCnapName)) {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 47b3624..5ab89b9 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -46,6 +46,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.UUSInfo;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
 
 import java.util.Objects;
 
@@ -64,6 +65,7 @@
     private ImsPhoneCall mParent;
     private ImsCall mImsCall;
     private Bundle mExtras = new Bundle();
+    private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
 
     private boolean mDisconnected;
 
@@ -123,6 +125,9 @@
      */
     private boolean mIsVideoEnabled = true;
 
+    // Store the current audio codec
+    private int mAudioCodec = ImsStreamMediaProfile.AUDIO_QUALITY_NONE;
+
     //***** Event Constants
     private static final int EVENT_DTMF_DONE = 1;
     private static final int EVENT_PAUSE_DONE = 2;
@@ -955,6 +960,13 @@
                 changed = true;
             }
 
+            // Metrics for audio codec
+            if (localCallProfile != null
+                    && localCallProfile.mMediaProfile.mAudioQuality != mAudioCodec) {
+                mAudioCodec = localCallProfile.mMediaProfile.mAudioQuality;
+                mMetrics.writeAudioCodecIms(mOwner.mPhone.getPhoneId(), imsCall.getCallSession());
+            }
+
             int newAudioQuality =
                     getAudioQualityFromCallProfile(localCallProfile, remoteCallProfile);
             if (getAudioQuality() != newAudioQuality) {
diff --git a/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java b/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
index a8221b4..b4a5e6e 100644
--- a/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
@@ -130,4 +130,10 @@
         mEvent.calls = rilCalls;
         return this;
     }
+
+    /** Set the audio codec. */
+    public CallSessionEventBuilder setAudioCodec(int audioCodec) {
+        mEvent.audioCodec = audioCodec;
+        return this;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index 75ea68f..9af7fc5 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -45,14 +45,17 @@
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataService;
+import android.telephony.ims.ImsCallProfile;
 import android.telephony.ims.ImsCallSession;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.SparseArray;
 
+import com.android.internal.telephony.DriverCall;
 import com.android.internal.telephony.GsmCdmaConnection;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RIL;
@@ -297,6 +300,8 @@
                 return "PHONE_STATE_CHANGED";
             case TelephonyCallSession.Event.Type.NITZ_TIME:
                 return "NITZ_TIME";
+            case TelephonyCallSession.Event.Type.AUDIO_CODEC:
+                return "AUDIO_CODEC";
             default:
                 return Integer.toString(event);
         }
@@ -386,6 +391,9 @@
                                 + " isMultiparty = " + call.isMultiparty);
                     }
                     pw.decreaseIndent();
+                } else if (event.type == TelephonyCallSession.Event.Type.AUDIO_CODEC) {
+                    pw.println(callSessionEventToString(event.type)
+                            + "(" + event.audioCodec + ")");
                 } else {
                     pw.println(callSessionEventToString(event.type));
                 }
@@ -1867,6 +1875,136 @@
         addTelephonyEvent(event);
     }
 
+    /**
+     * Convert IMS audio codec into proto defined value
+     *
+     * @param c IMS codec value
+     * @return Codec value defined in call session proto
+     */
+    private int convertImsCodec(int c) {
+        switch (c) {
+            case ImsStreamMediaProfile.AUDIO_QUALITY_AMR:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR_WB;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_QCELP13K;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_B;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_WB;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_NW;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_EFR;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_FR;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_HR;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_G711U:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G711U;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_G723:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G723;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_G711A:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G711A;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_G722:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G722;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_G711AB:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G711AB;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_G729:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G729;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_NB;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_WB;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_SWB;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_FB;
+            default:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_UNKNOWN;
+        }
+    }
+
+    /**
+     * Convert GSM/CDMA audio codec into proto defined value
+     *
+     * @param c GSM/CDMA codec value
+     * @return Codec value defined in call session proto
+     */
+    private int convertGsmCdmaCodec(int c) {
+        switch (c) {
+            case DriverCall.AUDIO_QUALITY_AMR:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR;
+            case DriverCall.AUDIO_QUALITY_AMR_WB:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR_WB;
+            case DriverCall.AUDIO_QUALITY_GSM_EFR:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_EFR;
+            case DriverCall.AUDIO_QUALITY_GSM_FR:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_FR;
+            case DriverCall.AUDIO_QUALITY_GSM_HR:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_HR;
+            case DriverCall.AUDIO_QUALITY_EVRC:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC;
+            case DriverCall.AUDIO_QUALITY_EVRC_B:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_B;
+            case DriverCall.AUDIO_QUALITY_EVRC_WB:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_WB;
+            case DriverCall.AUDIO_QUALITY_EVRC_NW:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_NW;
+            default:
+                return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_UNKNOWN;
+        }
+    }
+
+    /**
+     * Write audio codec event
+     *
+     * @param phoneId Phone id
+     * @param session IMS call session
+     */
+    public void writeAudioCodecIms(int phoneId, ImsCallSession session) {
+        InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
+        if (callSession == null) {
+            Rlog.e(TAG, "Call session is missing");
+            return;
+        }
+
+        ImsCallProfile localCallProfile = session.getLocalCallProfile();
+        if (localCallProfile != null) {
+            int codec = convertImsCodec(localCallProfile.mMediaProfile.mAudioQuality);
+            callSession.addEvent(new CallSessionEventBuilder(
+                    TelephonyCallSession.Event.Type.AUDIO_CODEC)
+                    .setCallIndex(getCallId(session))
+                    .setAudioCodec(codec));
+
+            if (VDBG) Rlog.v(TAG, "Logged Audio Codec event. Value: " + codec);
+        }
+    }
+
+    /**
+     * Write audio codec event
+     *
+     * @param phoneId Phone id
+     * @param audioQuality Audio quality value
+     */
+    public void writeAudioCodecGsmCdma(int phoneId, int audioQuality) {
+        InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
+        if (callSession == null) {
+            Rlog.e(TAG, "Call session is missing");
+            return;
+        }
+
+        int codec = convertGsmCdmaCodec(audioQuality);
+        callSession.addEvent(new CallSessionEventBuilder(
+                TelephonyCallSession.Event.Type.AUDIO_CODEC)
+                .setAudioCodec(codec));
+
+        if (VDBG) Rlog.v(TAG, "Logged Audio Codec event. Value: " + codec);
+    }
+
     //TODO: Expand the proto in the future
     public void writeOnImsCallProgressing(int phoneId, ImsCallSession session) {}
     public void writeOnImsCallStarted(int phoneId, ImsCallSession session) {}