BpfMap: wrap native fd with ParcelFileDescriptor to avoid fd leak

ParcelFileDescriptor has implemented finalize(). Wrap native fd into
ParcelFileDescriptor which helps to release fd automatically.

Bug: 230880517
Test: manual test
Steps:
1. Connect to IPv6 only wifi and clat maps are created
$ adb shell cmd wifi set-wifi-enabled enabled
05-12 13:53:41.182  1793  2031 W BpfMap  : open /sys/fs/bpf/net_shared/map_clatd_clat_ingress6_map..: 493
05-12 13:53:41.182  1793  2031 W BpfMap  : open /sys/fs/bpf/net_shared/map_clatd_clat_egress4_map..: 546

$ adb shell ls -all proc/1793/fd | grep bpf
.. system system 64 2022-05-12 13:55:35 .. 493 -> anon_inode:bpf-map
.. system system 64 2022-05-12 13:55:35 .. 546 -> anon_inode:bpf-map

$ adb shell dumpsys connectivity
Forwarding rules:
  BPF ingress map: iif nat64Prefix v6Addr -> v4Addr oif
    47 /64:ff9b::/96 /2a00:79e1:abc:6f02:6efd:1d4b:f05e:25bd -> /192.0.0.4 54
  BPF egress map: iif v4Addr -> v6Addr nat64Prefix oif
    54 /192.0.0.4 -> /2a00:79e1:abc:6f02:6efd:1d4b:f05e:25bd /64:ff9b::/96 47 ether

2. Disconnect from IPv6 only wifi, force GC and clat map fds are released
$ adb shell cmd wifi set-wifi-enabled disabled
$ adb shell kill -10 1793
$ adb shell ls -all proc/1793/fd | grep bpf
(fd 493 and 546 are removed)

Change-Id: I26bbafbd73eccab6f4ae2c71690ecad12bbef7df
diff --git a/staticlibs/device/com/android/net/module/util/BpfMap.java b/staticlibs/device/com/android/net/module/util/BpfMap.java
index f1420c2..0ee862a 100644
--- a/staticlibs/device/com/android/net/module/util/BpfMap.java
+++ b/staticlibs/device/com/android/net/module/util/BpfMap.java
@@ -18,6 +18,7 @@
 import static android.system.OsConstants.EEXIST;
 import static android.system.OsConstants.ENOENT;
 
+import android.os.ParcelFileDescriptor;
 import android.system.ErrnoException;
 
 import androidx.annotation.NonNull;
@@ -26,6 +27,7 @@
 
 import com.android.net.module.util.Struct;
 
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.NoSuchElementException;
@@ -57,7 +59,7 @@
     private static final int BPF_NOEXIST = 1;
     private static final int BPF_EXIST = 2;
 
-    private final int mMapFd;
+    private final ParcelFileDescriptor mMapFd;
     private final Class<K> mKeyClass;
     private final Class<V> mValueClass;
     private final int mKeySize;
