Android C++ Standard Library
The VeridiumID Mobile SDK includes components written in C++ and built with the Android NDK. If you have multiple C++ dependencies your app must include one version of the C++ standard library, provided by the Android NDK, that is compatible with them all.
Background: C++ Standard Library
Android requires that the C++ standard library, libc++, is distributed with your app as it is not part of the OS. You can only have one instance of the C++ standard library linked into a program. The best option is to use the shared variant of libc++, referred to by the NDK as c++_shared
.
[The other alternative, using c++_static
, is problematic unless the app includes exactly one C++ shared library; each shared library would link in the static C++ standard library, and so end up duplicating it]
Pitfalls
When building with Android Studio, the build process collects all C++/native libraries to bundle into the APK. If there are multiple copies of libraries with same filename, it may error out or pick one of them arbitrarily. It also can strip the standard library to remove unused symbols to reduce code size. This can cause incompatibilities to arise, where libraries refuse to load with unresolved symbol errors.
Android NDK r23b introduced a backward compatibility breakage. C++ components built with r23b or later may fail to link to older versions of the standard library (forwards compatibility is unaffected). If a dependancy, other than the Veridium SDK, includes the C++ shared library from an older NDK it can be arbitrarily included and cause a missing symbol error when initialising the Veridium SDK.
Troubleshooting
Scanning the app’s APK dependencies using this python script version_check.py will provide a list of native libraries and identify those causing compatibility issues.
Solutions
1. Update Dependencies
The most effective solution is to update dependencies to versions that use r23b or later. As r23b is from 2021, it is likely there are newer builds available.
2. Use a Build Task to Replace libc++_shared.so
As part of the Gradle build tree for android apps (as provided by the Gradle plugin 'com.android.application'
) the task merge<buildType>NativeLibs
, where <builtType>
is the name of the selected build type, complies together all native libraries. We can add a finaliser task using finalizedBy()
to copy a version of libc++_shared.so to /intermediates/merged_native_libs/
.
Edit the app’s build.gradle
, The example below has two build types, release
and debug
, and so configures two tasks; mergeReleaseNativeLibs
and mergeDebugNativeLibs. (You can observe the list of tasks in Android Studio in the Gradle tab). We add a finaliser task replaceStdLib()
to each merge task that copies the contents of stdlib_nkd\
placed one directory up from the build directory.
apply plugin: 'com.android.application'
android {
buildTypes {
debug { ... }
release { ... }
}
}
task replaceStdLib {
def destDir = file("$buildDir/intermediates/merged_native_libs/")
inputs.dir("$buildDir/../stdlib_nkd")
inputs.dir("$buildDir/intermediates/merged_native_libs/")
doLast {
copy {
from(fileTree("$buildDir/../stdlib_nkd").include('**/*.so'))
into destDir
}
logger.lifecycle("Merged NDK libc++_shared.so to $destDir")
}
}
afterEvaluate { evaluated ->
evaluated.tasks.findByName("mergeReleaseNativeLibs").finalizedBy('replaceStdLib')
evaluated.tasks.findByName("mergeDebugNativeLibs").finalizedBy('replaceStdLib')
}
stdlib_nkd\
can contain libc++_shared.so
files taken directly from the Android NDK installation at [ndk/<version>/sources/cxx-stl/llvm-libc++/libs/], but Veridium can provide this on request. The folder should be organised to reflect the structure of $buildDir/intermediates/merged_native_libs/,
hence should contain folders with your build type names.
Note, the NDK libraries include all symbols, including debug symbols. Unnecessary symbols will be stripped when the app apk is constructed later in the build process (depending on your configuration).
3. Manually strip libc++_shared.so
Manually remove libc++_shared.so
from all dependency .aar
files and instead include libc++_shared.so
via a “dummy“ C++ library.
Change the extension of veridium-core-release.aar
to .zip
, extract the contents, remove every instance of libc++_shared.so
, then re-zip it and change the extension back to .aar
. Remove any included libc++_shared.so from third-party .aar
dependencies using the same method.
Configure your own C++ libraries
Any C++ libraries you build as part of your Android Studio project must link with c++_shared
rather than the default c++_static
. The NDK has documentation for this, but in summary:
CMake
The section of the Gradle file which invokes CMake should set the ANDROID_STL
variable.
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=libc++_shared‘
cppFlags ''
}
}
ndk-build
Set APP_STL
in the http://Application.mk file:
APP_STL := c++_shared
Create a 'dummy' C++ library
If you're not already building your own C++ library, you can create an empty C++ library in Android Studio and configure it as described in the previous section; this will cause Android Studio to bundle libc++_shared.so into the app.