Tuesday, December 27, 2016
Improving Stability with Private C C Symbol Restrictions in Android N
Improving Stability with Private C C Symbol Restrictions in Android N
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 arent sure whether your app uses private libraries, you can immediately check it for warnings on the N Developer Preview.
Were making this change because its 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 shouldnt have to make emergency app updates to handle platform changes. For that reason, we recommend against using private C/C++ symbols. Private symbols arent 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 theres 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 didnt 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, weve identified a set of libraries that see significant use from Google Plays 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 dont 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 | Apps 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 youll see a warning in logcat and a toast on the display. This will happen only if your apps
targetSdkVersion
is less than N. If you change your manifest to target N, loading will fail: Javas 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), youll see a warning in logcat for all API levels before N, but loading will fail if you update your app so that its
targetSdkVersion
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 your
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.
What do the errors look like?
Heres some example logcat output from an app that hasnt bumped its target SDK version (and so the restriction isnt 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, youll 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 youre using dlopen(3) from C/C++ youll 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.
Available link for download