This repo contains a basic Android project that builds and verifies the avaiability of Netty's epoll native transport.
In this particular implementation, the availability of the epoll transport is verified by calling Netty's Epoll.ensureAvailability() from an instrumentation test (androidTest folder).
It could be done in many other ways, that is just one.
This was tested on:
- Android 16 emulator image BP41.250916.009.A1 (x86_64).
- NDK 27.0.12077973
- Gradle 8.13
- Android Gradle Plugin 8.13.1
The PR to merge the build fixes upstream is now up!
netty/netty#16016
The commits in there contain exhaustive details on what was changed and why, so please have a look as that's the most up to date source of truth.
What comes below is a sort of developer journal of how this repo came to be.
There were a few hurdles in getting the native epoll transport to build and run on Android:
-
The Netty library isn't packaged in a way that is suitable for consumption in an Android app.
Because of that, adding a dependency to the library results in the framework not being able to find the native (.so) libraries.Click to see error logs
12-05 12:17:39.670 12252 12267 I TestRunner: started: testNettyEpollAvailability(com.faenil.nettyepollandroidtest.ExampleInstrumentedTest) 12-05 12:17:39.697 12252 12267 E pollandroidtest: hiddenapi: Accessing hidden method Lsun/misc/Unsafe;->getAndAddInt(Ljava/lang/Object;JI)I (runtime_flags=0, domain=core-platform, api=max-target-r) from /data/app/~~QbU0x9-NdmAvAu-xlZNQmQ==/com.faenil.nettyepollandroidtest.test-LDnb8mOWExzLs8wQMk62nA==/base.apk (domain=app, TargetSdkVersion=36) using linking: denied 12-05 12:17:39.700 12252 12267 I PlatformDependent: Your platform does not provide complete low-level API for accessing direct buffers reliably. Unless explicitly requested, heap buffer will always be preferred to avoid potential system instability. 12-05 12:17:39.704 12252 12267 E pollandroidtest: No implementation found for int io.netty.channel.epoll.Native.offsetofEpollData() (tried Java_io_netty_channel_epoll_Native_offsetofEpollData and Java_io_netty_channel_epoll_Native_offsetofEpollData__) - is the library loaded, e.g. System.loadLibrary? 12-05 12:17:39.707 12252 12267 D nativeloader: Load libnetty_transport_native_epoll_x86_64.so using class loader ns clns-9 (caller=/data/app/~~QbU0x9-NdmAvAu-xlZNQmQ==/com.faenil.nettyepollandroidtest.test-LDnb8mOWExzLs8wQMk62nA==/base.apk): dlopen failed: library "libnetty_transport_native_epoll_x86_64.so" not found 12-05 12:17:39.708 12252 12267 D nativeloader: Load libnetty_transport_native_epoll_x86_64.so using class loader ns clns-9 (caller=/data/app/~~QbU0x9-NdmAvAu-xlZNQmQ==/com.faenil.nettyepollandroidtest.test-LDnb8mOWExzLs8wQMk62nA==/base.apk): dlopen failed: library "libnetty_transport_native_epoll_x86_64.so" not found 12-05 12:17:39.710 12252 12267 D nativeloader: Load libnetty_transport_native_epoll.so using class loader ns clns-9 (caller=/data/app/~~QbU0x9-NdmAvAu-xlZNQmQ==/com.faenil.nettyepollandroidtest.test-LDnb8mOWExzLs8wQMk62nA==/base.apk): dlopen failed: library "libnetty_transport_native_epoll.so" not found 12-05 12:17:39.711 12252 12267 D nativeloader: Load libnetty_transport_native_epoll.so using class loader ns clns-9 (caller=/data/app/~~QbU0x9-NdmAvAu-xlZNQmQ==/com.faenil.nettyepollandroidtest.test-LDnb8mOWExzLs8wQMk62nA==/base.apk): dlopen failed: library "libnetty_transport_native_epoll.so" not found 12-05 12:17:39.712 12252 12267 E TestRunner: failed: testNettyEpollAvailability(com.faenil.nettyepollandroidtest.ExampleInstrumentedTest) 12-05 12:17:39.712 12252 12267 E TestRunner: ----- begin exception ----- 12-05 12:17:39.718 12252 12267 E TestRunner: java.lang.UnsatisfiedLinkError: failed to load the required native library 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.channel.epoll.Epoll.ensureAvailability(Epoll.java:90) 12-05 12:17:39.718 12252 12267 E TestRunner: at com.faenil.nettyepollandroidtest.ExampleInstrumentedTest.testNettyEpollAvailability(ExampleInstrumentedTest.kt:23) [...] 12-05 12:17:39.718 12252 12267 E TestRunner: at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:446) 12-05 12:17:39.718 12252 12267 E TestRunner: at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2627) 12-05 12:17:39.718 12252 12267 E TestRunner: Caused by: java.lang.UnsatisfiedLinkError: could not load a native library: netty_transport_native_epoll_x86_64 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryLoader.load(NativeLibraryLoader.java:244) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.channel.epoll.Native.loadNativeLibrary(Native.java:334) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.channel.epoll.Native.<clinit>(Native.java:96) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.channel.epoll.Epoll.<clinit>(Epoll.java:42) 12-05 12:17:39.718 12252 12267 E TestRunner: ... 34 more 12-05 12:17:39.718 12252 12267 E TestRunner: Suppressed: java.lang.UnsatisfiedLinkError: could not load a native library: netty_transport_native_epoll 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryLoader.load(NativeLibraryLoader.java:244) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.channel.epoll.Native.loadNativeLibrary(Native.java:337) 12-05 12:17:39.718 12252 12267 E TestRunner: ... 36 more 12-05 12:17:39.718 12252 12267 E TestRunner: Caused by: java.io.FileNotFoundException: META-INF/native/libnetty_transport_native_epoll.so 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryLoader.load(NativeLibraryLoader.java:189) 12-05 12:17:39.718 12252 12267 E TestRunner: ... 37 more 12-05 12:17:39.718 12252 12267 E TestRunner: Suppressed: java.lang.UnsatisfiedLinkError: dlopen failed: library "libnetty_transport_native_epoll.so" not found 12-05 12:17:39.718 12252 12267 E TestRunner: at java.lang.Runtime.loadLibrary0(Runtime.java:1097) 12-05 12:17:39.718 12252 12267 E TestRunner: at java.lang.Runtime.loadLibrary0(Runtime.java:1019) 12-05 12:17:39.718 12252 12267 E TestRunner: at java.lang.System.loadLibrary(System.java:1765) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryUtil.loadLibrary(NativeLibraryUtil.java:38) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryLoader.loadLibrary(NativeLibraryLoader.java:395) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryLoader.load(NativeLibraryLoader.java:166) 12-05 12:17:39.718 12252 12267 E TestRunner: ... 37 more 12-05 12:17:39.718 12252 12267 E TestRunner: Suppressed: java.lang.UnsatisfiedLinkError: dlopen failed: library "libnetty_transport_native_epoll.so" not found 12-05 12:17:39.718 12252 12267 E TestRunner: at java.lang.Runtime.loadLibrary0(Runtime.java:1097) 12-05 12:17:39.718 12252 12267 E TestRunner: at java.lang.Runtime.loadLibrary0(Runtime.java:1019) 12-05 12:17:39.718 12252 12267 E TestRunner: at java.lang.System.loadLibrary(System.java:1765) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryUtil.loadLibrary(NativeLibraryUtil.java:38) 12-05 12:17:39.718 12252 12267 E TestRunner: at java.lang.reflect.Method.invoke(Native Method) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryLoader$1.run(NativeLibraryLoader.java:421) 12-05 12:17:39.718 12252 12267 E TestRunner: at java.security.AccessController.doPrivileged(AccessController.java:46) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryLoader.loadLibraryByHelper(NativeLibraryLoader.java:413) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryLoader.loadLibrary(NativeLibraryLoader.java:387) 12-05 12:17:39.718 12252 12267 E TestRunner: ... 38 more 12-05 12:17:39.718 12252 12267 E TestRunner: Caused by: java.io.FileNotFoundException: META-INF/native/libnetty_transport_native_epoll_x86_64.so 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryLoader.load(NativeLibraryLoader.java:189) 12-05 12:17:39.718 12252 12267 E TestRunner: ... 37 more 12-05 12:17:39.718 12252 12267 E TestRunner: Suppressed: java.lang.UnsatisfiedLinkError: dlopen failed: library "libnetty_transport_native_epoll_x86_64.so" not found 12-05 12:17:39.718 12252 12267 E TestRunner: at java.lang.Runtime.loadLibrary0(Runtime.java:1097) 12-05 12:17:39.718 12252 12267 E TestRunner: at java.lang.Runtime.loadLibrary0(Runtime.java:1019) 12-05 12:17:39.718 12252 12267 E TestRunner: at java.lang.System.loadLibrary(System.java:1765) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryUtil.loadLibrary(NativeLibraryUtil.java:38) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryLoader.loadLibrary(NativeLibraryLoader.java:395) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryLoader.load(NativeLibraryLoader.java:166) 12-05 12:17:39.718 12252 12267 E TestRunner: ... 37 more 12-05 12:17:39.718 12252 12267 E TestRunner: Suppressed: java.lang.UnsatisfiedLinkError: dlopen failed: library "libnetty_transport_native_epoll_x86_64.so" not found 12-05 12:17:39.718 12252 12267 E TestRunner: at java.lang.Runtime.loadLibrary0(Runtime.java:1097) 12-05 12:17:39.718 12252 12267 E TestRunner: at java.lang.Runtime.loadLibrary0(Runtime.java:1019) 12-05 12:17:39.718 12252 12267 E TestRunner: at java.lang.System.loadLibrary(System.java:1765) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryUtil.loadLibrary(NativeLibraryUtil.java:38) 12-05 12:17:39.718 12252 12267 E TestRunner: at java.lang.reflect.Method.invoke(Native Method) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryLoader$1.run(NativeLibraryLoader.java:421) 12-05 12:17:39.718 12252 12267 E TestRunner: at java.security.AccessController.doPrivileged(AccessController.java:46) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryLoader.loadLibraryByHelper(NativeLibraryLoader.java:413) 12-05 12:17:39.718 12252 12267 E TestRunner: at io.netty.util.internal.NativeLibraryLoader.loadLibrary(NativeLibraryLoader.java:387) 12-05 12:17:39.718 12252 12267 E TestRunner: ... 38 more 12-05 12:17:39.718 12252 12267 E TestRunner: ----- end exception ----- 12-05 12:17:39.725 12252 12267 I TestRunner: finished: testNettyEpollAvailability(com.faenil.nettyepollandroidtest.ExampleInstrumentedTest) -
Even if the libraries were packaged in a way that the Android build system would automatically pick them up, they'd still not work. Netty's native libraries, at least as of 4.2.7.Final, were built for standard Linux distros, so they need to be rebuilt for Android in any case (due to the different userspace and e.g. glibc vs bionic)
See https://stackoverflow.com/a/56876479 by Dan Albert (Google)
Indeed, if you (like me, like digging and tinkering with things) try to manually extract the .so from Netty's jars and put it in a directory where gradle can pick it up and bundle it with the app, you will stil encounter failures in loading is such as
java.lang.UnsatisfiedLinkError: dlopen failed: library "libdl.so.2" not found: needed by /data/app/[...]/base.apk!/lib/x86_64/libnetty_transport_native_epoll_x86_64.so in namespace clns-9 -
After solving the packaging challenges, there were a couple of build failures that required addressing.
netty_unix_errors.cused GNU'sstrerror_ron Android, which is actually not available unless_GNU_SOURCEis defined.
Android exposes XSI-compliantstrerror_rby default, so use that instead.
See Android 16 source- replacing
bzero, which is not available on Android platforms, withmemset
To build the project yourself:
- Download Android Studio
- Launch Studio and open this project
- run the
testNettyEpollAvailabilityinstrumentation test
Note: if you want to see the original compilation failures make sure the netty submodule in this repo is updated to use the netty-4.2.7.Final branch (or any older release branch).
The branch used by this repo by default will most likely already contain the build fixes and hence will not allow to reproduce the build failures.