@@ -72,8 +74,7 @@
      */
     public BpfMap(@NonNull final String path, final int flag, final Class<K> key,
             final Class<V> value) throws ErrnoException, NullPointerException {
-        mMapFd = bpfFdGet(path, flag);
-
+        mMapFd = ParcelFileDescriptor.adoptFd(bpfFdGet(path, flag));
         mKeyClass = key;
         mValueClass = value;
         mKeySize = Struct.getSize(key);
@@ -85,10 +86,11 @@
      * The derived class implements an internal mocked map. It need to implement all functions
      * which are related with the native BPF map because the BPF map handler is not initialized.
      * See BpfCoordinatorTest#TestBpfMap.
+     * TODO: remove once TestBpfMap derive from IBpfMap.
      */
     @VisibleForTesting
     protected BpfMap(final Class<K> key, final Class<V> value) {
-        mMapFd = -1;
+        mMapFd = ParcelFileDescriptor.adoptFd(-1 /*invalid*/);  // unused
         mKeyClass = key;
         mValueClass = value;
         mKeySize = Struct.getSize(key);
@@ -101,7 +103,7 @@
      */
     @Override
     public void updateEntry(K key, V value) throws ErrnoException {
-        writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_ANY);
+        writeToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(), BPF_ANY);
     }
 
     /**
@@ -112,7 +114,7 @@
     public void insertEntry(K key, V value)
             throws ErrnoException, IllegalStateException {
         try {
-            writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_NOEXIST);
+            writeToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(), BPF_NOEXIST);
         } catch (ErrnoException e) {
             if (e.errno == EEXIST) throw new IllegalStateException(key + " already exists");
 
@@ -128,7 +130,7 @@
     public void replaceEntry(K key, V value)
             throws ErrnoException, NoSuchElementException {
         try {
-            writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_EXIST);
+            writeToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(), BPF_EXIST);
         } catch (ErrnoException e) {
             if (e.errno == ENOENT) throw new NoSuchElementException(key + " not found");
 
@@ -146,13 +148,13 @@
     public boolean insertOrReplaceEntry(K key, V value)
             throws ErrnoException {
         try {
-            writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_NOEXIST);
+            writeToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(), BPF_NOEXIST);
             return true;   /* insert succeeded */
         } catch (ErrnoException e) {
             if (e.errno != EEXIST) throw e;
         }
         try {
-            writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_EXIST);
+            writeToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(), BPF_EXIST);
             return false;   /* replace succeeded */
         } catch (ErrnoException e) {
             if (e.errno != ENOENT) throw e;
@@ -169,7 +171,7 @@
     /** Remove existing key from eBpf map. Return false if map was not modified. */
     @Override
     public boolean deleteEntry(K key) throws ErrnoException {
-        return deleteMapEntry(mMapFd, key.writeToBytes());
+        return deleteMapEntry(mMapFd.getFd(), key.writeToBytes());
     }
 
     /** Returns {@code true} if this map contains no elements. */
@@ -202,7 +204,7 @@
 
     private byte[] getNextRawKey(@Nullable final byte[] key) throws ErrnoException {
         byte[] nextKey = new byte[mKeySize];
-        if (getNextMapKey(mMapFd, key, nextKey)) return nextKey;
+        if (getNextMapKey(mMapFd.getFd(), key, nextKey)) return nextKey;
 
         return null;
     }
@@ -237,7 +239,7 @@
 
     private byte[] getRawValue(final byte[] key) throws ErrnoException {
         byte[] value = new byte[mValueSize];
-        if (findMapEntry(mMapFd, key, value)) return value;
+        if (findMapEntry(mMapFd.getFd(), key, value)) return value;
 
         return null;
     }
@@ -262,8 +264,8 @@
     }
 
     @Override
-    public void close() throws ErrnoException {
-        closeMap(mMapFd);
+    public void close() throws IOException {
+        mMapFd.close();
     }
 
     /**
@@ -281,8 +283,6 @@
         }
     }
 
-    private static native int closeMap(int fd) throws ErrnoException;
-
     private native int bpfFdGet(String path, int mode) throws ErrnoException, NullPointerException;
 
     private native void writeToMapEntry(int fd, byte[] key, byte[] value, int flags)
diff --git a/staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp b/staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp
index e25e17d..e3f48e5 100644
--- a/staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp
+++ b/staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp
@@ -27,15 +27,6 @@
 
 namespace android {
 
-static jint com_android_net_module_util_BpfMap_closeMap(JNIEnv *env, jobject clazz,
-        jint fd) {
-    int ret = close(fd);
-
-    if (ret) jniThrowErrnoException(env, "closeMap", errno);
-
-    return ret;
-}
-
 static jint com_android_net_module_util_BpfMap_bpfFdGet(JNIEnv *env, jobject clazz,
         jstring path, jint mode) {
     ScopedUtfChars pathname(env, path);
@@ -112,8 +103,6 @@
  */
 static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
-    { "closeMap", "(I)I",
-        (void*) com_android_net_module_util_BpfMap_closeMap },
     { "bpfFdGet", "(Ljava/lang/String;I)I",
         (void*) com_android_net_module_util_BpfMap_bpfFdGet },
     { "writeToMapEntry", "(I[B[BI)V",