Add BpfDump.toBase64EncodedString to dump raw map

Move base64 related bpf map function and constant from BpfCoordinator.
This is preparation for testing BPF map in ClatCoordinator.

Test: atest NetworkStaticLibTests
Change-Id: I3a23393abe5b108cdb8d621e99b74fd20847474e
diff --git a/staticlibs/Android.bp b/staticlibs/Android.bp
index 31a7262..e5f8d58 100644
--- a/staticlibs/Android.bp
+++ b/staticlibs/Android.bp
@@ -111,6 +111,7 @@
     name: "net-utils-device-common-bpf",
     srcs: [
         "device/com/android/net/module/util/BpfBitmap.java",
+        "device/com/android/net/module/util/BpfDump.java",
         "device/com/android/net/module/util/BpfMap.java",
         "device/com/android/net/module/util/BpfUtils.java",
         "device/com/android/net/module/util/HexDump.java",
diff --git a/staticlibs/device/com/android/net/module/util/BpfDump.java b/staticlibs/device/com/android/net/module/util/BpfDump.java
new file mode 100644
index 0000000..fec225c
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/BpfDump.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 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.net.module.util;
+
+import android.util.Base64;
+
+import androidx.annotation.NonNull;
+
+/**
+ * The classes and the methods for BPF dump utilization.
+ */
+public class BpfDump {
+    // Using "," as a separator between base64 encoded key and value is safe because base64
+    // characters are [0-9a-zA-Z/=+].
+    public static final String BASE64_DELIMITER = ",";
+
+    /**
+     * Encode BPF key and value into a base64 format string which uses the delimiter ',':
+     * <base64 encoded key>,<base64 encoded value>
+     */
+    public static <K extends Struct, V extends Struct> String toBase64EncodedString(
+            @NonNull final K key, @NonNull final V value) {
+        final byte[] keyBytes = key.writeToBytes();
+        final String keyBase64Str = Base64.encodeToString(keyBytes, Base64.DEFAULT)
+                .replace("\n", "");
+        final byte[] valueBytes = value.writeToBytes();
+        final String valueBase64Str = Base64.encodeToString(valueBytes, Base64.DEFAULT)
+                .replace("\n", "");
+
+        return keyBase64Str + BASE64_DELIMITER + valueBase64Str;
+    }
+
+    // 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
new file mode 100644
index 0000000..b166b4a
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/BpfDumpTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 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.net.module.util;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BpfDumpTest {
+    @Test
+    public void testToBase64EncodedString() {
+        final Struct.U32 key = new Struct.U32(123);
+        final Struct.U32 value = new Struct.U32(456);
+
+        // Verified in python:
+        //   import base64
+        //   print(base64.b64encode(b'\x7b\x00\x00\x00')) # key: ewAAAA==
+        //   print(base64.b64encode(b'\xc8\x01\x00\x00')) # value: yAEAAA==
+        assertEquals("7B000000", HexDump.toHexString(key.writeToBytes()));
+        assertEquals("C8010000", HexDump.toHexString(value.writeToBytes()));
+        assertEquals("ewAAAA==,yAEAAA==", BpfDump.toBase64EncodedString(key, value));
+    }
+}