Merge "[DO NOT MERGE] Add RCS metrics for provisioning event"
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 804382a..9ce83b3 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -5039,10 +5039,13 @@
     @Override
     public int getDataNetworkTypeForSubscriber(int subId, String callingPackage,
             String callingFeatureId) {
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, subId, callingPackage, callingFeatureId,
-                "getDataNetworkTypeForSubscriber")) {
-            return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        String functionName = "getDataNetworkTypeForSubscriber";
+        if (!TelephonyPermissions.checkCallingOrSelfReadNonDangerousPhoneStateNoThrow(
+                mApp, functionName)) {
+            if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+                    mApp, subId, callingPackage, callingFeatureId, functionName)) {
+                return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+            }
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -5064,10 +5067,13 @@
     @Override
     public int getVoiceNetworkTypeForSubscriber(int subId, String callingPackage,
             String callingFeatureId) {
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, subId, callingPackage, callingFeatureId,
-                "getDataNetworkTypeForSubscriber")) {
-            return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        String functionName = "getVoiceNetworkTypeForSubscriber";
+        if (!TelephonyPermissions.checkCallingOrSelfReadNonDangerousPhoneStateNoThrow(
+                mApp, functionName)) {
+            if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+                    mApp, subId, callingPackage, callingFeatureId, functionName)) {
+                return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+            }
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -6580,18 +6586,25 @@
      * There are other factors deciding whether mobile data is actually enabled, but they are
      * not considered here. See {@link #isDataEnabled(int)} for more details.
      *
-     * Accepts either ACCESS_NETWORK_STATE, MODIFY_PHONE_STATE or carrier privileges.
+     * Accepts either READ_BASIC_PHONE_STATE, ACCESS_NETWORK_STATE, MODIFY_PHONE_STATE
+     * or carrier privileges.
      *
      * @return {@code true} if data is enabled else {@code false}
      */
     @Override
     public boolean isUserDataEnabled(int subId) {
+        String functionName = "isUserDataEnabled";
         try {
-            mApp.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
-                    null);
+            try {
+                mApp.enforceCallingOrSelfPermission(permission.READ_BASIC_PHONE_STATE,
+                        functionName);
+            } catch (Exception e) {
+                mApp.enforceCallingOrSelfPermission(permission.ACCESS_NETWORK_STATE, functionName);
+            }
         } catch (Exception e) {
             TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
-                    mApp, subId, "isUserDataEnabled");
+                    mApp, subId, functionName);
+
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -6621,17 +6634,24 @@
      */
     @Override
     public boolean isDataEnabled(int subId) {
+        String functionName = "isDataEnabled";
         try {
             try {
                 mApp.enforceCallingOrSelfPermission(
                         android.Manifest.permission.ACCESS_NETWORK_STATE,
-                        null);
+                        functionName);
             } catch (Exception e) {
-                mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
-                        "isDataEnabled");
+                try {
+                    mApp.enforceCallingOrSelfPermission(
+                            android.Manifest.permission.READ_PHONE_STATE,
+                            functionName);
+                } catch (Exception e2) {
+                    mApp.enforceCallingOrSelfPermission(
+                            permission.READ_BASIC_PHONE_STATE, functionName);
+                }
             }
         } catch (Exception e) {
-            enforceReadPrivilegedPermission("isDataEnabled");
+            enforceReadPrivilegedPermission(functionName);
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -6661,12 +6681,24 @@
     @Override
     public boolean isDataEnabledForReason(int subId,
             @TelephonyManager.DataEnabledReason int reason) {
+        String functionName = "isDataEnabledForReason";
         try {
-            mApp.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
-                    null);
+            try {
+                mApp.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.ACCESS_NETWORK_STATE,
+                        functionName);
+            } catch (Exception e) {
+                mApp.enforceCallingOrSelfPermission(permission.READ_BASIC_PHONE_STATE,
+                        functionName);
+            }
         } catch (Exception e) {
-            mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
-                    "isDataEnabledForReason");
+            try {
+                mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
+                        functionName);
+            } catch (Exception e2) {
+                TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+                        mApp, subId, functionName);
+            }
         }
 
 
@@ -8586,6 +8618,7 @@
      *
      * <p>Requires one of the following permissions:
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE},
+     * {@link android.Manifest.permission#READ_BASIC_PHONE_STATE},
      * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling app has carrier
      * privileges.
      *
@@ -8595,12 +8628,19 @@
      */
     @Override
     public boolean isDataRoamingEnabled(int subId) {
+        String functionName = "isDataRoamingEnabled";
         try {
-            mApp.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
-                    null);
+            try {
+                mApp.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.ACCESS_NETWORK_STATE,
+                        functionName);
+            } catch (Exception e) {
+                mApp.enforceCallingOrSelfPermission(
+                        permission.READ_BASIC_PHONE_STATE, functionName);
+            }
         } catch (Exception e) {
             TelephonyPermissions.enforceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
-                    mApp, subId, "isDataRoamingEnabled");
+                    mApp, subId, functionName);
         }
 
         boolean isEnabled = false;
diff --git a/src/com/android/services/telephony/rcs/DelegateStateTracker.java b/src/com/android/services/telephony/rcs/DelegateStateTracker.java
index 321c7ba..18aab88 100644
--- a/src/com/android/services/telephony/rcs/DelegateStateTracker.java
+++ b/src/com/android/services/telephony/rcs/DelegateStateTracker.java
@@ -24,9 +24,12 @@
 import android.telephony.ims.aidl.ISipDelegate;
 import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
 import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Log;
 
+import com.android.internal.telephony.metrics.RcsStats;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -50,11 +53,15 @@
     private boolean mCreatedCalled = false;
     private int mRegistrationStateOverride = -1;
 
+    private Set<String> mDelegateSupportedTags;
+    private final RcsStats mRcsStats;
+
     public DelegateStateTracker(int subId, ISipDelegateConnectionStateCallback appStateCallback,
-            ISipDelegate localDelegateImpl) {
+            ISipDelegate localDelegateImpl, RcsStats rcsStats) {
         mSubId = subId;
         mAppStateCallback = appStateCallback;
         mLocalDelegateImpl = localDelegateImpl;
+        mRcsStats = rcsStats;
     }
 
     /**
@@ -63,10 +70,13 @@
      * Registration and state updates will be send via the
      * {@link SipDelegateBinderConnection.StateCallback} callback implemented by this class as they
      * arrive.
+     * @param supportedTags the tags supported by the SipTransportController and ImsService creating
+     *                      the SipDelegate. These tags will be used as a key for SipDelegate
+     *                      metrics.
      * @param deniedTags The tags denied by the SipTransportController and ImsService creating the
      *         SipDelegate. These tags will need to be notified back to the IMS application.
      */
