Merge tag 'android-13.0.0_r52' into int/13/fp3

Android 13.0.0 Release 52 (TQ3A.230605.012)

* tag 'android-13.0.0_r52':
  Split iwlan error classname and first line of stacktrace in metrics
  Add iwlan error classname and first line of stacktrace in metrics
  Add control for enable/disable N1_MODE_CAPABILITY
  Reset the error status once the tunnel is opened
  Start the throttling handling only if the tunnel fails to bring up

Change-Id: I0a014189802e2cdc5168b5c2ba4ddf1a0a0a7186
diff --git a/src/com/google/android/iwlan/IwlanDataService.java b/src/com/google/android/iwlan/IwlanDataService.java
index ba00b97..a87d6eb 100644
--- a/src/com/google/android/iwlan/IwlanDataService.java
+++ b/src/com/google/android/iwlan/IwlanDataService.java
@@ -921,6 +921,12 @@
         }
 
         @VisibleForTesting
+        @Nullable
+        public MetricsAtom getMetricsAtomByApn(String apnName) {
+            return mMetricsAtomForApn.get(apnName);
+        }
+
+        @VisibleForTesting
         public IwlanTunnelCallback getIwlanTunnelCallback() {
             return mIwlanTunnelCallback;
         }
@@ -1181,6 +1187,9 @@
                         // Record setup result for the Metrics
                         metricsAtom.setSetupRequestResult(DataServiceCallback.RESULT_SUCCESS);
                         metricsAtom.setIwlanError(iwlanError.getErrorType());
+
+                        metricsAtom.setIwlanErrorWrappedClassnameAndStack(iwlanError);
+
                         metricsAtom.setTunnelState(tunnelState.getState());
                         metricsAtom.setMessageId(
                                 IwlanStatsLog.IWLAN_SETUP_DATA_CALL_RESULT_REPORTED);
diff --git a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
index ca2586f..638b8ba 100644
--- a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
+++ b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
@@ -1523,6 +1523,8 @@
                                     (int) mEpdgServerSelectionDuration,
                                     (int) mIkeTunnelEstablishmentDuration);
 
+                    reportIwlanError(apnName, new IwlanError(IwlanError.NO_ERROR));
+
                     setIsEpdgAddressSelected(true);
                     mValidEpdgInfo.resetIndex();
                     printRequestQueue("EVENT_CHILD_SESSION_OPENED");
@@ -1563,10 +1565,12 @@
                         iface.close();
                     }
 
