Add BpfDump#dumpMapStatus

Test: atest NetworkStaticLibTests
Change-Id: I77d397b16380a397573cc528fe7f15f381cda21f
diff --git a/staticlibs/device/com/android/net/module/util/BpfDump.java b/staticlibs/device/com/android/net/module/util/BpfDump.java
index 2534d61..7549e71 100644
--- a/staticlibs/device/com/android/net/module/util/BpfDump.java
+++ b/staticlibs/device/com/android/net/module/util/BpfDump.java
@@ -15,6 +15,8 @@
  */
 package com.android.net.module.util;
 
+import static android.system.OsConstants.R_OK;
+
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.Base64;
@@ -110,6 +112,24 @@
         }
     }
 
+    /**
+     * Dump the BpfMap status
+     */
+    public static <K extends Struct, V extends Struct> void dumpMapStatus(IBpfMap<K, V> map,
+            PrintWriter pw, String mapName, String path) {
+        if (map != null) {
+            pw.println(mapName + ": OK");
+            return;
+        }
+        try {
+            Os.access(path, R_OK);
+            pw.println(mapName + ": NULL(map is pinned to " + path + ")");
+        } catch (ErrnoException e) {
+            pw.println(mapName + ": NULL(map is not pinned to " + path + ": "
+                    + Os.strerror(e.errno) + ")");
+        }
+    }
+
     // TODO: add a helper to dump bpf map content with the map name, the header line
     // (ex: "BPF ingress map: iif nat64Prefix v6Addr -> v4Addr oif"), a lambda that
     // knows how to dump each line, and the PrintWriter.
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/BpfDumpTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/BpfDumpTest.java
index 3932925..4e48332 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/BpfDumpTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/BpfDumpTest.java
@@ -16,10 +16,19 @@
 
 package com.android.net.module.util;
 
+import static android.system.OsConstants.EPERM;
+import static android.system.OsConstants.R_OK;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
+import android.system.ErrnoException;
+import android.system.Os;
 import android.util.Pair;
 
 import androidx.test.filters.SmallTest;
@@ -29,6 +38,7 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -118,4 +128,36 @@
         assertTrue(dump.contains("key=123, val=456"));
         assertTrue(dump.contains("key=789, val=123"));
     }
+
+    private String getDumpMapStatus(final IBpfMap<Struct.U32, Struct.U32> map) {
+        final StringWriter sw = new StringWriter();
+        BpfDump.dumpMapStatus(map, new PrintWriter(sw), "mapName", "mapPath");
+        return sw.toString();
+    }
+
+    @Test
+    public void testGetMapStatus() {
+        final IBpfMap<Struct.U32, Struct.U32> map =
+                new TestBpfMap<>(Struct.U32.class, Struct.U32.class);
+        assertEquals("mapName: OK\n", getDumpMapStatus(map));
+    }
+
+    @Test
+    public void testGetMapStatusNull() {
+        final MockitoSession session = mockitoSession()
+                .spyStatic(Os.class)
+                .startMocking();
+        try {
+            // Os.access succeeds
+            doReturn(true).when(() -> Os.access("mapPath", R_OK));
+            assertEquals("mapName: NULL(map is pinned to mapPath)\n", getDumpMapStatus(null));
+
+            // Os.access throws EPERM
+            doThrow(new ErrnoException("", EPERM)).when(() -> Os.access("mapPath", R_OK));
+            assertEquals("mapName: NULL(map is not pinned to mapPath: Operation not permitted)\n",
+                    getDumpMapStatus(null));
+        } finally {
+            session.finishMocking();
+        }
+    }
 }