21 June 2016
Posted by Dimitry Ivanov & Elliott Hughes, Software Engineers
As documented in the Android N behavioral changes, to protect Android users and apps from unforeseen crashes, Android N will restrict which libraries your C/C++ code can link against at runtime. As a result, if your app uses any private symbols from platform libraries, you will need to update it to either use the public NDK APIs or to include its own copy of those libraries. Some libraries are public: the NDK exposes libandroid, libc, libcamera2ndk, libdl, libGLES, libjnigraphics, liblog, libm, libmediandk, libOpenMAXAL, libOpenSLES, libstdc++, libvulkan, and libz as part of the NDK API. Other libraries are private, and Android N only allows access to them for platform HALs, system daemons, and the like. If you aren’t sure whether your app uses private libraries, you can immediately check it for warnings on the N Developer Preview.
We’re making this change because it’s painful for users when their apps stop working after a platform update. Whether they blame the app developer or the platform, everybody loses. Users should have a consistent app experience across updates, and developers shouldn’t have to make emergency app updates to handle platform changes. For that reason, we recommend against using private C/C++ symbols. Private symbols aren’t tested as part of the Compatibility Test Suite (CTS) that all Android devices must pass. They may not exist, or they may behave differently. This makes apps that use them more likely to fail on specific devices, or on future releases — as many developers found when Android 6.0 Marshmallow switched from OpenSSL to BoringSSL.
You may be surprised that there’s no STL in the list of NDK libraries. The three STL implementations included in the NDK — the LLVM libc++, the GNU STL, and libstlport — are intended to be bundled with your app, either by statically linking into your library, or by inclusion as a separate shared library. In the past, some developers have assumed that they didn’t need to package the library because the OS itself had a copy. This assumption is incorrect: a particular STL implementation may disappear (as was the case with stlport, which was removed in Marshmallow), may never have been available (as is the case with the GNU STL), or it may change in ABI incompatible ways (as is the case with the LLVM libc++).
In order to reduce the user impact of this transition, we’ve identified a set of libraries that see significant use from Google Play’s most-installed apps, and that are feasible for us to support in the short term (including libandroid_runtime.so, libcutils.so, libcrypto.so, and libssl.so). For legacy code in N, we will temporarily support these libraries in order to give you more time to transition. Note that we don't intend to continue this support in any future Android platform release, so if you see a warning that means your code will not work in a future release — please fix it now!
Table 1. What to expect if your app is linking against private native libraries.
Libraries | App's targetSdkVersion | Runtime access via dynamic linker | Impact, N Developer Preview | Impact, Final N Release | Impact, future platform version |
NDK Public | Any | Accessible | |||
Private (graylist) | <=23 | Temporarily accessible | Warning / Toast | Warning | Error |
>=24 | Restricted | Error | Error | Error | |
Private (all other)> | Any | Restricted | Error | Error | Error |
Please test your app during the N Previews.
targetSdkVersion
is less than N. If
you change your manifest to target N, loading will fail: Java’s
System.loadLibrary will throw, and C/C++’s dlopen(3) will return NULL.Test your apps on the Developer Preview — if you see a toast like this one, your app is accessing private native APIs. Please fix your code soon!
targetSdkVersion
is N or later.
targetSdkVersion
prior to O. If you believe there is missing functionality from
the NDK API that will make it impossible for you to transition off a temporarily
accessible library, please file a bug here.
Here’s some example logcat output from an app that hasn’t bumped its target SDK
version (and so the restriction isn’t fully enforced because this is only the
developer preview):
03-21 17:07:51.502 31234 31234 W linker : library "libandroid_runtime.so" ("/system/lib/libandroid_runtime.so") needed or dlopened by "/data/app/com.popular-app.android-2/lib/arm/libapplib.so" is not accessible for the namespace "classloader-namespace" - the access is temporarily granted as a workaround for http://b/26394120
This is telling you that your library “libapplib.so” refers to the library “libandroid_runtime.so”, which is a private library.
When Android N ships, or if you set your target SDK version to N now, you’ll see something like this if you try to use System.loadLibrary from Java:
java.lang.UnsatisfiedLinkError: dlopen failed: library "libcutils.so" ("/system/lib/libcutils.so") needed or dlopened by "/system/lib/libnativeloader.so" is not accessible for the namespace "classloader-namespace" at java.lang.Runtime.loadLibrary0(Runtime.java:977) at java.lang.System.loadLibrary(System.java:1602)
If you’re using dlopen(3) from C/C++ you’ll get a NULL return and dlerror(3) will return the same “dlopen failed...” string as shown above.
For more information about how to check if your app is using private symbols, see the FAQ on developer.android.com.