-    public void sipDelegateConnected(Set<FeatureTagState> deniedTags) {
+    public void sipDelegateConnected(Set<String> supportedTags, Set<FeatureTagState> deniedTags) {
         logi("SipDelegate connected with denied tags:" + deniedTags);
         // From the IMS application perspective, we only call onCreated/onDestroyed once and
         // provide the local implementation of ISipDelegate, which doesn't change, even though
@@ -74,6 +84,8 @@
         if (!mCreatedCalled) {
             mCreatedCalled = true;
             notifySipDelegateCreated();
+            mDelegateSupportedTags = supportedTags;
+            mRcsStats.createSipDelegateStats(mSubId, mDelegateSupportedTags);
         }
         mRegistrationStateOverride = -1;
         mDelegateDeniedTags = new ArrayList<>(deniedTags);
@@ -84,8 +96,8 @@
      *
      * This will trigger an override of the IMS application's registration state. All feature tags
      * in the REGISTERED state will be overridden to move to the deregistering state specified until
-     * a new SipDelegate was successfully created and {@link #sipDelegateConnected(Set)} was called
-     * or it was destroyed and {@link #sipDelegateDestroyed(int)} was called.
+     * a new SipDelegate was successfully created and {@link #sipDelegateConnected(Set, Set)} was
+     * called or it was destroyed and {@link #sipDelegateDestroyed(int)} was called.
      * @param deregisteringReason The new deregistering reason that all feature tags in the
      *         registered state should now report.
      */
@@ -115,6 +127,7 @@
         mRegistrationStateOverride = -1;
         try {
             mAppStateCallback.onDestroyed(reason);
+            mRcsStats.onSipDelegateStats(mSubId, mDelegateSupportedTags, reason);
         } catch (RemoteException e) {
             logw("sipDelegateDestroyed: IMS application is dead: " + e);
         }
@@ -141,6 +154,11 @@
         logi("onRegistrationStateChanged: sending reg state " + registrationState);
         try {
             mAppStateCallback.onFeatureTagStatusChanged(registrationState, mDelegateDeniedTags);
+            Set<String> registeredFeatureTags = registrationState.getRegisteredFeatureTags();
+            mRcsStats.onSipTransportFeatureTagStats(mSubId,
+                    new ArraySet<FeatureTagState>(mDelegateDeniedTags),
+                    registrationState.getDeregisteredFeatureTags(),
+                    registeredFeatureTags);
         } catch (RemoteException e) {
             logw("onRegistrationStateChanged: IMS application is dead: " + e);
         }
diff --git a/src/com/android/services/telephony/rcs/SipDelegateController.java b/src/com/android/services/telephony/rcs/SipDelegateController.java
index 8cc70a4..c728141 100644
--- a/src/com/android/services/telephony/rcs/SipDelegateController.java
+++ b/src/com/android/services/telephony/rcs/SipDelegateController.java
@@ -34,6 +34,7 @@
 import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.metrics.RcsStats;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.PrintWriter;
@@ -102,7 +103,7 @@
         mMessageTransportWrapper = new MessageTransportWrapper(mSubId, executorService,
                 messageCallback);
         mDelegateStateTracker = new DelegateStateTracker(mSubId, stateCallback,
-                mMessageTransportWrapper.getDelegateConnection());
+                mMessageTransportWrapper.getDelegateConnection(), RcsStats.getInstance());
     }
 
     /**
@@ -191,7 +192,7 @@
                     .collect(Collectors.toSet()));
             mMessageTransportWrapper.openTransport(resultPair.first, allowedTags,
                     resultPair.second);
-            mDelegateStateTracker.sipDelegateConnected(resultPair.second);
+            mDelegateStateTracker.sipDelegateConnected(allowedTags, resultPair.second);
             return true;
         });
     }
diff --git a/src/com/android/services/telephony/rcs/SipSessionTracker.java b/src/com/android/services/telephony/rcs/SipSessionTracker.java
index 5ab482f..68e3065 100644
--- a/src/com/android/services/telephony/rcs/SipSessionTracker.java
+++ b/src/com/android/services/telephony/rcs/SipSessionTracker.java
@@ -24,6 +24,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.SipMessageParsingUtils;
+import com.android.internal.telephony.metrics.RcsStats;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.PrintWriter;
@@ -66,6 +67,14 @@
     // associated pending operation.
     private final ArrayMap<String, Runnable> mPendingAck = new ArrayMap<>();
 
+    private final RcsStats mRcsStats;
+    int mSubId;
+
+    public SipSessionTracker(int subId, RcsStats rcsStats) {
+        mSubId = subId;
+        mRcsStats = rcsStats;
+    }
+
     /**
      * Filter a SIP message to determine if it will result in a new SIP dialog. This will need to be
      * successfully acknowledged by the remote IMS stack using
@@ -73,10 +82,10 @@
      *
      * @param message The Incoming SIP message.
      */
-    public void filterSipMessage(SipMessage message) {
+    public void filterSipMessage(int direction, SipMessage message) {
         final Runnable r;
         if (startsEarlyDialog(message)) {
-            r = getCreateDialogRunnable(message);
+            r = getCreateDialogRunnable(direction, message);
         } else if (closesDialog(message)) {
             r = getCloseDialogRunnable(message);
         } else if (SipMessageParsingUtils.isSipResponse(message.getStartLine())) {
@@ -137,6 +146,8 @@
         if (dialogsToCleanup.isEmpty()) return;
         logi("Cleanup dialogs associated with call id: " + callId);
         for (SipDialog d : dialogsToCleanup) {
+            mRcsStats.onSipTransportSessionClosed(mSubId, callId, 0,
+                    d.getState() == d.STATE_CLOSED);
             d.close();
             logi("Dialog closed: " + d);
         }
@@ -197,6 +208,9 @@
      * Clears all tracked sessions.
      */
     public void clearAllSessions() {
+        for (SipDialog d : mTrackedDialogs) {
+            mRcsStats.onSipTransportSessionClosed(mSubId, d.getCallId(), 0, false);
+        }
         mTrackedDialogs.clear();
         mPendingAck.clear();
     }
@@ -262,7 +276,7 @@
         return SIP_CLOSE_DIALOG_REQUEST_METHOD.equalsIgnoreCase(startLineSegments[0]);
     }
 
-    private Runnable getCreateDialogRunnable(SipMessage m) {
+    private Runnable getCreateDialogRunnable(int direction, SipMessage m) {
         return () -> {
             List<SipDialog> duplicateDialogs = mTrackedDialogs.stream()
                     .filter(d -> d.getCallId().equals(m.getCallIdParameter()))
@@ -273,6 +287,10 @@
                 return;
             }
             SipDialog dialog = SipDialog.fromSipMessage(m);
+            String[] startLineSegments =
+                    SipMessageParsingUtils.splitStartLineAndVerify(m.getStartLine());
+            mRcsStats.earlySipTransportSession(startLineSegments[0], dialog.getCallId(),
+                    direction);
             logi("Starting new SipDialog: " + dialog);
             mTrackedDialogs.add(dialog);
         };
@@ -285,6 +303,7 @@
                     .collect(Collectors.toList());
             if (dialogsToClose.isEmpty()) return;
             logi("Closing dialogs associated with: " + m);
+            mRcsStats.onSipTransportSessionClosed(mSubId, m.getCallIdParameter(), 0, true);
             for (SipDialog d : dialogsToClose) {
                 d.close();
                 logi("Dialog closed: " + d);
@@ -344,11 +363,13 @@
         if (statusCode <= 100) return;
         // If 300+, then this dialog has received an error response and should move to closed state.
         if (statusCode >= 300) {
+            mRcsStats.onSipTransportSessionClosed(mSubId, m.getCallIdParameter(), statusCode, true);
             d.close();
             return;
         }
         if (toTag == null) logw("updateSipDialogState: No to tag for message: " + m);
         if (statusCode >= 200) {
+            mRcsStats.confirmedSipTransportSession(m.getCallIdParameter(), statusCode);
             d.confirm(toTag);
             return;
         }
diff --git a/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java b/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java
index 777026c..3b1b26d 100644
--- a/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java
+++ b/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java
@@ -16,6 +16,9 @@
 
 package com.android.services.telephony.rcs;
 
+import static com.android.internal.telephony.TelephonyStatsLog.SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__INCOMING;
+import static com.android.internal.telephony.TelephonyStatsLog.SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING;
+
 import android.telephony.ims.DelegateRegistrationState;
 import android.telephony.ims.FeatureTagState;
 import android.telephony.ims.SipDelegateConfiguration;
@@ -26,6 +29,8 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.SipMessageParsingUtils;
+import com.android.internal.telephony.metrics.RcsStats;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.services.telephony.rcs.validator.IncomingTransportStateValidator;
 import com.android.services.telephony.rcs.validator.MalformedSipMessageValidator;
@@ -145,11 +150,13 @@
     private PendingTask mPendingClose;
     private PendingRegCleanupTask mPendingRegCleanup;
     private Consumer<Set<String>> mRegistrationAppliedConsumer;
+    private final RcsStats mRcsStats;
 
     public TransportSipMessageValidator(int subId, ScheduledExecutorService executor) {
         mSubId = subId;
         mExecutor = executor;
-        mSipSessionTracker = new SipSessionTracker();
+        mRcsStats = RcsStats.getInstance();
+        mSipSessionTracker = new SipSessionTracker(subId, mRcsStats);
         mOutgoingTransportStateValidator = new OutgoingTransportStateValidator(mSipSessionTracker);
         mIncomingTransportStateValidator = new IncomingTransportStateValidator();
         mOutgoingMessageValidator = new MalformedSipMessageValidator().andThen(
@@ -163,7 +170,7 @@
     public TransportSipMessageValidator(int subId, ScheduledExecutorService executor,
             SipSessionTracker sipSessionTracker,
             OutgoingTransportStateValidator outgoingStateValidator,
-            IncomingTransportStateValidator incomingStateValidator) {
+            IncomingTransportStateValidator incomingStateValidator, RcsStats rcsStats) {
         mSubId = subId;
         mExecutor = executor;
         mSipSessionTracker = sipSessionTracker;
@@ -171,6 +178,7 @@
         mIncomingTransportStateValidator = incomingStateValidator;
         mOutgoingMessageValidator = mOutgoingTransportStateValidator;
         mIncomingMessageValidator = mIncomingTransportStateValidator;
+        mRcsStats = rcsStats;
     }
 
     /**
@@ -365,7 +373,11 @@
         }
         ValidationResult result = mOutgoingMessageValidator.validate(message);
         logi("verifyOutgoingMessage: " + result + ", message=" + message);
-        if (result.isValidated) mSipSessionTracker.filterSipMessage(message);
+        if (result.isValidated) {
+            mSipSessionTracker.filterSipMessage(
+                    SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING, message);
+        }
+        updateForMetrics(SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING, message, result);
         return result;
     }
 
@@ -378,7 +390,11 @@
     public ValidationResult verifyIncomingMessage(SipMessage message) {
         ValidationResult result = mIncomingMessageValidator.validate(message);
         logi("verifyIncomingMessage: " + result + ", message=" + message);
-        if (result.isValidated) mSipSessionTracker.filterSipMessage(message);
+        if (result.isValidated) {
+            mSipSessionTracker.filterSipMessage(
+                    SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__INCOMING, message);
+        }
+        updateForMetrics(SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__INCOMING, message, result);
         return result;
     }
 
@@ -539,6 +555,28 @@
                 .collect(Collectors.toSet());
     }
 
+    private void updateForMetrics(int direction, SipMessage m, ValidationResult result) {
+        String[] startLineSegments = SipMessageParsingUtils
+                .splitStartLineAndVerify(m.getStartLine());
+        if (SipMessageParsingUtils.isSipRequest(m.getStartLine())) {
+            if (result.isValidated) {
+                // SipMessage add to list for Metrics stats
+                mRcsStats.onSipMessageRequest(m.getCallIdParameter(), startLineSegments[0],
+                        direction);
+            } else {
+                //Message sending fail and there is no response.
+                mRcsStats.invalidatedMessageResult(mSubId, startLineSegments[0], direction,
+                        result.restrictedReason);
+            }
+        } else if (SipMessageParsingUtils.isSipResponse(m.getStartLine())) {
+            int statusCode = Integer.parseInt(startLineSegments[1]);
+            mRcsStats.onSipMessageResponse(mSubId, m.getCallIdParameter(), statusCode,
+                    result.restrictedReason);
+        } else {
+            logw("Message is Restricted");
+        }
+    }
+
     private void logi(String log) {
         Log.i(SipTransportController.LOG_TAG, LOG_TAG + "[" + mSubId + "] " + log);
         mLocalLog.log("[I] " + log);
diff --git a/tests/src/com/android/services/telephony/rcs/DelegateStateTrackerTest.java b/tests/src/com/android/services/telephony/rcs/DelegateStateTrackerTest.java
index 8236f44..25b5339 100644
--- a/tests/src/com/android/services/telephony/rcs/DelegateStateTrackerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/DelegateStateTrackerTest.java
@@ -37,6 +37,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.TelephonyTestBase;
+import com.android.internal.telephony.metrics.RcsStats;
 
 import org.junit.After;
 import org.junit.Before;
@@ -56,6 +57,7 @@
 
     @Mock private ISipDelegate mSipDelegate;
     @Mock private ISipDelegateConnectionStateCallback mAppCallback;
+    @Mock private RcsStats mRcsStats;
 
     @Before
     public void setUp() throws Exception {
@@ -76,12 +78,14 @@
     @Test
     public void testDelegateCreated() throws Exception {
         DelegateStateTracker stateTracker = new DelegateStateTracker(TEST_SUB_ID, mAppCallback,
-                mSipDelegate);
+                mSipDelegate, mRcsStats);
         Set<FeatureTagState> deniedTags = getMmTelDeniedTag();
-        stateTracker.sipDelegateConnected(deniedTags);
+        Set<String> supportedTags = getSupportedTags();
+        stateTracker.sipDelegateConnected(supportedTags, deniedTags);
         // Calling connected multiple times should not generate multiple onCreated events.
-        stateTracker.sipDelegateConnected(deniedTags);
+        stateTracker.sipDelegateConnected(supportedTags, deniedTags);
         verify(mAppCallback).onCreated(mSipDelegate);
+        verify(mRcsStats).createSipDelegateStats(TEST_SUB_ID, supportedTags);
 
         // Ensure status updates are sent to app as expected.
         DelegateRegistrationState regState = new DelegateRegistrationState.Builder()
@@ -97,8 +101,10 @@
         stateTracker.onConfigurationChanged(c);
         verify(mAppCallback).onFeatureTagStatusChanged(eq(regState),
                 eq(new ArrayList<>(deniedTags)));
+        verify(mRcsStats).onSipTransportFeatureTagStats(TEST_SUB_ID, deniedTags,
+                regState.getDeregisteredFeatureTags(),
+                regState.getRegisteredFeatureTags());
         verify(mAppCallback).onConfigurationChanged(c);
-
         verify(mAppCallback, never()).onDestroyed(anyInt());
     }
 
@@ -109,14 +115,18 @@
     @Test
     public void testDelegateDestroyed() throws Exception {
         DelegateStateTracker stateTracker = new DelegateStateTracker(TEST_SUB_ID, mAppCallback,
-                mSipDelegate);
+                mSipDelegate, mRcsStats);
         Set<FeatureTagState> deniedTags = getMmTelDeniedTag();
-        stateTracker.sipDelegateConnected(deniedTags);
+        Set<String> supportedTags = getSupportedTags();
+        stateTracker.sipDelegateConnected(supportedTags, deniedTags);
+        verify(mRcsStats).createSipDelegateStats(eq(TEST_SUB_ID), eq(supportedTags));
 
         stateTracker.sipDelegateDestroyed(
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
         verify(mAppCallback).onDestroyed(
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        verify(mRcsStats).onSipDelegateStats(eq(TEST_SUB_ID), eq(supportedTags),
+                eq(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP));
     }
 
     /**
@@ -130,9 +140,10 @@
     @Test
     public void testDelegateChangingRegisteredTagsOverride() throws Exception {
         DelegateStateTracker stateTracker = new DelegateStateTracker(TEST_SUB_ID, mAppCallback,
-                mSipDelegate);
+                mSipDelegate, mRcsStats);
         Set<FeatureTagState> deniedTags = getMmTelDeniedTag();
-        stateTracker.sipDelegateConnected(deniedTags);
+        Set<String> supportedTags = getSupportedTags();
+        stateTracker.sipDelegateConnected(supportedTags, deniedTags);
         // SipDelegate created
         verify(mAppCallback).onCreated(mSipDelegate);
         DelegateRegistrationState regState = new DelegateRegistrationState.Builder()
@@ -158,7 +169,7 @@
                         DelegateRegistrationState.DEREGISTERED_REASON_NOT_PROVISIONED)
                 .build();
         // new underlying SipDelegate created
-        stateTracker.sipDelegateConnected(deniedTags);
+        stateTracker.sipDelegateConnected(supportedTags, deniedTags);
         stateTracker.onRegistrationStateChanged(regState);
 
         // Verify registration state through the process:
@@ -187,9 +198,10 @@
     @Test
     public void testDelegateChangingDeniedTagsChanged() throws Exception {
         DelegateStateTracker stateTracker = new DelegateStateTracker(TEST_SUB_ID, mAppCallback,
-                mSipDelegate);
+                mSipDelegate, mRcsStats);
         Set<FeatureTagState> deniedTags = getMmTelDeniedTag();
-        stateTracker.sipDelegateConnected(deniedTags);
+        Set<String> supportedTags = getSupportedTags();
+        stateTracker.sipDelegateConnected(supportedTags, deniedTags);
         // SipDelegate created
         verify(mAppCallback).onCreated(mSipDelegate);
         DelegateRegistrationState regState = new DelegateRegistrationState.Builder()
@@ -220,7 +232,7 @@
         // new underlying SipDelegate created, but SipDelegate denied one to one chat
         deniedTags.add(new FeatureTagState(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG,
                 SipDelegateManager.DENIED_REASON_NOT_ALLOWED));
-        stateTracker.sipDelegateConnected(deniedTags);
+        stateTracker.sipDelegateConnected(supportedTags, deniedTags);
         DelegateRegistrationState fullyDeniedRegState = new DelegateRegistrationState.Builder()
                 .build();
         // In this special case, it will be the SipDelegateConnectionBase that will trigger
@@ -244,9 +256,10 @@
     @Test
     public void testDelegateChangingDeniedTagsChangingToDestroy() throws Exception {
         DelegateStateTracker stateTracker = new DelegateStateTracker(TEST_SUB_ID, mAppCallback,
-                mSipDelegate);
+                mSipDelegate, mRcsStats);
         Set<FeatureTagState> deniedTags = getMmTelDeniedTag();
-        stateTracker.sipDelegateConnected(deniedTags);
+        Set<String> supportedTags = getSupportedTags();
+        stateTracker.sipDelegateConnected(supportedTags, deniedTags);
         // SipDelegate created
         verify(mAppCallback).onCreated(mSipDelegate);
         DelegateRegistrationState regState = new DelegateRegistrationState.Builder()
@@ -296,4 +309,11 @@
                 SipDelegateManager.DENIED_REASON_NOT_ALLOWED));
         return deniedTags;
     }
+
+    private Set<String> getSupportedTags() {
+        Set<String> supportedTags = new ArraySet<>();
+        supportedTags.add(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG);
+        supportedTags.add(ImsSignallingUtils.GROUP_CHAT_TAG);
+        return supportedTags;
+    }
 }
diff --git a/tests/src/com/android/services/telephony/rcs/SipDelegateControllerTest.java b/tests/src/com/android/services/telephony/rcs/SipDelegateControllerTest.java
index 5b0e7c5..78f6894 100644
--- a/tests/src/com/android/services/telephony/rcs/SipDelegateControllerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/SipDelegateControllerTest.java
@@ -107,7 +107,8 @@
         assertTrue(future.get());
         verify(mMockMessageTracker).openTransport(mMockSipDelegate, request.getFeatureTags(),
                 Collections.emptySet());
-        verify(mMockDelegateStateTracker).sipDelegateConnected(Collections.emptySet());
+        verify(mMockDelegateStateTracker).sipDelegateConnected(request.getFeatureTags(),
+                Collections.emptySet());
     }
 
     @SmallTest
@@ -138,7 +139,7 @@
         allowedTags.removeAll(deniedTags.stream().map(FeatureTagState::getFeatureTag)
                 .collect(Collectors.toSet()));
         verify(mMockMessageTracker).openTransport(mMockSipDelegate, allowedTags, deniedTags);
-        verify(mMockDelegateStateTracker).sipDelegateConnected(deniedTags);
+        verify(mMockDelegateStateTracker).sipDelegateConnected(allowedTags, deniedTags);
     }
 
     @SmallTest
@@ -249,7 +250,9 @@
                 Collections.emptySet());
         verify(mMockMessageTracker).openTransport(mMockSipDelegate, newFts,
                 Collections.emptySet());
-        verify(mMockDelegateStateTracker, times(2)).sipDelegateConnected(Collections.emptySet());
+        verify(mMockDelegateStateTracker).sipDelegateConnected(
+                request.getFeatureTags(), Collections.emptySet());
+        verify(mMockDelegateStateTracker).sipDelegateConnected(newFts, Collections.emptySet());
     }
 
     private void createSipDelegate(DelegateRequest request, SipDelegateController controller)
diff --git a/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java b/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java
index 823a8be..37abb83 100644
--- a/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java
@@ -16,19 +16,28 @@
 
 package com.android.services.telephony.rcs;
 
+import static com.android.internal.telephony.TelephonyStatsLog.SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+
 import android.net.Uri;
 import android.telephony.ims.SipMessage;
 import android.util.Base64;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.internal.telephony.metrics.RcsStats;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.nio.ByteBuffer;
 import java.util.Arrays;
@@ -90,11 +99,104 @@
     // Keep track of the string entry so we can generate unique strings.
     private int mStringEntryCounter = 0;
     private SipSessionTracker mTrackerUT;
+    private static final int TEST_SUB_ID = 1;
+    private static final String TEST_INVITE_SIP_METHOD = "INVITE";
+    private static final int TEST_SIP_RESPONSE_CODE = 200;
+    private static final int TEST_SIP_CLOSE_RESPONSE_CODE = 0;
+    @Mock
+    private RcsStats mRcsStats;
 
     @Before
     public void setUp() {
         mStringEntryCounter = 0;
-        mTrackerUT = new SipSessionTracker();
+        MockitoAnnotations.initMocks(this);
+        mTrackerUT = new SipSessionTracker(TEST_SUB_ID, mRcsStats);
+    }
+
+    @Test
+    public void testMetricsEndedGracefullyBye() {
+        DialogAttributes attr = new DialogAttributes();
+        // INVITE
+        SipMessage inviteRequest = generateSipRequest(SipMessageUtils.INVITE_SIP_METHOD, attr);
+        filterMessage(inviteRequest, attr);
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getEarlyDialogs(), attr);
+
+        // confirmed dialog
+        attr.setToTag();
+        SipMessage inviteConfirm = generateSipResponse("200", "OK", attr);
+        filterMessage(inviteConfirm, attr);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr);
+
+        // Gracefully Ended
+        SipMessage inviteClose = generateSipRequest(SipMessageUtils.BYE_SIP_METHOD, attr);
+        filterMessage(inviteClose, attr);
+
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getClosedDialogs(), attr);
+
+        // verify Metrics information
+        verify(mRcsStats).onSipTransportSessionClosed(eq(TEST_SUB_ID), eq(attr.callId),
+                eq(TEST_SIP_CLOSE_RESPONSE_CODE), eq(true));
+    }
+
+    @Test
+    public void testMetricsCloseCleanupSession() {
+        //mTrackerUT.setRcsStats(mRcsStats);
+        DialogAttributes attr = new DialogAttributes();
+        // INVITE A -> B
+        SipMessage inviteRequest = generateSipRequest(SipMessageUtils.INVITE_SIP_METHOD, attr);
+        filterMessage(inviteRequest, attr);
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getEarlyDialogs(), attr);
+
+        // confirmed dialog
+        attr.setToTag();
+        SipMessage inviteConfirm = generateSipResponse("200", "OK", attr);
+        filterMessage(inviteConfirm, attr);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr);
+
+        //forcefully close session
+        mTrackerUT.cleanupSession(attr.callId);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        assertTrue(mTrackerUT.getClosedDialogs().isEmpty());
+
+        // verify Metrics information
+        verify(mRcsStats).onSipTransportSessionClosed(eq(TEST_SUB_ID), eq(attr.callId),
+                eq(TEST_SIP_CLOSE_RESPONSE_CODE), eq(false));
+    }
+
+    @Test
+    public void testMetricsCloseClearAllSessions() {
+        //mTrackerUT.setRcsStats(mRcsStats);
+        DialogAttributes attr = new DialogAttributes();
+
+        // INVITE
+        SipMessage inviteRequest = generateSipRequest(SipMessageUtils.INVITE_SIP_METHOD, attr);
+        filterMessage(inviteRequest, attr);
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getEarlyDialogs(), attr);
+
+        // confirmed dialog
+        attr.setToTag();
+        SipMessage inviteConfirm = generateSipResponse("200", "OK", attr);
+        filterMessage(inviteConfirm, attr);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr);
+
+        //forcefully close session
+        mTrackerUT.clearAllSessions();
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        assertTrue(mTrackerUT.getClosedDialogs().isEmpty());
+
+        // verify Metrics information
+        verify(mRcsStats).onSipTransportSessionClosed(eq(TEST_SUB_ID), eq(attr.callId),
+                eq(TEST_SIP_CLOSE_RESPONSE_CODE), eq(false));
     }
 
     @Test
@@ -291,7 +393,8 @@
     public void testAcknowledgeMessageFailed() {
         DialogAttributes attr = new DialogAttributes();
         SipMessage inviteRequest = generateSipRequest(SipMessageUtils.INVITE_SIP_METHOD, attr);
-        mTrackerUT.filterSipMessage(inviteRequest);
+        mTrackerUT.filterSipMessage(
+                SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING, inviteRequest);
         // Do not acknowledge the request and ensure that the operation has not been applied yet.
         assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
         assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
@@ -310,8 +413,10 @@
         // We unexpectedly received two filter requests for the same branchId without
         // acknowledgePendingMessage being called in between. Ensure that when it is called, it
         // applies both operations.
-        mTrackerUT.filterSipMessage(inviteRequest);
-        mTrackerUT.filterSipMessage(inviteConfirm);
+        mTrackerUT.filterSipMessage(
+                SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING, inviteRequest);
+        mTrackerUT.filterSipMessage(
+                SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING, inviteConfirm);
         assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
         assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
         // we should skip right to confirmed as both operations run back-to-back
@@ -321,7 +426,8 @@
     }
 
     private void filterMessage(SipMessage m, DialogAttributes attr) {
-        mTrackerUT.filterSipMessage(m);
+        mTrackerUT.filterSipMessage(
+                SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING, m);
         mTrackerUT.acknowledgePendingMessage(attr.branchId);
     }
     private void verifyContainsCallIds(Set<SipDialog> callIdSet, DialogAttributes... attrs) {
diff --git a/tests/src/com/android/services/telephony/rcs/TransportSipMessageValidatorTest.java b/tests/src/com/android/services/telephony/rcs/TransportSipMessageValidatorTest.java
index 4d222e3..0fe897f 100644
--- a/tests/src/com/android/services/telephony/rcs/TransportSipMessageValidatorTest.java
+++ b/tests/src/com/android/services/telephony/rcs/TransportSipMessageValidatorTest.java
@@ -16,6 +16,9 @@
 
 package com.android.services.telephony.rcs;
 
+import static com.android.internal.telephony.TelephonyStatsLog.SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__INCOMING;
+import static com.android.internal.telephony.TelephonyStatsLog.SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -23,6 +26,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -37,6 +41,7 @@
 
 import com.android.TelephonyTestBase;
 import com.android.TestExecutorService;
+import com.android.internal.telephony.metrics.RcsStats;
 import com.android.services.telephony.rcs.validator.IncomingTransportStateValidator;
 import com.android.services.telephony.rcs.validator.OutgoingTransportStateValidator;
 import com.android.services.telephony.rcs.validator.ValidationResult;
@@ -76,6 +81,8 @@
     private IncomingTransportStateValidator mIncomingStateValidator;
     @Mock
     private OutgoingTransportStateValidator mOutgoingStateValidator;
+    @Mock
+    private RcsStats mRcsStats;
 
     @Before
     public void setUp() throws Exception {
@@ -117,12 +124,50 @@
     }
 
     @Test
+    public void testMetricsResponse() {
+        String testSipInviteMethod = "INVITE";
+        String testSipMessageMethod = "MESSAGE";
+        int testSipResponseCode = 200;
+        int testMessageError = 0;
+
+        TestExecutorService executor = new TestExecutorService();
+        TransportSipMessageValidator tracker = openTransport(executor);
+        // Since the incoming/outgoing messages were verified, there should have been two calls
+        // to filter the message.
+        verify(mSipSessionTracker).filterSipMessage(
+                SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING, TEST_MESSAGE);
+        verify(mSipSessionTracker).filterSipMessage(
+                SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__INCOMING, TEST_MESSAGE);
+
+        assertTrue(tracker.verifyOutgoingMessage(generateSipRequest("INVITE",
+                "testId1"), TEST_CONFIG_VERSION).isValidated);
+        verify(mRcsStats).onSipMessageRequest(eq("testId1"),
+                eq(testSipInviteMethod),
+                eq(SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING));
+
+        assertTrue(tracker.verifyOutgoingMessage(generateSipRequest("MESSAGE",
+                "testId2"), TEST_CONFIG_VERSION).isValidated);
+        verify(mRcsStats).onSipMessageRequest(eq("testId2"),
+                eq(testSipMessageMethod),
+                eq(SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING));
+
+        assertTrue(tracker.verifyIncomingMessage(
+                 generateSipResponse("200", "OK", "testId2"))
+                 .isValidated);
+        verify(mRcsStats).onSipMessageResponse(eq(TEST_SUB_ID), eq("testId2"),
+                eq(testSipResponseCode), eq(testMessageError));
+    }
+
+    @Test
     public void testSessionTrackerFiltering() {
         TestExecutorService executor = new TestExecutorService();
         TransportSipMessageValidator tracker = openTransport(executor);
         // Since the incoming/outgoing messages were verified, there should have been two calls
         // to filter the message.
-        verify(mSipSessionTracker, times(2)).filterSipMessage(TEST_MESSAGE);
+        verify(mSipSessionTracker).filterSipMessage(
+                SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING, TEST_MESSAGE);
+        verify(mSipSessionTracker).filterSipMessage(
+                SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__INCOMING, TEST_MESSAGE);
         // ensure pass through methods are working
         tracker.acknowledgePendingMessage("abc");
         verify(mSipSessionTracker).acknowledgePendingMessage("abc");
@@ -140,7 +185,10 @@
         assertFalse(tracker.verifyOutgoingMessage(TEST_MESSAGE, TEST_CONFIG_VERSION).isValidated);
         // The number of times the filter method was called should still only be two after these
         // messages were not validated.
-        verify(mSipSessionTracker, times(2)).filterSipMessage(TEST_MESSAGE);
+        verify(mSipSessionTracker).filterSipMessage(
+                SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING, TEST_MESSAGE);
+        verify(mSipSessionTracker).filterSipMessage(
+                SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__INCOMING, TEST_MESSAGE);
     }
 
 
@@ -482,6 +530,27 @@
         doReturn(ValidationResult.SUCCESS).when(mIncomingStateValidator).validate(any());
         doReturn(mIncomingStateValidator).when(mIncomingStateValidator).andThen(any());
         return new TransportSipMessageValidator(TEST_SUB_ID, executor, mSipSessionTracker,
-                mOutgoingStateValidator, mIncomingStateValidator);
+                mOutgoingStateValidator, mIncomingStateValidator, mRcsStats);
+    }
+
+    private SipMessage generateSipResponse(String statusCode, String statusString, String callId) {
+        String fromHeader = "Alice <sip:alice@atlanta.com>;tag=1928301774";
+        String toHeader = "Bob <sip:bob@biloxi.com>";
+        String branchId = "AAAA";
+        String fromTag = "tag=1928301774";
+        String toTag = "";
+        return SipMessageUtils.generateSipResponse(statusCode, statusString, fromHeader,
+            toHeader, branchId, callId, fromTag, toTag);
+    }
+
+    private SipMessage generateSipRequest(String requestMethod, String callId) {
+        String fromHeader = "Alice <sip:alice@atlanta.com>;tag=1928301774";
+        String toHeader = "Bob <sip:bob@biloxi.com>";
+        String branchId = "AAAA";
+        String fromTag = "tag=1928301774";
+        String toTag = "";
+        String toUri = "sip:bob@biloxi.com";
+        return SipMessageUtils.generateSipRequest(requestMethod, fromHeader, toHeader,
+                toUri, branchId, callId, fromTag, toTag);
     }
 }