Move offload config hidl usage from native to java

TetheringUtil JNI is too big that it statically linking hidl and its
dependency library. To remove these static libraries, calling hidl in java
directly instead of using JNI.

Bug: 148984662
Test: -build, flash, boot
      -manually ON/OFF tethering

Change-Id: Id5a9759eb453fddaf0c3b7a31da17224e35e963e
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 285f4ab..4efe934 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -28,6 +28,7 @@
         "netd_aidl_interface-unstable-java",
         "netlink-client",
         "networkstack-aidl-interfaces-unstable-java",
+        "android.hardware.tetheroffload.config-V1.0-java",
         "android.hardware.tetheroffload.control-V1.0-java",
         "net-utils-framework-common",
     ],
@@ -48,25 +49,26 @@
 // Due to b/143733063, APK can't access a jni lib that is in APEX (but not in the APK).
 cc_library {
     name: "libtetherutilsjni",
+    sdk_version: "current",
     srcs: [
         "jni/android_net_util_TetheringUtils.cpp",
     ],
     shared_libs: [
-        "libcgrouprc",
-        "libnativehelper_compat_libc++",
-        "libvndksupport",
-    ],
-    static_libs: [
-        "android.hardware.tetheroffload.config@1.0",
         "liblog",
-        "libbase",
-        "libcutils",
-        "libhidlbase",
-        "libjsoncpp",
-        "libprocessgroup",
-        "libutils",
+        "libnativehelper_compat_libc++",
     ],
 
+    // We cannot use plain "libc++" here to link libc++ dynamically because it results in:
+    //   java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not found
+    // even if "libc++" is added into jni_libs below. Adding "libc++_shared" into jni_libs doesn't
+    // build because soong complains of:
+    //   module Tethering missing dependencies: libc++_shared
+    //
+    // So, link libc++ statically. This means that we also need to ensure that all the C++ libraries
+    // we depend on do not dynamically link libc++. This is currently the case, because liblog is
+    // C-only and libnativehelper_compat_libc also uses stl: "c++_static".
+    stl: "c++_static",
+
     cflags: [
         "-Wall",
         "-Werror",
@@ -85,9 +87,8 @@
     // Build system doesn't track transitive dependeicies for jni_libs, list all the dependencies
     // explicitly.
     jni_libs: [
-        "libcgrouprc",
+        "liblog",
         "libnativehelper_compat_libc++",
-        "libvndksupport",
         "libtetherutilsjni",
     ],
     resource_dirs: [
diff --git a/Tethering/jni/android_net_util_TetheringUtils.cpp b/Tethering/jni/android_net_util_TetheringUtils.cpp
index 1cf8f98..5493440 100644
--- a/Tethering/jni/android_net_util_TetheringUtils.cpp
+++ b/Tethering/jni/android_net_util_TetheringUtils.cpp
@@ -16,123 +16,18 @@
 
 #include <errno.h>
 #include <error.h>
-#include <hidl/HidlSupport.h>
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
-#include <linux/netfilter/nfnetlink.h>
-#include <linux/netlink.h>
 #include <net/if.h>
 #include <netinet/icmp6.h>
 #include <sys/socket.h>
-#include <android-base/unique_fd.h>
-#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
 
 #define LOG_TAG "TetheringUtils"
-#include <utils/Log.h>
+#include <android/log.h>
 
 namespace android {
 
-using hardware::hidl_handle;
-using hardware::hidl_string;
-using hardware::tetheroffload::config::V1_0::IOffloadConfig;
-
-namespace {
-
-inline const sockaddr * asSockaddr(const sockaddr_nl *nladdr) {
-    return reinterpret_cast<const sockaddr *>(nladdr);
-}
-
-int conntrackSocket(unsigned groups) {
-    base::unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER));
-    if (s.get() < 0) return -errno;
-
-    const struct sockaddr_nl bind_addr = {
-        .nl_family = AF_NETLINK,
-        .nl_pad = 0,
-        .nl_pid = 0,
-        .nl_groups = groups,
-    };
-    if (bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) != 0) {
-        return -errno;
-    }
-
-    const struct sockaddr_nl kernel_addr = {
-        .nl_family = AF_NETLINK,
-        .nl_pad = 0,
-        .nl_pid = 0,
-        .nl_groups = groups,
-    };
-    if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
-        return -errno;
-    }
-
-    return s.release();
-}
-
-// Return a hidl_handle that owns the file descriptor owned by fd, and will
-// auto-close it (otherwise there would be double-close problems).
-//
-// Rely upon the compiler to eliminate the constexprs used for clarity.
-hidl_handle handleFromFileDescriptor(base::unique_fd fd) {
-    hidl_handle h;
-
-    static constexpr int kNumFds = 1;
-    static constexpr int kNumInts = 0;
-    native_handle_t *nh = native_handle_create(kNumFds, kNumInts);
-    nh->data[0] = fd.release();
-
-    static constexpr bool kTakeOwnership = true;
-    h.setTo(nh, kTakeOwnership);
-
-    return h;
-}
-
-}  // namespace
-
-static jboolean android_net_util_configOffload(
-        JNIEnv* /* env */) {
-    sp<IOffloadConfig> configInterface = IOffloadConfig::getService();
-    if (configInterface.get() == nullptr) {
-        ALOGD("Could not find IOffloadConfig service.");
-        return false;
-    }
-
-    // Per the IConfigOffload definition:
-    //
-    // fd1   A file descriptor bound to the following netlink groups
-    //       (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
-    //
-    // fd2   A file descriptor bound to the following netlink groups
-    //       (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
-    base::unique_fd
-            fd1(conntrackSocket(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY)),
-            fd2(conntrackSocket(NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY));
-    if (fd1.get() < 0 || fd2.get() < 0) {
-        ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
-        return false;
-    }
-
-    hidl_handle h1(handleFromFileDescriptor(std::move(fd1))),
-                h2(handleFromFileDescriptor(std::move(fd2)));
-
-    bool rval(false);
-    hidl_string msg;
-    const auto status = configInterface->setHandles(h1, h2,
-            [&rval, &msg](bool success, const hidl_string& errMsg) {
-                rval = success;
-                msg = errMsg;
-            });
-    if (!status.isOk() || !rval) {
-        ALOGE("IOffloadConfig::setHandles() error: '%s' / '%s'",
-              status.description().c_str(), msg.c_str());
-        // If status is somehow not ok, make sure rval captures this too.
-        rval = false;
-    }
-
-    return rval;
-}
-
 static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
         jint ifIndex)
 {
@@ -229,7 +124,6 @@
  */
 static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
-    { "configOffload", "()Z", (void*) android_net_util_configOffload },
     { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket },
 };
 
