DataCallSessionStats captures Cross-SIM calling

- This revert^2s commit 6c57c33cce121bf3f198752a165409a23fffe6ad,
- patches the fix on top of this commit, and
- gates the new functionality behind a feature flag.

FIX DESCRIPTION:
- The DefaultNetworkMonitor singleton prevents the memory leak bug with
 multiple default network callback registrations, one per DataNetwork.
- DataCallSessionStats gets a reference to DefaultNetworkMonitor
 on creation, to determine when a PDN is established on IWLAN, whether
 it is WiFi Calling or Backup Calling.

Test: Updated DataCallSessionStatsTest, Live test with flag enabled to
 verify data_call_session proto sets 'iwlan_cross_sim' during Backup
 Calling. Verified no functional regression when flag is both enabled
 and disabled.

Bug: 313956117
Change-Id: I869165a1fecf18a2eeaab18bd3a0b639d1fc8e1b
diff --git a/flags/data.aconfig b/flags/data.aconfig
index a74d279..daddc2e 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -97,3 +97,10 @@
   description: "Ignore existing networks when checking if internet is allowed"
   bug: "284420611"
 }
+
+flag {
+  name: "data_call_session_stats_captures_cross_sim_calling"
+  namespace: "telephony"
+  description: "The DataCallSessionStats metrics will capture whether the IWLAN PDN is set up on cross-SIM calling."
+  bug: "313956117"
+}
diff --git a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
index afb87dd..e1f6309 100644
--- a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
@@ -19,6 +19,7 @@
 import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__IP_TYPE__APN_PROTOCOL_IPV4;
 
 import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
 import android.os.SystemClock;
 import android.telephony.Annotation.ApnType;
 import android.telephony.Annotation.DataFailureCause;
@@ -62,8 +63,16 @@
 
     public static final int SIZE_LIMIT_HANDOVER_FAILURES = 15;
 
+    private final DefaultNetworkMonitor mDefaultNetworkMonitor;
+
     public DataCallSessionStats(Phone phone) {
         mPhone = phone;
+        mDefaultNetworkMonitor = PhoneFactory.getMetricsCollector().getDefaultNetworkMonitor();
+    }
+
+    private boolean isSystemDefaultNetworkMobile() {
+        NetworkCapabilities nc = mDefaultNetworkMonitor.getNetworkCapabilities();
+        return nc != null && nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
     }
 
     /** Creates a new ongoing atom when data call is set up. */
@@ -101,6 +110,9 @@
                     (currentRat == TelephonyManager.NETWORK_TYPE_IWLAN)
                             ? 0
                             : ServiceStateStats.getBand(mPhone);
+            // Limitation: Will not capture IKE mobility between Backup Calling <-> WiFi Calling.
+            mDataCallSession.isIwlanCrossSim = currentRat == TelephonyManager.NETWORK_TYPE_IWLAN
+                    && isSystemDefaultNetworkMobile();
         }
 
         // only set if apn hasn't been set during setup
@@ -199,6 +211,8 @@
             if (mDataCallSession.ratAtEnd != currentRat) {
                 mDataCallSession.ratSwitchCount++;
                 mDataCallSession.ratAtEnd = currentRat;
+                mDataCallSession.isIwlanCrossSim = currentRat == TelephonyManager.NETWORK_TYPE_IWLAN
+                        && isSystemDefaultNetworkMobile();
             }
             // band may have changed even if RAT was the same
             mDataCallSession.bandAtEnd =
@@ -288,6 +302,7 @@
         copy.handoverFailureRat = Arrays.copyOf(call.handoverFailureRat,
                 call.handoverFailureRat.length);
         copy.isNonDds = call.isNonDds;
+        copy.isIwlanCrossSim = call.isIwlanCrossSim;
         return copy;
     }
 
@@ -313,6 +328,7 @@
         proto.handoverFailureCauses = new int[0];
         proto.handoverFailureRat = new int[0];
         proto.isNonDds = false;
+        proto.isIwlanCrossSim = false;
         return proto;
     }
 
