Fix NPE or RTE handling for sockets in dump

 - Fix to prevent crash when socket is null
 - Modified to handle exception for the socket
 - Added thread-safe guide to dump
 - Add gating permission

Bug: 335688353
Test: atest FrameworksIkeTests & atest CtsIkeTestCases
Test: tested with VoWiFi call and gen bugreport
Change-Id: I5f53b59d9d65de04c76f28bd860cdb6d132c6369
diff --git a/src/java/android/net/ipsec/ike/IkeSession.java b/src/java/android/net/ipsec/ike/IkeSession.java
index ce6b6e8..14facd4 100644
--- a/src/java/android/net/ipsec/ike/IkeSession.java
+++ b/src/java/android/net/ipsec/ike/IkeSession.java
@@ -470,10 +470,16 @@
      */
     @FlaggedApi("com.android.ipsec.flags.dumpsys_api")
     public void dump(@NonNull PrintWriter pw) {
+        // TODO(b/336409878): Add @RequiresPermission annotation.
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.DUMP, mContext.getAttributionTag());
+
+        // Please make sure that the dump is thread-safe
+        // so the client won't get a crash or exception when adding codes to the dump.
         pw.println();
         pw.println("IkeSession:");
         pw.println("------------------------------");
-        // Dump ike state machine
+        // Dump ike state machine.
         if (mIkeSessionStateMachine != null) {
             pw.println();
             mIkeSessionStateMachine.dump(pw);
diff --git a/src/java/android/net/ipsec/ike/IkeSessionParams.java b/src/java/android/net/ipsec/ike/IkeSessionParams.java
index c723ed2..433cb23 100644
--- a/src/java/android/net/ipsec/ike/IkeSessionParams.java
+++ b/src/java/android/net/ipsec/ike/IkeSessionParams.java
@@ -2368,9 +2368,13 @@
      * Dumps the state of {@link IkeSessionParams}
      *
      * @param pw {@link PrintWriter} to write the state of the object.
+     * @param prefix prefix for indentation
      * @hide
      */
     public void dump(PrintWriter pw, String prefix) {
+        // Please make sure that the dump is thread-safe
+        // so the client won't get a crash or exception when adding codes to the dump.
+
         pw.println("------------------------------");
         pw.println("IkeSessionParams:");
         pw.println(prefix + "Caller configured network: " + mCallerConfiguredNetwork);
diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
index adc8f49..c08b421 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
@@ -6015,16 +6015,18 @@
      */
     public void dump(PrintWriter pw) {
         super.dump(new FileDescriptor(), pw, new String[0]);
+        // Please make sure that the dump is thread-safe
+        // so the client won't get a crash or exception when adding codes to the dump.
 
         // TODO(b/310058405): To use IndentingPrintWriter Utility Class for Indentation purpose
         String prefix = "    ";
 
-        // dump ike session params data
+        // Dump ike session params data.
         if (mIkeSessionParams != null) {
             mIkeSessionParams.dump(pw, prefix);
         }
 
-        // dump ike connection controller data
+        // Dump ike connection controller data.
         if (mIkeConnectionCtrl != null) {
             mIkeConnectionCtrl.dump(pw, prefix);
         }
diff --git a/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionController.java b/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionController.java
index 78140ce..9f6ebd2 100644
--- a/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionController.java
+++ b/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionController.java
@@ -1275,8 +1275,12 @@
      * Dumps the state of {@link IkeConnectionController}
      *
      * @param pw {@link PrintWriter} to write the state of the object.
+     * @param prefix prefix for indentation
      */
     public void dump(PrintWriter pw, String prefix) {
+        // Please make sure that the dump is thread-safe
+        // so the client won't get a crash or exception when adding codes to the dump.
+
         pw.println("------------------------------");
         pw.println("IkeConnectionController:");
         pw.println(prefix + "Network: " + mNetwork);
@@ -1284,8 +1288,7 @@
         pw.println(prefix + "Local address: " + mLocalAddress);
         pw.println(prefix + "Remote(Server) address: " + mRemoteAddress);
         pw.println(prefix + "Mobility status: " + mMobilityEnabled);
-        pw.println(prefix + "Local port: " + getLocalPort());
-        pw.println(prefix + "Remote(server) port: " + getRemotePort());
+        printPortInfo(pw, prefix);
         pw.println(
                 prefix + "Esp ip version: " + IkeSessionParams.IP_VERSION_TO_STR.get(mIpVersion));
         pw.println(
@@ -1294,6 +1297,30 @@
         pw.println();
     }
 
+    /**
+     * Port information may sometimes cause exceptions such as NPE or RTE, Dumps ports including the
+     * exception.
+     *
+     * @param pw {@link PrintWriter} to write the state of the object.
+     * @param prefix prefix for indentation
+     */
+    private void printPortInfo(PrintWriter pw, String prefix) {
+        // Make it thread-safe. Since this method may be accessed simultaneously from
+        // multiple threads, The socket is assigned locally and then printed.
+        IkeSocket socket = mIkeSocket;
+        if (socket == null) {
+            pw.println(prefix + "Local port: null socket");
+            pw.println(prefix + "Remote(server) port: null socket");
+        } else {
+            try {
+                pw.println(prefix + "Local port: " + socket.getLocalPort());
+            } catch (ErrnoException e) {
+                pw.println(prefix + "Local port: failed to get port");
+            }
+            pw.println(prefix + "Remote(server) port: " + socket.getIkeServerPort());
+        }
+    }
+
     @Override
     public void onUnderlyingNetworkUpdated(
             Network network,
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionControllerTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionControllerTest.java
index a60e081..e221f64 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionControllerTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/net/IkeConnectionControllerTest.java
@@ -38,6 +38,7 @@
 import static com.android.internal.net.ipsec.test.ike.net.IkeConnectionController.NAT_TRAVERSAL_UNSUPPORTED;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThrows;
@@ -96,6 +97,8 @@
 import org.mockito.ArgumentCaptor;
 
 import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
@@ -1634,4 +1637,19 @@
     public void testOnUnderlyingNetworkUpdatedWithNat64V6Address() throws Exception {
         verifyDnsLookupWithCachedIpv6Address(true /* isNat64 */, true /* dnsExpected */);
     }
+
+    @Test
+    @IgnoreUpTo(VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public void testDump() throws Exception {
+        mIkeConnectionCtrl.tearDown();
+        // Clear the network callback registration call in #setUp()
+        resetMockConnectManager();
+
+        setupRemoteAddressForNetwork(mMockDefaultNetwork, new InetAddress[0]);
+        mIkeConnectionCtrl = buildIkeConnectionCtrl();
+
+        final StringWriter stringWriter = new StringWriter();
+        mIkeConnectionCtrl.dump(new PrintWriter(stringWriter), "");
+        assertFalse(stringWriter.toString().isEmpty());
+    }
 }