blob: 2cb85b2b4a1431a052e62116b669d8ae9a89086b [file] [log] [blame] [view]
# Platform APIs
The latest version of this document is available at
https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md.
## Implications of Adding a New Platform API
Before adding a platform API to the NDK, there are some guarantees and
restrictions that need to be considered.
### ABI Compatibility
The NDK ABI must be forward compatible. Apps that are in the Play Store must
continue to function on new devices. This means that once something becomes NDK
ABI, it must continue to be exposed and behavior must be preserved by the
platform for the lifetime of the ABI (for all intents and purposes, forever).
The NDK ABI includes but is not limited to:
* Exposed functions and global data.
* Layout and size of publicly visible data structures.
* Values of constants (such as enums).
### Source Compatibility
Source compatibility should be maintained, though exceptions have been made.
When appropriate, source compatibility breaking features can be limited to only
breaking when the user is targeting at least the API level that broke the change
(e.g. `#if __ANDROID_API__ >= 24`), which ensures that any currently building
app will continue building until the developer chooses to upgrade to a new
platform level.
Note that the definition of target API level in the NDK differs from the SDK.
For the NDK, the target API level is the minimum supported API level for the
app. If any non-ABI features are guarded by the target API level, they will only
be available to apps with a minimum target that includes that API level.
As a practical example of this, the NDK historically did not expose the `ALOG*`
log macros, only the `__android_log_*` functions. If the macros were to be added
but only exposed for API levels android-24 and newer, these helper macros that
could otherwise be available to Gingerbread would only be usable by developers
that chose to forfeit all Android users on devices older than android-24.
### API Restrictions
NDK APIs are C APIs only. This restriction may be lifted in the future, but at
the moment Android's C++ ABI is not guaranteed to be stable. Note that this does
not restrict the implementation of the API to C, only the interface that is
exposed in the headers.
NDK API headers can only depend on other NDK API headers. Platform headers from
android-base, libcutils, libnativehelper, etc are not available to the NDK.
## For Platform Developers
To get your API into the NDK you'll generally need to define the two pieces that
implement it: the headers and the libraries. Often the library is libandroid.so
and you won't need to add a new library, but you'll need to modify an existing
one. For this reason, libraries and the headers for those libraries are defined
separately.
### Headers
To add headers to the NDK, create an `ndk_headers` module in your Android.bp.
Examples of this module type can be found in [bionic/libc/Android.bp].
These module definitions are as follows:
```
// Will install $MODULE_PATH/include/foo/bar/baz.h to qux/bar/baz.h (relative to
// $NDK_OUT/sysroot/usr/include).
ndk_headers {
name: "foo",
// Base directory of the headers being installed. This path will be stripped
// when installed.
from: "include/foo",
// Install path within the sysroot.
to: "qux",
// List of headers to install. Relative to the Android.bp. Glob compatible.
// The common case is "include/**/*.h".
srcs: ["include/foo/bar/baz.h"],
// If true, makes the headers available to the platform but hides them from
// the NDK release. When actively developing an NDK API, `draft` should be
// set to true so the API isn't released until it is ready. It can still be
// used within the platform and CTS even if it is a draft.
//
// Note that false is the default, so when releasing an API this line may
// simply be removed.
draft: true,
}
```
### Libraries
To add a library to the NDK, create an `ndk_library` module in your Android.bp.
An example of this module type can be found in [bionic/libc/Android.bp].
These module defintions are as follows:
```
// The name of the generated file will be based on the module name by stripping
// the ".ndk" suffix from the module name. Module names must end with ".ndk"
// (as a convention to allow soong to guess the NDK name of a dependency when
// needed). "libfoo.ndk" will generate "libfoo.so.
ndk_library {
name: "libfoo.ndk",
// A version script with some metadata that encodes version and arch
// mappings so that only one script is needed instead of one per API/arch.
// An example of this file can be found at [bionic/libc/libc.map.txt].
//
// For a new library, this is something you'll need to create yourself.
// These should *not* be generated by `readelf -sW`ing your library. The
// purpose of these files is to explicitly list your APIs so you never
// accidentally expose private API that you'll need to support forever.
symbol_file: "libfoo.map.txt",
// The first API level a library was available. A library will be generated
// for every API level beginning with this one.
first_version: "9",
// Same behavior as the `draft` property in `ndk_headers`.
draft: true,
}
```
### Linker namespaces' public libraries list
You wouldn't be adding a library to the NDK unless you actually wanted apps to
be able to use your library, but in Android N or later, apps are only allowed
to access libraries that are part of the NDK API surface. The list of libraries
available to apps is stored in:
- `system/core/rootdir/etc/public.libraries.android.txt` for main Android
- `system/core/rootdir/etc/public.libraries.wear.txt` for Android Wear
devices
- `system/core/rootdir/etc/public.libraries.iot.txt` for Android Things devices
If a library is specific to either 32 or 64-bit ABI, append `32` or `64` to the
line (with space) to specify the bitness. e.g. `libfoo.so 32`. If neither 32 nor
64 is specified, the library is considered to be available for all ABIs of the
device.
By default, all the libraries listed in the above text file are preloaded when the
app process is started. This behavior can be controlled per library by appending
`nopreload` option. e.g. `libfoo.so nopreload`. Libraries with the option won't be
preloaded, but will be loaded on demand.
The `nopreload` option and the bitness option can be used together as in
`libfoo.so 32 nopreload`.
### CTS
There's also a CTS test to ensure that non-NDK libraries don't get added to
the public libraries list. This test can be found at
`cts/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java`. Simply
add your library to the `PUBLIC_SYSTEM_LIBRARIES` list in that file.
[bionic/libc/Android.bp]: https://android.googlesource.com/platform/bionic/+/master/libc/Android.bp
### Making sure it works
The best way to make sure you've set everything up properly is to add a test to
CTS. Make sure this test is built using the NDK (make sure `LOCAL_SDK_VERSION`
is set, `sdk_version` if you're using Android.bp). If your test passes in CTS
when it is built with the NDK, then everything has been set up properly.
Note that without this step, it is possible that one of the above steps will
have been done incorrectly and you wouldn't know without inspecting everything
yourself. If the `ndk_library` rule ends up in an Android.bp that never gets
parsed and there are no tests built with the NDK that use that library, your
build will still pass.
## For NDK Developers
The platform APIs reach the NDK via the "toolchain" module of the NDK (currently
via the intermediates of "sysroot" and "platforms". The sysroot currently is
just the headers, whereas the libraries are in platforms and CRT objects are
built as part of the NDK (an ELF note is added to the CRT objects so NDK
binaries contain NDK version information).
### Updating the Sysroot
The NDK sysroot is provided as prebuilts in `//prebuilts/ndk/platform`. To
update these, use `poetry run update-sysroot` (from the `//ndk` directory).
Prebuilts suitable for check-in must be taken from the build servers. However,
to test changes that have not yet been submitted to the platform, do the
following:
```bash
$ cd path/to/platform
$ OUT_DIR=ndk-out DIST_DIR=ndk-dist build/soong/scripts/build-ndk-prebuilts.sh
$ cd path/to/ndk/ndk
$ poetry run update-sysroot --no-download \
path/to/platform/ndk-dist/ndk_platform.tar.bz2
```
Note that most branches will include at least one codenamed release in the
sysroot artifacts. Clang only handles integer API levels, so these directories
must be renamed when creating the sysroot. This is done automatically by
`update-sysroot` based on the contents of [meta/platforms.json].
[meta/platforms.json]: ../meta/platforms.json