diff --git a/src/java/com/android/internal/telephony/metrics/DefaultNetworkMonitor.java b/src/java/com/android/internal/telephony/metrics/DefaultNetworkMonitor.java
new file mode 100644
index 0000000..1dab10b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/DefaultNetworkMonitor.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2023 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 android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.os.Handler;
+import android.os.Looper;
+
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.telephony.Rlog;
+
+/**
+ * Monitors the system default network registration and tracks the currently available
+ * {@link Network} and its {@link NetworkCapabilities}.
+ */
+public class DefaultNetworkMonitor extends Handler {
+
+    private static final String TAG = DefaultNetworkMonitor.class.getSimpleName();
+
+    @Nullable private NetworkCallback mDefaultNetworkCallback;
+    @Nullable private Network mNetwork;
+    @Nullable private NetworkCapabilities mNetworkCapabilities;
+
+    private final class DefaultNetworkCallback extends NetworkCallback {
+        @Override
+        public void onAvailable(@NonNull Network network) {
+            mNetwork = network;
+        }
+
+        @Override
+        public void onCapabilitiesChanged(@NonNull Network network,
+                @NonNull NetworkCapabilities nc) {
+            if (network == mNetwork) {
+                mNetworkCapabilities = nc;
+            }
+        }
+
+        @Override
+        public void onLost(@NonNull Network network) {
+            mNetwork = null;
+            mNetworkCapabilities = null;
+        }
+    }
+
+    DefaultNetworkMonitor(@NonNull Context context, @NonNull FeatureFlags featureFlags) {
+        super(Looper.myLooper());
+        if (featureFlags.dataCallSessionStatsCapturesCrossSimCalling()) {
+            registerSystemDefaultNetworkCallback(context);
+        }
+    }
+
+    private void registerSystemDefaultNetworkCallback(@NonNull Context context) {
+        if (mDefaultNetworkCallback != null) {
+            return;
+        }
+        ConnectivityManager connectivityManager =
+                context.getSystemService(ConnectivityManager.class);
+        if (connectivityManager != null) {
+            mDefaultNetworkCallback = new DefaultNetworkCallback();
+            connectivityManager.registerSystemDefaultNetworkCallback(
+                    mDefaultNetworkCallback, this);
+        } else {
+            Rlog.e(TAG, "registerSystemDefaultNetworkCallback: ConnectivityManager is null!");
+        }
+    }
+
+    @Nullable public NetworkCapabilities getNetworkCapabilities() {
+        return mNetworkCapabilities;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index 8bd2547..da9bb54 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -171,19 +171,20 @@
     private final StatsManager mStatsManager;
     private final VonrHelper mVonrHelper;
     private final AirplaneModeStats mAirplaneModeStats;
+    private final DefaultNetworkMonitor mDefaultNetworkMonitor;
     private final Set<DataCallSessionStats> mOngoingDataCallStats = ConcurrentHashMap.newKeySet();
     private static final Random sRandom = new Random();
 
     public MetricsCollector(Context context, @NonNull FeatureFlags featureFlags) {
         this(context, new PersistAtomsStorage(context),
-                new DeviceStateHelper(context), new VonrHelper(featureFlags));
+                new DeviceStateHelper(context), new VonrHelper(featureFlags), featureFlags);
     }
 
     /** Allows dependency injection. Used during unit tests. */
     @VisibleForTesting
     public MetricsCollector(
             Context context, PersistAtomsStorage storage, DeviceStateHelper deviceStateHelper,
-                    VonrHelper vonrHelper) {
+                    VonrHelper vonrHelper, @NonNull FeatureFlags featureFlags) {
         mStorage = storage;
         mDeviceStateHelper = deviceStateHelper;
         mStatsManager = (StatsManager) context.getSystemService(Context.STATS_MANAGER);
@@ -232,6 +233,7 @@
         }
 
         mAirplaneModeStats = new AirplaneModeStats(context);
+        mDefaultNetworkMonitor = new DefaultNetworkMonitor(context, featureFlags);
     }
 
     /**
@@ -362,6 +364,10 @@
         mOngoingDataCallStats.remove(call);
     }
 
+    public DefaultNetworkMonitor getDefaultNetworkMonitor() {
+        return mDefaultNetworkMonitor;
+    }
+
     private void concludeDataCallSessionStats() {
         for (DataCallSessionStats stats : mOngoingDataCallStats) {
             stats.conclude();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 89e9075..eab52ef 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -115,6 +115,7 @@
 import com.android.internal.telephony.imsphone.ImsNrSaModeHandler;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
+import com.android.internal.telephony.metrics.DefaultNetworkMonitor;
 import com.android.internal.telephony.metrics.DeviceStateHelper;
 import com.android.internal.telephony.metrics.ImsStats;
 import com.android.internal.telephony.metrics.MetricsCollector;
@@ -265,6 +266,7 @@
     protected CarrierPrivilegesTracker mCarrierPrivilegesTracker;
     protected VoiceCallSessionStats mVoiceCallSessionStats;
     protected PersistAtomsStorage mPersistAtomsStorage;
+    protected DefaultNetworkMonitor mDefaultNetworkMonitor;
     protected MetricsCollector mMetricsCollector;
     protected SmsStats mSmsStats;
     protected TelephonyAnalytics mTelephonyAnalytics;
@@ -538,6 +540,7 @@
         mCarrierPrivilegesTracker = Mockito.mock(CarrierPrivilegesTracker.class);
         mVoiceCallSessionStats = Mockito.mock(VoiceCallSessionStats.class);
         mPersistAtomsStorage = Mockito.mock(PersistAtomsStorage.class);
+        mDefaultNetworkMonitor = Mockito.mock(DefaultNetworkMonitor.class);
         mMetricsCollector = Mockito.mock(MetricsCollector.class);
         mSmsStats = Mockito.mock(SmsStats.class);
         mTelephonyAnalytics = Mockito.mock(TelephonyAnalytics.class);
@@ -887,6 +890,7 @@
         // Metrics
         doReturn(null).when(mContext).getFileStreamPath(anyString());
         doReturn(mPersistAtomsStorage).when(mMetricsCollector).getAtomsStorage();
+        doReturn(mDefaultNetworkMonitor).when(mMetricsCollector).getDefaultNetworkMonitor();
         doReturn(mWifiManager).when(mContext).getSystemService(eq(Context.WIFI_SERVICE));
         doReturn(mDeviceStateHelper).when(mMetricsCollector).getDeviceStateHelper();
         doReturn(CELLULAR_SERVICE_STATE__FOLD_STATE__STATE_UNKNOWN)
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/DataCallSessionStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/DataCallSessionStatsTest.java
new file mode 100644
index 0000000..d63dc3c
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/DataCallSessionStatsTest.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2023 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.NetworkCapabilities;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.telephony.DataFailCause;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.DataCallResponse;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+public class DataCallSessionStatsTest extends TelephonyTest {
+
+    private DataCallResponse mDefaultImsResponse = buildDataCallResponse("ims", 0);
+    private static class TestableDataCallSessionStats extends DataCallSessionStats {
+        private long mTimeMillis = 0L;
+
+        TestableDataCallSessionStats(Phone phone) {
+            super(phone);
+        }
+
+        @Override
+        protected long getTimeMillis() {
+            return mTimeMillis;
+        }
+
+        private void setTimeMillis(long timeMillis) {
+            mTimeMillis = timeMillis;
+        }
+    }
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+    private TestableDataCallSessionStats mDataCallSessionStats;
+    private NetworkCapabilities mCellularNetworkCapabilities = new NetworkCapabilities.Builder()
+            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build();
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        when(mServiceState.getDataRegistrationState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        mDataCallSessionStats = new TestableDataCallSessionStats(mPhone);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mDataCallSessionStats = null;
+        super.tearDown();
+    }
+
+    private DataCallResponse buildDataCallResponse(String apn, long retryDurationMillis) {
+        return new DataCallResponse.Builder()
+                .setId(apn.hashCode())
+                .setRetryDurationMillis(retryDurationMillis)
+                .build();
+    }
+
+    @Test
+    @SmallTest
+    public void testSetupDataCallOnCellularIms_success() {
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_LTE,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NONE);
+
+        mDataCallSessionStats.setTimeMillis(60000L);
+        mDataCallSessionStats.conclude();
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(1, stats.durationMinutes);
+        assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.ratAtEnd);
+        assertTrue(stats.ongoing);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetupDataCallOnIwlan_success() {
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_IWLAN,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NONE);
+
+        mDataCallSessionStats.setTimeMillis(120000L);
+        mDataCallSessionStats.conclude();
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(2, stats.durationMinutes);
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
+        assertFalse(stats.isIwlanCrossSim);
+        assertTrue(stats.ongoing);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetupDataCallOnCrossSimCalling_success() {
+        doReturn(mCellularNetworkCapabilities)
+                .when(mDefaultNetworkMonitor).getNetworkCapabilities();
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_IWLAN,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NONE);
+
+        mDataCallSessionStats.setTimeMillis(60000L);
+        mDataCallSessionStats.conclude();
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(1, stats.durationMinutes);
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
+        assertTrue(stats.isIwlanCrossSim);
+        assertTrue(stats.ongoing);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetupDataCallOnCellularIms_failure() {
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_LTE,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NETWORK_FAILURE);
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(0, stats.durationMinutes);
+        assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.ratAtEnd);
+        assertFalse(stats.ongoing);
+    }
+
+    @Test
+    @SmallTest
+    public void testHandoverFromCellularToIwlan_success() {
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_LTE,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NONE);
+
+        mDataCallSessionStats.onDrsOrRatChanged(TelephonyManager.NETWORK_TYPE_IWLAN);
+        mDataCallSessionStats.conclude();
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
+        assertEquals(1, stats.ratSwitchCount);
+        assertTrue(stats.ongoing);
+    }
+
+    @Test
+    @SmallTest
+    public void testHandoverFromCellularToCrossSimCalling_success() {
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_LTE,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NONE);
+
+        doReturn(mCellularNetworkCapabilities)
+                .when(mDefaultNetworkMonitor).getNetworkCapabilities();
+        mDataCallSessionStats.onDrsOrRatChanged(TelephonyManager.NETWORK_TYPE_IWLAN);
+        mDataCallSessionStats.conclude();
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
+        assertEquals(1, stats.ratSwitchCount);
+        assertTrue(stats.isIwlanCrossSim);
+        assertTrue(stats.ongoing);
+    }
+
+    @Test
+    @SmallTest
+    public void testHandoverFromCellularToIwlan_failure() {
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_LTE,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NONE);
+
+        mDataCallSessionStats.onHandoverFailure(DataFailCause.IWLAN_DNS_RESOLUTION_TIMEOUT,
+                TelephonyManager.NETWORK_TYPE_LTE, TelephonyManager.NETWORK_TYPE_IWLAN);
+        mDataCallSessionStats.conclude();
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.ratAtEnd);
+        assertTrue(stats.ongoing);
+        assertEquals(DataFailCause.IWLAN_DNS_RESOLUTION_TIMEOUT,
+                stats.handoverFailureCauses[0]);
+
+        int cellularToIwlanFailureDirection = TelephonyManager.NETWORK_TYPE_LTE
+                | (TelephonyManager.NETWORK_TYPE_IWLAN << 16);
+        assertEquals(cellularToIwlanFailureDirection, stats.handoverFailureRat[0]);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetupDataCallOnIwlan_success_thenOOS() {
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_IWLAN,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NONE);
+        when(mServiceState.getDataRegistrationState())
+                .thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        mDataCallSessionStats.onDataCallDisconnected(DataFailCause.IWLAN_IKE_DPD_TIMEOUT);
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
+        assertTrue(stats.oosAtEnd);
+        assertFalse(stats.ongoing);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
index 0e1135e..07fc2d0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
@@ -43,6 +43,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
 import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms;
@@ -96,6 +97,7 @@
     private UiccPort mActivePort;
     private ServiceStateStats mServiceStateStats;
     private VonrHelper mVonrHelper;
+    private FeatureFlags mFeatureFlags;
 
     private MetricsCollector mMetricsCollector;
 
@@ -109,9 +111,10 @@
         mActivePort = mock(UiccPort.class);
         mServiceStateStats = mock(ServiceStateStats.class);
         mVonrHelper = mock(VonrHelper.class);
+        mFeatureFlags = mock(FeatureFlags.class);
         mMetricsCollector =
                 new MetricsCollector(mContext, mPersistAtomsStorage,
-                        mDeviceStateHelper, mVonrHelper);
+                        mDeviceStateHelper, mVonrHelper, mFeatureFlags);
         doReturn(mSST).when(mSecondPhone).getServiceStateTracker();
         doReturn(mServiceStateStats).when(mSST).getServiceStateStats();
     }