@@ -242,7 +136,7 @@
 extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
     JNIEnv *env;
     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
-        ALOGE("ERROR: GetEnv failed");
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed");
         return JNI_ERR;
     }
 
diff --git a/Tethering/src/android/net/util/TetheringUtils.java b/Tethering/src/android/net/util/TetheringUtils.java
index fa543bd..5a6d5c1 100644
--- a/Tethering/src/android/net/util/TetheringUtils.java
+++ b/Tethering/src/android/net/util/TetheringUtils.java
@@ -24,14 +24,6 @@
  * {@hide}
  */
 public class TetheringUtils {
-
-    /**
-     * Offload management process need to know conntrack rules to support NAT, but it may not have
-     * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and
-     * share them with offload management process.
-     */
-    public static native boolean configOffload();
-
     /**
      * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
      * @param fd the socket's {@link FileDescriptor}.
diff --git a/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 90b9d3f..b545717 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -18,19 +18,28 @@
 
 import static android.net.util.TetheringUtils.uint16;
 
+import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
 import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
 import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
 import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
 import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
 import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
+import android.net.netlink.NetlinkSocket;
 import android.net.util.SharedLog;
-import android.net.util.TetheringUtils;
+import android.net.util.SocketUtils;
 import android.os.Handler;
+import android.os.NativeHandle;
 import android.os.RemoteException;
+import android.system.ErrnoException;
+import android.system.Os;
 import android.system.OsConstants;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.net.SocketException;
 import java.util.ArrayList;
 
 
@@ -49,6 +58,10 @@
     private static final String NO_INTERFACE_NAME = "";
     private static final String NO_IPV4_ADDRESS = "";
     private static final String NO_IPV4_GATEWAY = "";
+    // Reference kernel/uapi/linux/netfilter/nfnetlink_compat.h
+    private static final int NF_NETLINK_CONNTRACK_NEW = 1;
+    private static final int NF_NETLINK_CONNTRACK_UPDATE = 2;
+    private static final int NF_NETLINK_CONNTRACK_DESTROY = 4;
 
     private final Handler mHandler;
     private final SharedLog mLog;
@@ -121,9 +134,103 @@
         return DEFAULT_TETHER_OFFLOAD_DISABLED;
     }
 
-    /** Configure offload management process. */
+    /**
+     * Offload management process need to know conntrack rules to support NAT, but it may not have
+     * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and
+     * share them with offload management process.
+     */
     public boolean initOffloadConfig() {
-        return TetheringUtils.configOffload();
+        IOffloadConfig offloadConfig;
+        try {
+            offloadConfig = IOffloadConfig.getService();
+        } catch (RemoteException e) {
+            mLog.e("getIOffloadConfig error " + e);
+            return false;
+        }
+        if (offloadConfig == null) {
+            mLog.e("Could not find IOffloadConfig service");
+            return false;
+        }
+        // Per the IConfigOffload definition:
+        //
+        // h1    provides a file descriptor bound to the following netlink groups
+        //       (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
+        //
+        // h2    provides a file descriptor bound to the following netlink groups
+        //       (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
+        final NativeHandle h1 = createConntrackSocket(
+                NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
+        if (h1 == null) return false;
+
+        final NativeHandle h2 = createConntrackSocket(
+                NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
+        if (h2 == null) {
+            closeFdInNativeHandle(h1);
+            return false;
+        }
+
+        final CbResults results = new CbResults();
+        try {
+            offloadConfig.setHandles(h1, h2,
+                    (boolean success, String errMsg) -> {
+                        results.mSuccess = success;
+                        results.mErrMsg = errMsg;
+                    });
+        } catch (RemoteException e) {
+            record("initOffloadConfig, setHandles fail", e);
+            return false;
+        }
+        // Explicitly close FDs.
+        closeFdInNativeHandle(h1);
+        closeFdInNativeHandle(h2);
+
+        record("initOffloadConfig, setHandles results:", results);
+        return results.mSuccess;
+    }
+
+    private void closeFdInNativeHandle(final NativeHandle h) {
+        try {
+            h.close();
+        } catch (IOException | IllegalStateException e) {
+            // IllegalStateException means fd is already closed, do nothing here.
+            // Also nothing we can do if IOException.
+        }
+    }
+
+    private NativeHandle createConntrackSocket(final int groups) {
+        FileDescriptor fd;
+        try {
+            fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER);
+        } catch (ErrnoException e) {
+            mLog.e("Unable to create conntrack socket " + e);
+            return null;
+        }
+
+        final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups);
+        try {
+            Os.bind(fd, sockAddr);
+        } catch (ErrnoException | SocketException e) {
+            mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e);
+            try {
+                SocketUtils.closeSocket(fd);
+            } catch (IOException ie) {
+                // Nothing we can do here
+            }
+            return null;
+        }
+        try {
+            Os.connect(fd, sockAddr);
+        } catch (ErrnoException | SocketException e) {
+            mLog.e("connect to kernel fail for groups " + groups + " error: " + e);
+            try {
+                SocketUtils.closeSocket(fd);
+            } catch (IOException ie) {
+                // Nothing we can do here
+            }
+            return null;
+        }
+
+        return new NativeHandle(fd, true);
     }
 
     /** Initialize the tethering offload HAL. */