Skip to content

faenil/NettyEpollAndroidBuildFailureTest

Repository files navigation

Netty epoll native transport on Android: build fixes

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

Upstream PR

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.

The path to building the epoll native transport on Android

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.c used GNU's strerror_r on Android, which is actually not available unless _GNU_SOURCE is defined.
      Android exposes XSI-compliant strerror_r by default, so use that instead.
      See Android 16 source
    • replacing bzero, which is not available on Android platforms, with memset

How to

To build the project yourself:

  • Download Android Studio
  • Launch Studio and open this project
  • run the testNettyEpollAvailability instrumentation 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.

About

This repo provides evidence for the build failure experienced when building Netty's native epoll transport on Android

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors