Building OpenSSL is an arcane thing to do sometimes. I have nowhere on the web found a real simple solution to build OpenSSL for Android that met the number one requirement for me: Being able to build on a build host (or development host) that runs Windows. I simply do not want to maintain another build machine running some Linux flavor only in order to be able to build an OpenSSL static library and a shared library that uses the former. I would rather want to use Visual Studio 2017 with its support for native C/C++ development which also suggests using the same toolchain for building the static OpenSSL library and the shared libraries that are going to consume it. Since I am planning to use Xamarin for my mobile apps, having the entire development and build process on a Windows box would be a big win for me.
�
DISCLAIMER: I have not yet tried, whether the OpenSSL libraries produced with my set of scripts and configuration files actually work in a real world Android app. But the build procedures themselves succeed and create the familiar libcrypto.a, libcrypto.so.1.0.0, libssl.a and libssl.so.1.0.0 files which are also all in the correct binary format as you can easily verify running readelf on them (the build script actually does this).
�
Challenges
Since OpenSSL for Android is usually built on a Linux host using Google’s NDK, its entire build procedure also heavily relies on infrastructure that is usually found on a Linux box: Shell scripts, PERL, symbolic links and build tools like make and friends. None of these infrastructure elements can be found on a standard Windows box and things like symbolic links have been unfeasible until the arrival of Windows 10 Build 10.0.15063, at least for non-administrative accounts (you do develop and build as a LUA user, don’t ya?). So we need a Unix-like environment for this purpose running on Windows plus some other arcane preconditions set which we will see in a moment. WSL cannot be used for this purpose because it only allows 64-bit Linux applications to be executed. However, the NDK that Google ships for Linux comes with a number of 32-bit applications which are not a problem when executed on a «real» installation of Linux but call for trouble on WSL. So the only option left over for this purpose is Cygwin.
�
Using the scripts
In order to use the scripts I have written, follow theses steps:
- Make sure all preconditions are fulfilled, as described in the paragraph below
- Unpack the zip File (Download) into a writable directory, make sure that you have the «Change permissions» right for the directory and all its files and subdirectories granted, otherwise the calls to chmod in the build scripts will fail (cygwin places some strange NULL SID ACEs into the DACL of files that are granted execution right using chmod which requires the «Change permissions» access right)
- Download openssl-1.0.2q.tar.gz from openssl.org (Download) and copy it into the directory created in the previous step.
- Open a windows command prompt and change the current directory to the directory from the above steps
- Execute «build.bat» and wait (on my Ryzen Vega this takes something like 22 minutes)
- Find the header files and libraries for the build in a platform specific subdirectory like arch-x86, arch-arm in the subdirectory build\<android-api-version>
�
Preconditions
�In order to use the build scripts, you need Microsoft Visual Studio 2017 installed with the «Mobile development with C++» workload enabled. Unless disabled during Visual Studio Setup, this will create subdirectories like these in c:\Microsoft\AndroidNDK64: android-ndk-r12b, android-ndk-r13b, android-ndk-r15c. You might have them also in the directory c:\Microsoft\AndroidNDK. These directories contain the NDK in different versions and I assume that later versions of Visual Studio will ship with even newer Versions of the NDK than the ones mentioned here. The scripts of mine will assume that the r15c-Version of the NDK is installed in c:\Microsoft\AndroidNDK64\android-ndk-r15c but you can set your installation directory of the NDK prior to execution of the build scripts by setting the OPENSSL_ANDROID_NDK_ROOT environment variable in the command prompt to something like c:\Microsoft\AndroidNDK64\android-ndk-r12b like this:
�
set OPENSSL_ANDROID_NDK_ROOT=c:\Microsoft\AndroidNDK64\android-ndk-r12b
You also have to install cygwin, I have not tried with the 32-bit version of cygwin but I assume it works as well. Cygwin is a huge beast and we have to use only a tiny fraction of it for our purposes. Unfortunately is installer is not even digitally signed with an Authenticode signature, so be sure to check it with its signature file and a tool like Kleopatra in order to verify authenticity. When installing cygwin you can use the standard installation options (which will create a bash environment with PERL and other good stuff) but be sure to also install the following programs: dos2unix, makedepend, automake. We need dos2unix in order to convert a dynamically created shell script from CRLF-notation to LF-notation consumable by the cygwin bash and we need makedepend and automake for building via the OpenSSL shell scripts. The build scripts of mine assume that cygwin is installed in the� c:\cygwin64 directory but you can override this much like with the above OPENSSL_ANDROID_NDK_ROOT environment variable. So, e.g. if you have installed cygwin into d:\cygwin64, type the following in the command prompt prior to execution of build.bat:
set CYGWIN_PATH=d:\cygwin64
You might want to create a permanent CYGWIN_PATH environment variable for you development or build machine unless it is installed at the default location c:\cygwin64.
�
Since the build scripts for OpenSSL on Linux make heavy use of symbolic links for header files, you should also turn on «Developer Mode» on your build and development host (which should be Windows 10 10.0.15063 or later), otherwise the build scripts cannot be executed successfully by a standard user. From Windows 10 10.0.15063 on, symbolic links can be created on Windows machines without administrative permissions if the box is running in «Developer Mode».
�
How it works
build.bat will invoke creatcfg.bat and buildall.bat in succession. The purpose of creatcfg.bat is to create the Setenv-build.sh build script and the buildall.bat batch file with the configuration set via the CYGWIN_PATH and the OPENSSL_ANDROID_NDK_ROOT environment variables. While we are at it: You can also set the Android API version to be used for the build with the ANDROID_API environment variable (default value: android-21) like this prior to execution of build.bat:
set ANDROID_API=android-26
buildall.bat, which is created by creatcfg.bat is then executed by build.bat and will invoke the cygwin bash three times with arguments to execute. The first one will convert the newly created Setenv-build.sh from CRLF notation to LF notation with dos2unix, so the cygwin bash can consume it properly. The second will execute «chmod a+x» for all shell script files (this is the place where you will get an Access Denied error if the user executing this script does not have the «Change permissions» access right). And the last one will execute the buildall.sh shell script.
buildall.sh will first clean up the build subdirectory, if it exists and will recreate it and grant everyone full control to the build subdirectory (ok, this is cargo cult, I don’t really know if we really need this with developer mode turned on and the «Change permissions» access right set as documented before). After that it will invoke buildplatform.sh or each Android hardware platform and pipe stdout and stderr into a log file (arm64_build.log, arm_build.log, mips64_build.log, mips_build.log, x86_64_build.log and x86_build.log).
buildplatform.sh takes the build architecture as an argument and will first invoke the dynamically created shell script Setenv-build.sh, which will set a couple of environment variables that depend on your cygwin installation path, the NDK path being set and the Android API version chosen, as explained above. After that, buildplatform.sh will invoke an architecture specific configuraton file like� Setenv-mips.sh which will set architecture specific environment variables. It then creates a subdirectory for the architecture value passed as an argument and will then unpack the openssl tarball into this directory. It is the buildplatform.sh shell script where you also would want to change the name of this tarball once a newer version as 1.0.2q has been released, simply by changing the value of the OPENSSLNAME variable.
After the tarball has been unpacked, the buildplatform.sh script will copy the Configure file from my set of script files over the one that comes with the tarball. The reason is that the Configure file in the tarball is missing entries for the x86_64 build, the arm64 build and the mips64 build. You might want to check, once a newer version than 1.0.2q is used, if this copy step is still necessary and if it doesn’t break other builds, e.g. if the compiler options for GCC are changed in the standard Configure file from the tarball.
After that, the buildplatform.sh will perform the usual OpenSSL build sequence of
�
perl -pi -e ‘s/install: all install_docs install_sw/install: install_docs install_sw/g’ Makefile.org
perl configure $OPENSSL_CONFIG_ARCH shared no-asm no-ssl2 no-ssl3 no-comp no-hw no-engine make depend
make all
in order to build the platform (OPENSSL_CONFIG_ARCH is a variable denoting the hardware platform name).
Once a hardware platform is built with make all, the buildplatform.sh script invokes readelf for the generated library files so you can verify in the platform build log, that the created binaries have the proper format. The subsequent make install_sw step will then copy the build output into the build subdirectory. For some reason, this step fails, as you can see in the log files, but library files and header files are copied successfully, so I assume this step is complete enough to be usable.
Fun fact: Try to observe a running build with Process Explorer and the image path being shown as a column. You will then notice that the actual make.exe being invoked in the build process is not the one from cygwin but instead a binary that is part of the Visual Studio NDK installation.
�
�
Leftover stuff
I would guess that the same result with my scripts could be achieved with the Windows version of Google’s NDK. Drop me a note if you would like to share your results. Initially, some three years ago, I had started creating a build recipe for OpenSSL on Windows with Cygwin which finally emerged into today’s version of the build scripts, so simply pointing the OPENSSL_ANDROID_NDK_ROOT environment variable to the location where the Google NDK is installed might already work. At some point I decided to give the Microsoft Compilers a chance and this worked out pretty flawlessly. This also relieved me from having to install yet another SDK on my development and build boxes, this time Google’s NDK.
�
License
All the files I have written use the OpenSSL license, so nothing should change for you if you are already using OpenSSL.
I tried to build OpenSSL for Android with the following commands in Windows Command Prompt:
cd C:\dev\repos\openssl-1.1.1r set MY_NDK_DIR=C:\Users\dmitr\AppData\Local\Android\Sdk\ndk\23.1.7779620 set MY_CLANG_DIR=%MY_NDK_DIR%\toolchains\llvm\prebuilt\windows-x86_64\bin set MY_CMAKE=%MY_NDK_DIR%\prebuilt\windows-x86_64\bin\make.exe set MY_PERL_DIR=C:\dev\PFiles\Strawberry\perl\bin set PATH=%MY_CLANG_DIR%;%MY_PERL_DIR%;%PATH% set "MY_ANDROID_BUILD_ABI=x86_64" rem set "MY_ANDROID_BUILD_ABI=x86" rem set "MY_ANDROID_BUILD_ABI=arm64" rem set "MY_ANDROID_BUILD_ABI=arm" set MY_INSTALL_ROOT_DIR=C:/dev/libs/OpenSSL-android set MY_INSTALL_DIR=%MY_INSTALL_ROOT_DIR%/%MY_ANDROID_BUILD_ABI% set ANDROID_NDK_HOME=%MY_NDK_DIR% # Set compiler clang, instead of gcc by default set CC=clang perl Configure android-%MY_ANDROID_BUILD_ABI% -D__ANDROID_API__=23 --prefix="%MY_INSTALL_DIR%" --openssldir="%MY_INSTALL_DIR%" no-shared
But got the following error:
Configuring OpenSSL version 1.1.1r (0x1010112fL) for android-x86_64 Using os-specific seed configuration no NDK clang on $PATH at (eval 11) line 139.
in C:\dev\repos\openssl-1.1.1r\Configurations\15-android.conf
:
my $triarch = $triplet{$arch}; my $cflags; my $cppflags; # see if there is NDK clang on $PATH, "universal" or "standalone" if (which("clang") =~ m|^$ndk/.*/prebuilt/([^/]+)/|) { my $host=$1; # harmonize with gcc default my $arm = $ndkver > 16 ? "armv7a" : "armv5te"; (my $tridefault = $triarch) =~ s/^arm-/$arm-/; (my $tritools = $triarch) =~ s/(?:x|i6)86(_64)?-.*/x86$1/; if (length $sysroot) { $cflags .= " -target $tridefault " . "-gcc-toolchain \$($ndk_var)/toolchains" . "/$tritools-4.9/prebuilt/$host"; $user{CC} = "clang" if ($user{CC} !~ m|clang|); } else { $user{CC} = "$tridefault$api-clang"; } $user{CROSS_COMPILE} = undef; if (which("llvm-ar") =~ m|^$ndk/.*/prebuilt/([^/]+)/|) { $user{AR} = "llvm-ar"; $user{ARFLAGS} = [ "rs" ]; $user{RANLIB} = ":"; } } elsif ($is_standalone_toolchain) { my $cc = $user{CC} // "clang"; # One can probably argue that both clang and gcc should be # probed, but support for "standalone toolchain" was added # *after* announcement that gcc is being phased out, so # favouring clang is considered adequate. Those who insist # have option to enforce test for gcc with CC=gcc. if (which("$triarch-$cc") !~ m|^$ndk|) { die "no NDK $triarch-$cc on \$PATH"; } $user{CC} = $cc; $user{CROSS_COMPILE} = "$triarch-"; } elsif ($user{CC} eq "clang") { die "no NDK clang on \$PATH"; } else { if (which("$triarch-gcc") !~ m|^$ndk/.*/prebuilt/([^/]+)/|) { die "no NDK $triarch-gcc on \$PATH"; } $cflags .= " -mandroid"; $user{CROSS_COMPILE} = "$triarch-"; }
clang
is in PATH
, but
fails with the following error in Windows Command Prompt.
Undefined subroutine &main::which called at -e line 1.
I tried to switch to Bash:
cd /c/dev/repos/openssl-1.1.1r export MY_NDK_DIR=/c/Users/dmitr/AppData/Local/Android/Sdk/ndk/23.1.7779620 export MY_CLANG_DIR=${MY_NDK_DIR}/toolchains/llvm/prebuilt/windows-x86_64/bin export MY_CMAKE=${MY_NDK_DIR}/prebuilt/windows-x86_64/bin/make.exe export MY_PERL_DIR=/c/dev/PFiles/Strawberry/perl/bin export PATH=${MY_CLANG_DIR}:${MY_PERL_DIR}:${PATH} export "MY_ANDROID_BUILD_ABI=x86_64" #export "MY_ANDROID_BUILD_ABI=x86" #export "MY_ANDROID_BUILD_ABI=arm64" #export "MY_ANDROID_BUILD_ABI=arm" export MY_INSTALL_ROOT_DIR=/c/dev/libs/OpenSSL-android export MY_INSTALL_DIR=${MY_INSTALL_ROOT_DIR}/${MY_ANDROID_BUILD_ABI} export ANDROID_NDK_HOME=$MY_NDK_DIR export CC=clang perl Configure android-${MY_ANDROID_BUILD_ABI} -D__ANDROID_API__=23 --prefix=${MY_INSTALL_DIR} --openssldir=${MY_INSTALL_DIR} no-shared
but got the same error.
and which
did not work in Perl’s command line:
Undefined subroutine &main::which called at -e line 1.
My first program in Perl:
perl -e 'print("Hello World\n");'
I added
print("$ndk\n"); print(which("clang"));
and it displayed this:
C:\Users\dmitr\AppData\Local\Android\Sdk\ndk\20.1.5948944 C:\Users\dmitr\AppData\Local\Android\Sdk\ndk\201~1.594\TOOLCH~1\llvm\prebuilt\WINDOW~1\bin\clang.EXE
in both Windows Command Prompt and Bash, and more than that m||
is testing for forward-slash but which
appears to be returning backslashes.
I also added
and got:
C:\Users\dmitr\AppData\Local\Android\Sdk\ndk\201~1.594\TOOLCH~1\llvm\prebuilt\WINDOW~1\bin\clang.EXEC:\Users\dmitr\AppData\Local\Android\Sdk\ndk\20.1.5948944\toolchains\llvm\prebuilt\windows-x86_64\bin;C:\dev\PFiles\Strawberry\perl\bin ...
I have an impression that the script adds C:\Users\dmitr\AppData\Local\Android\Sdk\ndk\201~1.594\TOOLCH~1\llvm\prebuilt\WINDOW~1\bin\clang.EXE
to the PATH
in some wrong way.
The conclusion is that I would probably switch to Linux.
openssl-curl-android
Compile openssl and curl for Android
Prerequisites
Make sure you have Android NDK
installed.
You may also need to install autoconf
and libtool
toolchains as well as build essentials.
clone repo beefore download with command prompt
enter MSYS2
Download MSYS2 and configure enviroment might need to download other pacman
in MSYS2 terminal:
echo $MSYSTEM
export NDK=/D/android-sdk/ndk/23.1.7779620 # e.g. D:/android-sdk/ndk/23.0.7599858
export HOST_TAG=windows-x86_64 # e.g. darwin-x86_64, see https://developer.android.com/ndk/guides/other_build_systems#overview
export MIN_SDK_VERSION=21 # or any version you want
pacman -S mingw-w64-ucrt-x86_64-python mingw-w64-ucrt-x86_64-python-pip
#pacman -S mingw-w64-x86_64-python mingw-w64-x86_64-python-pip
#?? pip install diskutils
python -v
cd /d/android-sdk/ndk/23.1.7779620/build/tools
python make_standalone_toolchain.py --api 21 --arch arm64 --install-dir /d/android-toolchain2/arm64
output something like:
k10@DESKTOP-DVP2O8B UCRT64 /d/android-sdk/ndk/23.1.7779620/build/tools
$ python make_standalone_toolchain.py —api 21 —arch arm64 —install-dir /d/android-toolchain2/arm64
WARNING:main:make_standalone_toolchain.py is no longer necessary. The
%NDK%/toolchains/llvm/prebuilt/windows-x86_64/bin directory contains target-specific scripts that perform
the same task. For example, instead of:
C:\>python %NDK%/build/tools/make_standalone_toolchain.py \
--arch arm64 --api 21 --install-dir toolchain
C:\>toolchain/bin/clang++ src.cpp
Instead use:
C:\>%NDK%/toolchains/llvm/prebuilt/windows-x86_64/bin/aarch64-linux-android21-clang++ src.cpp
open D:\android-toolchain2\arm64\bin\aarch64-linux-android21-clang change clang.exe to clang120.exe or what ever is there
#!/bin/bash
if [ «$1» != «-cc1» ]; then
dirname $0
/clang.exe —target=aarch64-linux-android21 «$@»
else
# Target is already an argument.
dirname $0
/clang120.exe «$@»
fi
pacman -S autoconf automake
pacman -S libtool
chmod +x ./build.sh
./build.sh
If you do not want to compile them yourself, you can download pre-compiled static libraries from releases. They are in build.tar.gz
.
Doing your own compilation is recommended, since the pre-compiled binary can become outdated soon.
Checkout newer versions in git submodules to compile newer versions of the libraries. For example, to build OpenSSL_1_1_1l
and curl-7_78_0
:
cd openssl git fetch git checkout OpenSSL_1_1_1l cd .. cd curl git fetch git checkout curl-7_78_0 cd ..
Usage
git clone https://github.com/robertying/openssl-curl-android.git cd openssl-curl-android git submodule update --init --recursive export NDK=your_android_ndk_root_here # e.g. $HOME/Library/Android/sdk/ndk/23.0.7599858 export HOST_TAG=see_this_table_for_info # e.g. darwin-x86_64, see https://developer.android.com/ndk/guides/other_build_systems#overview export MIN_SDK_VERSION=23 # or any version you want chmod +x ./build.sh ./build.sh
All compiled libs are located in build/openssl
and build/curl
directory.
Use NDK to link those libs, part of Android.mk
example:
include $(CLEAR_VARS) LOCAL_MODULE := curl LOCAL_SRC_FILES := build/curl/$(TARGET_ARCH_ABI)/libcurl.a include $(PREBUILT_STATIC_LIBRARY)
Options
-
Change scripts’ configure arguments to meet your own needs.
-
For now, using TLS (https) in Android would throw
peer verification failed
.Please explicitly set
curl_easy_setopt(curl, CURLOPT_CAINFO, CA_BUNDLE_PATH);
whereCA_BUNDLE_PATH
is your ca bundle path in the device storage.You can download and copy cacert.pem to Android assets or the device internal storage to get TLS working for libcurl.
Working Examples
-
See this minimal example which calls
curl
from Android app, usingJNI
to uselibcurl
: AndroidCurlExample. It includesAndroid.mk
setup andJNI
configurations. -
Checkout this more complex repo to see how to integrate other compiled static libraries into an existing Android project, including
Android.mk
setup andJNI
configurations.

Android Ndk Tutorial Aide Android Ide Try to build the files using the following command. this will force complete rebuild and it’ll show the commands that the ndk build script is executing. (i.e. verbose mode) ndk build b v=1 also make sure you use the latest android ndk android ndk r8d. There are six steps to building the openssl library for use in various projects, and they are listed below. projects range from simple ndk based command line programs to android activities using the jni bridge. first, obtain the base files from openssl.org source :.

Android Ndk My Blog I am trying to build the static library for the android arm platform from my windows 11 pc. i have set the respective environment variables in windows from the note android.md: android ndk root=c:\users\51010384\appdata\local\android\sd. Android : how to build openssl for android on windows with ndk8? to access my live chat page, on google, search for «hows tech developer connect» i have a hidden feature that. Example code of openssl usage with android ndk. contribute to areful ndkuseopenssl development by creating an account on github. I tried to build openssl for android with the following commands in windows command prompt:.

Getting Started With Android Ndk Android Tutorial Example code of openssl usage with android ndk. contribute to areful ndkuseopenssl development by creating an account on github. I tried to build openssl for android with the following commands in windows command prompt:. Use the following commands to build and install the openssl library for android. this article aims to cover those modifications and how to integrate the compiled openssl files into an ndk project. Android ndk i tried downloading openssl android. then run ndk build which is ndk8c in this case. i get the error: process begin: createprocess (null, pwd, ) failed. d: development android android ndk r8d build gmsl gmsl:512: *** non numeric second argument to `wordlist’ function: ». stop. when i apply a fix to gsml as described here. Working build scripts for openssl libcrypto and libssl static and dynamic libraries primarily for using on android. mac, and linux build script are kept and updated as well. windows version removed from current repo, look to original one for the instructions. Versions that out of support won’t receive security fixes anymore. all users of 1.0.2 and 1.1.0 are encouraged to upgrade to 1.1.1 as soon as possible. this article will provide instructions for building the latest openssl library version 1.1.1c for android devices.
How to build OpenDDS for Android and incorporate OpenDDS into Android apps.
Variables¶
The following table describes some of the variables of paths that are referenced in this guide.
Note
You don’t have to actually set and use these, they are mostly for shorthand.
Variable |
Description |
---|---|
|
OpenDDS being built for Android |
|
OpenDDS host to help build OpenDDS for Android |
|
ACE being built for Android |
|
The Android NDK |
|
The generated toolchain |
|
The Android SDK (By default |
|
Android Studio |
|
The Java SDK |
|
Install prefix for cross-compiled OpenSSL |
|
android_abi |
|
A Target architecture name used by Android NDK |
|
android_api, the minimum Android API version number |
|
android_target_api, the target Android API version number |
Requirements¶
To build the core OpenDDS native libraries for Android you will need:
-
A development system supported by both OpenDDS and the Android NDK.
-
Windows and Linux were tested, but macOS should work as well.
-
-
Android Native Development Kit (NDK) r18 or higher.
Building with the NDK directly requires NDK r19 or higher.
You can download it separately from https://www.android.com or using the SDK Manager that comes with Android Studio.
If you downloaded the NDK using the SDK Manager, then it is located at$SDK/ndk-bundle
. -
Some knowledge about OpenDDS and Android development will be assumed, but more OpenDDS knowledge will be assumed than Android knowledge.
-
Windows users should see Building on Windows for additional requirements they might need.
In addition to those, building OpenDDS with optional dependencies also have additional requirements listed in their own sections.
The Using OpenDDS in a Android App assumes the use of Android Studio, but it will also work when just using the Android SDK if tweaked.
Building on Windows¶
If using you’re using Windows Subsystem for Linux (WSL), Docker, or anything like that, follow the Linux and macOS instructions.
You can copy the resulting libraries from the virtual environment to Windows and where they can be used in Android Studio as they would be used on Linux.
If you want to build OpenDDS for Android on Windows without WSL or Docker, follow the Windows instructions.
In addition to OpenDDS and the Android NDK you will also need the following software:
-
MSYS2
-
Building OpenDDS and its dependencies for Android requires various utilities that would normally come on a Unix system.
This guide will use MSYS2, which supplies many of those utilities.
Install MSYS2 from the official website at https://www.msys2.org and set it up. -
Follow all the install/update steps from the msys2.org website.
-
-
Strawberry Perl
-
OpenDDS Host tools build using Visual Studio
-
In a separate copy of OpenDDS, build OpenDDS as described in Building and Installing using Visual Studio, except use the
--host-tools-only
configure script option.
This OpenDDS (and the ACE+TAO it uses) must be the same version as the one used to build for Android. -
If you want to use Java in the Android build, also pass the
--java
configure script option here as described in Java.
You will also need to pass it to the configure script build for
Android when that comes.
-
Finally, all paths being passed to GNU Make must not contain spaces because ACE’s gnuace make scripts don’t handle those paths correctly on Windows.
This means the NDK, toolchain, MSYS2, JDK, OpenDDS source, OpenDDS host tools, etc. must not contain any spaces in their paths.
Building OpenDDS for Android¶
As Android targets multiple architectures and has many versions, an architecture and minimum API version to use will have to be decided.
As of writing this page lists Android version numbers and their corresponding API versions.
You will have to do a separate build for each architecture if you want to build OpenDDS for multiple architectures.
OpenDDS for Android can be built in two ways: Using the NDK Directly or Using a Standalone Toolchain.
Using the NDK directly is recommended by Google and means that toolchains don’t have to be generated for each target architecture.
Using the NDK Directly¶
Note
Building with the NDK directly requires NDK r19 or later.
Note
If you need to configure OpenDDS with any optional dependencies then read the relevant sections before configuring and building OpenDDS.
OpenDDS can be configured and built with the Android NDK using the following commands:
./configure --doc-group3 --target=android --macros=android_abi=$ABI --macros=android_api=$MIN_API --macros=android_ndk=$NDK make # Pass -j/--jobs with an appropriate value or this'll take a while...
Using a Standalone Toolchain¶
To build OpenDDS with with a Android standalone toolchain, a standalone toolchain must first be generated by using:
Linux and macOS
$NDK/build/tools/make_standalone_toolchain.py --arch $ARCH_ARG --api $MIN_API --install-dir $TOOLCHAIN
Windows
Android NDK includes Python in prebuilt\windows-x86_64\bin
for 64-bit Windows NDKs.
For the example above, assuming %NDK%
is the location of the NDK and %TOOLCHAIN%
is the desired location of the toolchain, run this command instead:
%NDK%\prebuilt\windows-x86_64\bin\python %NDK%\build\tools\make_standalone_toolchain.py --arch %ARCH_ARG% --api %MIN_API% --install-dir %TOOLCHAIN%
The --arch
argument for make_standalone_toolchain.py
and --macros=android_abi=<ARCH>
argument for the configure script must match according to this table.
Note
If you need to configure OpenDDS with any optional dependencies then read the relevant sections before configuring and building OpenDDS.
To configure and build OpenDDS after this, run:
Linux and macOS
./configure --target=android --macros=android_abi=$ABI PATH=$PATH:$TOOLCHAIN/bin make # Pass -j/--jobs with an appropriate value or this'll take a while...
Windows
configure --target=android --macros=android_abi=%ABI% --host-tools=%HOST_DDS% set PATH=%PATH%;%TOOLCHAIN%\bin;C:\msys64\usr\bin make REM Pass -j/--jobs with an appropriate value or this'll take a while...
Note
-
Pass
--host-tools
with the location of the OpenDDS host tools that were built using Visual Studio must be passed toconfigure
. -
You will need MSYS2 utilities in your
%PATH%
. -
Run these commands in a new Visual Studio command prompt that is different from where you configured the host tools.
Configure Script Macros¶
These are GNU make variables that can be passed using the --macros
configure script option.
They are mostly used by platform_android.GNU.
android_ndk¶
Location of Android NDK, same as $NDK.
This is required when building with the NDK directly, but not when building with a standalone toolchain.
android_sdk¶
Location of Android SDK, same as $SDK.
This is only required if enabling OpenDDS to use Android Java APIs.
android_abi¶
The architecture to cross-target.
When using ACE6/TAO2 it is optional as it defaults to armeabi-v7a
.
When using ACE7/ACE3 it is required.
The valid options are:
|
|
|
Description |
---|---|---|---|
|
|
|
32-bit ARM |
|
|
|
32-bit ARM with NEON |
|
|
|
64-bit ARM |
|
|
|
32-bit x86 |
|
|
|
64-bit x86 |
android_api¶
The minimum Android API to target.
This is the same as $MIN_API.
This is required when building with the NDK, but not when building with a standalone toolchain.
android_target_api¶
The Android API being targeted by an application.
This is the same as $TARGET_API.
This is only required if enabling OpenDDS to use Android Java APIs.
Host Tools¶
To cross-compile OpenDDS, host tools are required to process IDL.
These are programs that include tao_idl and opendds_idl that have to be built to run on the host system, not Android.
The example above generates two copies of OpenDDS, one in OpenDDS/build/host
and another in OpenDDS/build/target
.
If this is the case, then $HOST_DDS
will be the absolute path to build/host
and $DDS_ROOT
will be the absolute path to build/target
.
If building for more than one architecture, which will be necessary to cover the largest number of Android devices possible, it might make sense to build the OpenDDS host tools separately to cut down on compile time and disk space.
If this is the case, then $HOST_DDS
will be the location of the static host tools built for the host platform and $DDS_ROOT
will just be the location of the OpenDDS source code.
This should be done with the same version of OpenDDS and ACE/TAO as what you want to build for Android.
Pass --host-tools-only
to the configure script to generate static host tools.
Also pass --java $JDK
if you plan on using Java.
If you want to just the minimum needed for host OpenDDS tools and get rid of the rest of the source files, you can.
These are the binaries that make up the OpenDDS host tools:
-
$HOST_DDS/bin/opendds_idl
-
$HOST_DDS/bin/idl2jni
(if using the OpenDDS Java API) -
$HOST_DDS/ACE_TAO/bin/ace_gperf
-
$HOST_DDS/ACE_TAO/bin/tao_idl
These files can be separated from the rest of the OpenDDS and ACE/TAO source trees, but the directory structure must be kept.
To use these to build OpenDDS for Android, pass --host-tools $HOST_DDS
to the configure script.
Optional Dependencies¶
Java¶
To use OpenDDS in the traditional Android development language, Java, you will need to build the Java bindings when building OpenDDS.
See java/README
for details.
For Android you can use the JDK provided with Android Studio, JDK=$STUDIO/jre
.
Pass --java=$JDK
to the OpenDDS configure script.
Android SDK¶
OpenDDS can make use of Android’s Java SDK.
Right now this is just used for allowing OpenDDS to always be notified of network availability when targeting API 30 and later.
OpenSSL¶
OpenSSL is required for OpenDDS Security.
Android preloads the system SSL library (either OpenSSL or BoringSSL) for the Java Android API, so OpenSSL MUST be statically linked to the OpenDDS security library.
The static libraries will used if the shared libraries are not found.
This can be accomplished by either disabling the generation of the shared libraries by passing no-shared
to OpenSSL’s Configure
script or just deleting the so
files after building OpenSSL.
Linux and macOS
To build OpenSSL for Android, read the NOTES.ANDROID
file that comes with OpenSSL’s source code.
Windows
Cross-compiling OpenSSL on Windows:
-
Start the MSYS2 MSYS development shell using the start menu shortcut or
C:\msys64\msys2_shell.cmd -msys
-
cd /c/your/location/of/OpenSSL-source
-
export ANDROID_NDK_HOME=/c/your/location/of/ndk-standalone-toolchain
-
PATH+=:$ANDROID_NDK_HOME/bin
-
./Configure --prefix=$SSL_ROOT android-arm no-tests no-shared
(or replace-arm
with a different platform like-arm64
, see OpenSSL’sNOTES.ANDROID
file) -
make install_sw
Xerces¶
Xerces C++ is also required for OpenDDS Security.
It does not support Android specifically, but it comes with a CMake build script that can be paired with the Android NDK’s CMake toolchain.
Xerces requires a supported “transcoder” library.
For API levels greater than or equal to 28 one of these, GNU libiconv, is included with Android.
Before 28 any of the transcoders supported by Xerces would work theoretically but GNU libiconv was the one tested.
If GNU libiconv is used, build it as an archive library (--disable-shared
) so that the users of Xerces (ACE and OpenDDS) don’t need to be aware of it as an additional runtime dependency.
Download GNU libiconv version 1.16 source code and extract the archive.
Cross-compiling on Windows¶
GNU libiconv¶
-
Start the MSYS2 MSYS development shell using the start menu shortcut or
C:\msys64\msys2_shell.cmd -msys
-
cd /c/your/location/of/libiconv-source
-
export ANDROID_NDK_HOME=/c/your/location/of/ndk-standalone-toolchain
-
PATH+=:$ANDROID_NDK_HOME/bin
-
target=arm-linux-androideabi
(or select a different NDK target) -
./configure --disable-shared --prefix=/c/your/location/of/installed-libiconv --host=$target CC=$target-clang CXX=$target-clang++ LD=$target-ld CFLAGS="-fPIE -fPIC" LDFLAGS=-pie
-
make && make install
Note
The directory given by --prefix=
will be created by make install
and will have include
and lib
subdirectories that will be used by the Xerces build.
Xerces¶
A modified version of Xerces C++ hosted on OpenDDS GitHub organization has support for an external GNU libiconv.
Download this version using git (android
branch) or the via ZIP archive.
Start the Microsoft Visual Studio command prompt for C++ development (for example “x64 Native Tools Command Prompt for VS 2019”).
cmake
and ninja
should be on the PATH.
They can be installed as on option component in the Visual Studio installer (see “C++ CMake tools for Windows”), or downloaded separately.
Set environment variables based on the NDK location and Android configuration selected:
-
set target=arm-linux-androideabi
-
set abi=armeabi-v7a
-
set api=16
-
set NDK=C:\your\location\of\NDK
-
set GNU_ICONV_ROOT=C:\your\location\of\installed-libiconv
Configure and build with CMake
-
cd C:\your\location\of\Xerces-for-android
-
mkdir build & cd build
-
cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=C:\your\location\of\installed-xerces -DCMAKE_TOOLCHAIN_FILE=%NDK%\build\cmake\android.toolchain.cmake -DANDROID_ABI=%abi% -DANDROID_PLATFORM=android-%api% "-DANDROID_CPP_FEATURES=rtti exceptions" ..
-
cmake --build . --target install
Cross-Compiling IDL Libraries¶
Like all OpenDDS applications, you will need to use type support libraries generated from IDL files to use most of OpenDDS’s functionality.
Assuming the library is already setup and works for a desktop platform, then
you should be able to run:
(source $DDS_ROOT/setenv.sh; opendds_mwc.pl && PATH=$PATH:$TOOLCHAIN/bin make)
The resulting native IDL library file must be included with the rest of the native library files.
Java IDL Libraries¶
Java support for your IDL, assuming OpenDDS was built with Java, will available by inheriting dcps_java
in your IDL MPC project and will be built along with the native IDL libraries using the command above.
Java IDL libraries consist of two components: a Java jar
library file and a supporting native library so
file.
This native library must be included with the other native library files, and is different than the regular native IDL type support library.
Using OpenDDS in a Android App¶
After building OpenDDS and generating the IDL libraries, you will need to set up an app to be able to use OpenDDS.
There is a demo for using OpenDDS over the Internet that includes an Android app built using these instructions.
Adding the OpenDDS Native Libraries to the App¶
In your app’s build.gradle
(NOT THE ONE OF THE SAME NAME IN THE ROOT OF THE PROJECT) add this to the android
section:
sourceSets { main { jniLibs.srcDirs 'native_libs' } }
native_libs
is not a required name, but it needs to contain subdirectories named after the android_abi
of the native libraries it contains ABI/architecture table.
The exact list of libraries to include depend on what features you’re using but the basic list of library file for OpenDDS are as follows:
-
Core OpenDDS library and its dependencies:
-
If not already included because of a separate C++ NDK project, you must include the Clang C++ Standard Library. This is located at:
-
Standalone toolchain:
$TOOLCHAIN/sysroot/usr/lib/$ABI_PREFIX/libc++_shared.so
-
NDK:
$NDK/toolchains/llvm/prebuilt/$HOST_PLATFORM/sysroot/usr/lib/$ABI_PREFIX/libc++_shared.so
-
$ABI_PREFIX
is an identifier for the architecture whose possible values can be found in the ABI/architecture table.
-
-
$ACE_ROOT/lib/libACE.so
-
$ACE_ROOT/lib/libTAO.so
-
$DDS_ROOT/lib/libOpenDDS_Dcps.so
-
-
The following are the transport libraries, one for each transport type.
You will need at least one of these, depending on the transport(s) you want to use:-
$DDS_ROOT/lib/libOpenDDS_Rtps_Udp.so
-
Depends on
$DDS_ROOT/lib/libOpenDDS_Rtps.so
-
-
$DDS_ROOT/lib/libOpenDDS_Multicast.so
-
$DDS_ROOT/lib/libOpenDDS_Shmem.so
-
$DDS_ROOT/lib/libOpenDDS_Tcp.so
-
$DDS_ROOT/lib/libOpenDDS_Udp.so
-
-
The type support libraries for your IDL.
-
The following are the Discovery libraries.
Static discovery is built intolibOpenDDS_Dcps.so
, but most likely you will want one of these: -
Required to use RTPS Discovery:
-
$DDS_ROOT/lib/libOpenDDS_Rtps.so
-
-
Required to use the DCPSInfoRepo Discovery:
-
$DDS_ROOT/lib/libOpenDDS_InfoRepoDiscovery.so
-
Depends on:
-
$ACE_ROOT/lib/libTAO_PortableServer.so
-
$ACE_ROOT/lib/libTAO_AnyTypeCode.so
-
$ACE_ROOT/lib/libTAO_BiDirGIOP.so
-
$ACE_ROOT/lib/libTAO_CodecFactory.so
-
$ACE_ROOT/lib/libTAO_PI.so
-
-
-
-
Required to use OpenDDS Security:
-
$ACE_ROOT/lib/libACE_XML_Utils.so
-
libxerces-c-3.*.so
-
libiconv.so
if it is necessary to include it. -
$DDS_ROOT/lib/libOpenDDS_Security.so
-
-
-
In addition to the jars listed below, the following native libraries are required for using the Java API:
-
$DDS_ROOT/lib/libtao_java.so
-
$DDS_ROOT/lib/libidl2jni_runtime.so
-
$DDS_ROOT/lib/libOpenDDS_DCPS_Java.so
-
Depends on:
-
$DDS_ROOT/lib/libOpenDDS_Rtps_Udp.so
-
$DDS_ROOT/lib/libOpenDDS_Rtps.so
-
$DDS_ROOT/lib/libOpenDDS_Tcp.so
-
$DDS_ROOT/lib/libOpenDDS_Udp.so
-
$ACE_ROOT/lib/libTAO_PortableServer.so
-
$ACE_ROOT/lib/libTAO_AnyTypeCode.so
-
$ACE_ROOT/lib/libTAO_BiDirGIOP.so
-
$ACE_ROOT/lib/libTAO_CodecFactory.so
-
$ACE_ROOT/lib/libTAO_PI.so
-
-
-
The native part of the Java library for your IDL libraries.
-
This list might not be complete, especially if you’re using a major feature not listed here.
Adding OpenDDS Java Libraries to the App¶
In your app’s build.gradle
(NOT THE ONE OF THE SAME NAME IN THE ROOT OF THE PROJECT) add this to the dependencies
section if not already there:
implementation fileTree(include: ['*.jar'], dir: 'libs')
Copy these jar files from $DDS_ROOT/lib
to a directory called libs
in your app’s subdirectory.
Create libs
if it doesn’t exist
Like native_libs
, the libs
name isn’t required.
-
i2jrt.jar
-
i2jrt_corba.jar
-
OpenDDS_DCPS.jar
-
tao_java.jar
-
The Java part of the Java library for your IDL libraries.
Also copy the jar files from your IDL Libraries and sync with Gradle if you’re using Android Studio.
After this OpenDDS Java API should be able to be used the same as if using OpenDDS with the Hotspot JVM.
The exceptions and particulars to how Android can effect OpenDDS are described in the following sections.
Network Permissions and Availability¶
In AndroidManifest.xml
you will need to add the network permissions if they are not already there:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Failure to do so will result in ACE failing to access any sockets and OpenDDS will not be able to function.
Network Availability¶
When not running in an application targeting API 30 or later on Android 10 or later, Android builds of OpenDDS use the LinuxNetworkConfigMonitor
to reconfigure OpenDDS connections automatically when the device switches from one network (cellular or WiFi) to another.
When running in an application targeting API 30 or later on Android 10, LinuxNetworkConfigMonitor
can no longer be used, as Netlink sockets are blocked by the OS for security reasons.
In the logs this warning shows up as:
WARNING: LinuxNetworkConfigMonitor::open_i: could not open Netlink socket (this is expected for API>=30, see Android section in the Developer’s Guide)
Instead, NetworkConfigModifier
is utilized.
As a consequence of this, two variables are required from the user, android_sdk, and android_target_api.
These correspond to the location of your Android SDK, likely $HOME/Android/Sdk
on Linux, and the API number you are targeting.
The NetworkConfigModifier
is set up along with the necessary network callbacks when the user uses TheParticipantFactory.WithArgs
.
Pass the following options to the configure
script make this possible:
--macros=android_sdk=$SDK --macros=android_target_api=$TARGET_API --java
Configuration Files¶
OpenDDS can use several types of configuration files: a main configuration file, security configuration files, and security certificate files, among others.
On traditional platforms, distributing and reading these files is usually not an issue at all.
On Android however, an app has no traditional files of its own out of the box, so you can’t give OpenDDS a path to a file you want to distribute with the app without preparing beforehand.
If you already have a preferred way to include files in your app, then that will work as long as you can give OpenDDS the path to the files.
Android can open a file stream for resource and asset files.
Ideally OpenDDS would be able to accept these streams, but it doesn’t.
One solution to this is reading the streams into memory and then writing them to files in the app’s private directory.
This example is using assets, but resources will also work with some slight modifications.
// ... private String copyAsset(String asset_path) { File new_file = new File(getFilesDir(), asset_path); final String full_path = new_file.getAbsolutePath(); try { InputStream in = getAssets().open(asset_path, AssetManager.ACCESS_BUFFER); byte[] buffer = new byte[in.available()]; in.read(buffer); in.close(); FileOutputStream out = new FileOutputStream(new_file); out.write(buffer); out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return full_path; } // ... @Override protected void onCreate(Bundle savedInstanceState) { // ... final String config_file = copyAsset("opendds_config.ini"); String[] args = new String[] {"-DCPSConfigFile", config_file}; StringSeqHolder argsHolder = new StringSeqHolder(args); dpf = TheParticipantFactory.WithArgs(argsHolder); // ... } // ...
This example works but in production code the error handling should be improved and integrated with the app’s initialization.
Rewriting the file every time is not ideal, but OpenDDS’s files are small and this method ensures the files are up-to-date.
Multithreading¶
When using a DataReaderListener, the callbacks will be using a ACE reactor worker thread, which can’t make changes to the Android GUI directly because it’s not the main thread.
To have these callbacks affect changes in the Android GUI, use something like android.os.Handler:
// ... import android.os.Handler; // ... public class DataReaderListenerImpl extends DDS._DataReaderListenerLocalBase { private MainActivity context; public DataReaderListenerImpl(MainActivity context) { super(); this.context = context; } public synchronized void on_data_available(DDS.DataReader reader) { StatusDataReader mdr = StatusDataReaderHelper.narrow(reader); if (mdr == null) { return; } StatusHolder mh = new StatusHolder(new Status()); SampleInfoHolder sih = new SampleInfoHolder(new SampleInfo(0, 0, 0, new DDS.Time_t(), 0, 0, 0, 0, 0, 0, 0, false, 0)); int status = mdr.take_next_sample(mh, sih); if (status == RETCODE_OK.value) { // ... Handler handler = new Handler(context.getMainLooper()); handler.post(new Runnable() { @Override public void run() { context.tryToUpdateThermostat(thermostat_status); } }); } } }
Android Activity Lifecycle¶
The Android Activity Lifecycle is something that affects all Android apps.
In the case of OpenDDS, the interaction gets more complicated because of the intersection of the similar, but distinct process lifecycle.
The process hosts the activity, but isn’t guaranteed to be kept alive after onStop()
is called.
What makes this worse for NDK applications is that there doesn’t seem to be a way to be warned of the killing of the process the way Java application can rely on onDestroyed()
.
For most OpenDDS applications, this isn’t a serious issue.
An easy way to make sure participants are cleaned up is to create participants in onStart()
as might be expected, and always delete them in onStop()
, so that they may be created again in onStart()
.
The DomainParticpantFactory
can be retrieved either in onStart()
or more perhaps appropriately in Application.onStart(), given the singleton nature of both.
This might not be ideal or efficient though, because deleting and recreating participants will happen every time the app loses focus, like during orientation changes.
An alternative to this is to run OpenDDS within an Android Service separate from the main app with the service configured so that it does not stopped when the Application’s onStop()
is called.
The service should be specified in AndroidManifest.xml
.
<service android:name=".OpenDdsService" android:exported="false" android:stopWithTask="true"> </service>
OpenDDS service classes should extend Service
and provide an IBinder
for an application to use when it creates the ServiceConnection
.
For example:
public class MainActivity extends AppCompatActivity { // ... private OpenDdsService svc = null; private ServiceConnection ddsServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { OpenDdsService.OpenDdsBinder binder = (OpenDdsService.OpenDdsBinder) service ; svc = binder.getService(); // ... } @Override public void onServiceDisconnected(ComponentName name) { // ... } } // ... }
public class OpenDdsService extends Service { // ... private final IBinder binder = new OpenDdsBinder(); public class OpenDdsBinder extends Binder { OpenDdsService getService() { return OpenDdsService.this; } @Override public void onCreate() { // ... } @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_NOT_STICKY; } @Override public IBinder onBind(Intent intent) { return binder; } @Override public void onRebind(Intent intent) { super.onRebind(intent); } @Override public void onDestroy() { super.onDestroy(); stopSelf(); } } }
See Android’s Services overview for more information.