Posted by Dimitry Ivanov & Elliott Hughes, Software Engineers
As documented in the href="https://developer.android.com/preview/behavior-changes.html#ndk">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 |
What behavior will I see?
Please test your app during the N Previews.
N Preview behavior
- All public NDK libraries (libandroid, libc, libcamera2ndk, libdl, libGLES,
libjnigraphics, liblog, libm, libmediandk, libOpenMAXAL, libOpenSLES, libstdc++,
libvulkan, and libz), plus libraries that are part of your app are accessible.
- For all other libraries you’ll see a warning in logcat and a toast on the
display. This will happen only if your app’stargetSdkVersion
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!
N Final Release behavior
- All NDK libraries (libandroid, libc, libcamera2ndk, libdl, libGLES,
libjnigraphics, liblog, libm, libmediandk, libOpenMAXAL, libOpenSLES, libstdc++,
libvulkan, and libz), plus libraries that are part of your app are accessible.
- For the temporarily accessible libraries (such as libandroid_runtime.so,
libcutils.so, libcrypto.so, and libssl.so), you’ll see a warning in logcat for
all API levels before N, but loading will fail if you update your app so that
itstargetSdkVersion
is N or later.
- Attempts to load any other libraries will fail in the final release of
Android N, even if your app is targeting a pre-N platform version.
Future platform behavior
- In O, all access to the temporarily accessible libraries will be removed.
As a result, you should plan to update your app regardless of yourtargetSdkVersion
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.
What do the errors look like?
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 href="http://man7.org/linux/man-pages/man3/dlopen.3.html">dlopen(3) from
C/C++ you’ll get a NULL return and href="http://man7.org/linux/man-pages/man3/dlerror.3.html">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.