-                    if (tunnelConfig.isBackoffTimeValid()) {
-                        reportIwlanError(apnName, iwlanError, tunnelConfig.getBackoffTime());
-                    } else {
-                        reportIwlanError(apnName, iwlanError);
+                    if (!tunnelConfig.hasTunnelOpened()) {
+                        if (tunnelConfig.isBackoffTimeValid()) {
+                            reportIwlanError(apnName, iwlanError, tunnelConfig.getBackoffTime());
+                        } else {
+                            reportIwlanError(apnName, iwlanError);
+                        }
                     }
 
                     Log.d(TAG, "Tunnel Closed: " + iwlanError);
diff --git a/src/com/google/android/iwlan/proto/MetricsAtom.java b/src/com/google/android/iwlan/proto/MetricsAtom.java
index 33b0fcd..7ecf464 100644
--- a/src/com/google/android/iwlan/proto/MetricsAtom.java
+++ b/src/com/google/android/iwlan/proto/MetricsAtom.java
@@ -16,6 +16,10 @@
 
 package com.google.android.iwlan.proto;
 
+import android.net.ipsec.ike.exceptions.IkeIOException;
+import android.net.ipsec.ike.exceptions.IkeInternalException;
+
+import com.google.android.iwlan.IwlanError;
 import com.google.android.iwlan.IwlanStatsLog;
 
 public class MetricsAtom {
@@ -37,6 +41,8 @@
     private int mHandoverFailureMode;
     private int mRetryDurationMillis;
     private int mWifiSignalValue;
+    private String mIwlanErrorWrappedClassname;
+    private String mIwlanErrorWrappedStackFirstFrame;
 
     public void setMessageId(int messageId) {
         this.mMessageId = messageId;
@@ -110,6 +116,36 @@
         this.mWifiSignalValue = wifiSignalValue;
     }
 
+    public void setIwlanErrorWrappedClassnameAndStack(IwlanError iwlanError) {
+        Throwable iwlanErrorWrapped = iwlanError.getException();
+        if (iwlanErrorWrapped instanceof IkeInternalException
+                || iwlanErrorWrapped instanceof IkeIOException) {
+            iwlanErrorWrapped = iwlanErrorWrapped.getCause();
+        }
+
+        if (iwlanErrorWrapped == null) {
+            this.mIwlanErrorWrappedClassname = null;
+            this.mIwlanErrorWrappedStackFirstFrame = null;
+            return;
+        }
+
+        this.mIwlanErrorWrappedClassname = iwlanErrorWrapped.getClass().getCanonicalName();
+
+        StackTraceElement[] iwlanErrorWrappedStackTraceElements = iwlanErrorWrapped.getStackTrace();
+        this.mIwlanErrorWrappedStackFirstFrame =
+                iwlanErrorWrappedStackTraceElements.length != 0
+                        ? iwlanErrorWrappedStackTraceElements[0].toString()
+                        : null;
+    }
+
+    public String getIwlanErrorWrappedClassname() {
+        return mIwlanErrorWrappedClassname;
+    }
+
+    public String getIwlanErrorWrappedStackFirstFrame() {
+        return mIwlanErrorWrappedStackFirstFrame;
+    }
+
     public void sendMetricsData() {
         if (mMessageId == IwlanStatsLog.IWLAN_SETUP_DATA_CALL_RESULT_REPORTED) {
             IwlanStatsLog.write(
@@ -129,7 +165,9 @@
                     mIkeTunnelEstablishmentDurationMillis,
                     mTunnelState,
                     mHandoverFailureMode,
-                    mRetryDurationMillis);
+                    mRetryDurationMillis,
+                    mIwlanErrorWrappedClassname,
+                    mIwlanErrorWrappedStackFirstFrame);
             return;
         } else if (mMessageId == IwlanStatsLog.IWLAN_PDN_DISCONNECTED_REASON_REPORTED) {
             IwlanStatsLog.write(
diff --git a/test/com/google/android/iwlan/IwlanDataServiceTest.java b/test/com/google/android/iwlan/IwlanDataServiceTest.java
index f67b384..ee416d4 100644
--- a/test/com/google/android/iwlan/IwlanDataServiceTest.java
+++ b/test/com/google/android/iwlan/IwlanDataServiceTest.java
@@ -31,6 +31,7 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.exceptions.IkeInternalException;
 import android.os.test.TestLooper;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.DataFailCause;
@@ -56,6 +57,7 @@
 import com.google.android.iwlan.epdg.TunnelLinkProperties;
 import com.google.android.iwlan.epdg.TunnelLinkPropertiesTest;
 import com.google.android.iwlan.epdg.TunnelSetupRequest;
+import com.google.android.iwlan.proto.MetricsAtom;
 
 import org.junit.After;
 import org.junit.Before;
@@ -938,6 +940,114 @@
         mTestLooper.dispatchAll();
     }
 
+    @Test
+    public void testMetricsWhenTunnelClosedWithWrappedException() {
+        DataProfile dp = buildDataProfile();
+
+        mSpyIwlanDataServiceProvider.setTunnelState(
+                dp,
+                mMockDataServiceCallback,
+                TunnelState.TUNNEL_IN_BRINGUP,
+                null, /* linkProperties */
+                false /* isHandover */,
+                1 /* pduSessionId */);
+
+        mSpyIwlanDataServiceProvider.setMetricsAtom(
+                TEST_APN_NAME,
+                64, // type IMS
+                true,
+                13, // LTE
+                false,
+                true,
+                1 // Transport Wi-Fi
+                );
+
+        MetricsAtom metricsAtom = mSpyIwlanDataServiceProvider.getMetricsAtomByApn(TEST_APN_NAME);
+        assertNotNull(metricsAtom);
+
+        String exceptionMessage = "Some exception message";
+        Exception mockException = spy(new IllegalStateException(exceptionMessage));
+        String firstDeclaringClassName = "test.test.TestClass";
+        String firstMethodName = "someMethod";
+        String firstFileName = "TestClass.java";
+        int firstLineNumber = 12345;
+        StackTraceElement[] stackTraceElements = {
+            new StackTraceElement(
+                    firstDeclaringClassName, firstMethodName, firstFileName, firstLineNumber),
+            new StackTraceElement("test", "test", "test.java", 123)
+        };
+        doReturn(stackTraceElements).when(mockException).getStackTrace();
+
+        when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
+                .thenReturn(mMockErrorPolicyManager);
+        when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
+                .thenReturn(DataFailCause.ERROR_UNSPECIFIED);
+
+        mSpyIwlanDataServiceProvider
+                .getIwlanTunnelCallback()
+                .onClosed(TEST_APN_NAME, new IwlanError(new IkeInternalException(mockException)));
+
+        mTestLooper.dispatchAll();
+
+        var expectedStackFirstFrame =
+                firstDeclaringClassName
+                        + "."
+                        + firstMethodName
+                        + "("
+                        + firstFileName
+                        + ":"
+                        + firstLineNumber
+                        + ")";
+
+        assertEquals(
+                mockException.getClass().getCanonicalName(),
+                metricsAtom.getIwlanErrorWrappedClassname());
+
+        assertEquals(expectedStackFirstFrame, metricsAtom.getIwlanErrorWrappedStackFirstFrame());
+    }
+
+    @Test
+    public void testMetricsWhenTunnelClosedWithoutWrappedException() {
+        DataProfile dp = buildDataProfile();
+
+        mSpyIwlanDataServiceProvider.setTunnelState(
+                dp,
+                mMockDataServiceCallback,
+                TunnelState.TUNNEL_IN_BRINGUP,
+                null, /* linkProperties */
+                false /* isHandover */,
+                1 /* pduSessionId */);
+
+        mSpyIwlanDataServiceProvider.setMetricsAtom(
+                TEST_APN_NAME,
+                64, // type IMS
+                true,
+                13, // LTE
+                false,
+                true,
+                1 // Transport Wi-Fi
+                );
+
+        MetricsAtom metricsAtom = mSpyIwlanDataServiceProvider.getMetricsAtomByApn(TEST_APN_NAME);
+        assertNotNull(metricsAtom);
+
+        when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
+                .thenReturn(mMockErrorPolicyManager);
+        when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
+                .thenReturn(DataFailCause.ERROR_UNSPECIFIED);
+
+        mSpyIwlanDataServiceProvider
+                .getIwlanTunnelCallback()
+                .onClosed(
+                        TEST_APN_NAME,
+                        new IwlanError(IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED));
+
+        mTestLooper.dispatchAll();
+
+        assertEquals(null, metricsAtom.getIwlanErrorWrappedClassname());
+        assertEquals(null, metricsAtom.getIwlanErrorWrappedStackFirstFrame());
+    }
+
     private void mockTunnelSetupFail(DataProfile dp) {
         mSpyIwlanDataServiceProvider.setupDataCall(
                 AccessNetworkType.IWLAN, /* AccessNetworkType */
diff --git a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
index 4297dbf..3d16135 100644
--- a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
+++ b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
@@ -536,7 +536,7 @@
         final String secondApnName = "mms";
 
         IkeSessionArgumentCaptors firstTunnelArgumentCaptors =
-                verifyBringUpTunnelWithDnsQuery(firstApnName, null);
+                verifyBringUpTunnelWithDnsQuery(firstApnName);
         ChildSessionCallback firstCallback =
                 firstTunnelArgumentCaptors.mChildSessionCallbackCaptor.getValue();
 
@@ -1215,6 +1215,10 @@
         mEpdgTunnelManager.setIsEpdgAddressSelected(true);
     }
 
+    private IkeSessionArgumentCaptors verifyBringUpTunnelWithDnsQuery(String apnName) {
+        return verifyBringUpTunnelWithDnsQuery(apnName, null);
+    }
+
     private IkeSessionArgumentCaptors verifyBringUpTunnelWithDnsQuery(
             String apnName, IkeSession ikeSession) {
         reset(mMockIwlanTunnelCallback);
@@ -1286,6 +1290,9 @@
     }
 
     private void verifyTunnelOnOpened(String apnName, ChildSessionCallback childSessionCallback) {
+        doReturn(0L)
+                .when(mEpdgTunnelManager)
+                .reportIwlanError(eq(apnName), eq(new IwlanError(IwlanError.NO_ERROR)));
         mEpdgTunnelManager
                 .getTmIkeSessionCallback(apnName, mEpdgTunnelManager.getCurrentTokenForApn(apnName))
                 .onOpened(mMockIkeSessionConfiguration);
@@ -1299,6 +1306,8 @@
         childSessionCallback.onOpened(mMockChildSessionConfiguration);
         mTestLooper.dispatchAll();
 
+        verify(mEpdgTunnelManager, times(1))
+                .reportIwlanError(eq(apnName), eq(new IwlanError(IwlanError.NO_ERROR)));
         verify(mMockIwlanTunnelCallback, times(1)).onOpened(eq(apnName), any());
     }
 
@@ -1322,7 +1331,7 @@
         final String secondApnName = "mms";
 
         IkeSessionArgumentCaptors firstTunnelArgumentCaptors =
-                verifyBringUpTunnelWithDnsQuery(firstApnName, null);
+                verifyBringUpTunnelWithDnsQuery(firstApnName);
         ChildSessionCallback firstCallback =
                 firstTunnelArgumentCaptors.mChildSessionCallbackCaptor.getValue();
 
@@ -1703,6 +1712,29 @@
     }
 
     @Test
+    public void testNeverReportIwlanErrorWhenCloseAnOpenedTunnel() throws Exception {
+        IkeInternalException ikeException =
+                new IkeInternalException(new IOException("Retransmitting failure"));
+
+        IkeSessionArgumentCaptors ikeSessionArgumentCaptors =
+                verifyBringUpTunnelWithDnsQuery(TEST_APN_NAME);
+
+        ChildSessionCallback childSessionCallback =
+                ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue();
+        verifyTunnelOnOpened(TEST_APN_NAME, childSessionCallback);
+
+        reset(mEpdgTunnelManager); // reset number of times of reportIwlanError()
+
+        mEpdgTunnelManager
+                .getTmIkeSessionCallback(TEST_APN_NAME, 0)
+                .onClosedExceptionally(ikeException);
+        mTestLooper.dispatchAll();
+        verify(mEpdgTunnelManager, never()).reportIwlanError(eq(TEST_APN_NAME), any());
+        verify(mMockIwlanTunnelCallback, times(1))
+                .onClosed(eq(TEST_APN_NAME), eq(new IwlanError(ikeException)));
+    }
+
+    @Test
     public void testCanBringUpTunnel() throws Exception {
         String testApnName = "www.xyz.com";
         IwlanError error = new IwlanError(mMockIkeException);