Google is committed to advancing racial equity for Black communities. See how.

AIDL Backends

An AIDL backend is a target for stub code generation. When using AIDL files, you always use them in a particular language with a specific runtime. Depending on the context, you should use different AIDL backends.

AIDL has the following backends:

Backend Language API surface Build systems
Java Java SDK (stable) all
NDK C++ libbinder_ndk (stable) aidl_interface
CPP C++ libbinder (unstable) all

The NDK backend is new in Android 10.

Build systems

Depending on the backend, there are two different ways to compile AIDL into stub code. More detailed documentation on the build systems can be seen in the Soong Module Reference.

Core build system

In any cc_ or java_ Android.bp module (or in their Android.mk equivalents), .aidl files can be specified as source files. In this case, the Java/CPP backends of AIDL are used (not the NDK backend), and the classes to use the corresponding AIDL files are added automatically to the module. Options (such as local_include_dirs, which tells the build system the root path to AIDL files in that module) can be specified in these modules under an aidl: group. For more details, see specific documentation on the build system in the Soong Module Reference.

aidl_interface

See Stable AIDL. Types used with this build system must be structured, that is, expressed in AIDL directly. This means that custom parcelables cannot be used.

Types

The aidl compiler can be considered a reference implementation for types. When creating an interface, you can see the resulting interface file by invoking aidl --lang=<backend> .... When using the aidl_interface module, the output can also be seen in out/soong/.intermediates/<path to module>/.

Java/AIDL Type C++ Type NDK Type
boolean bool bool
byte int8_t int8_t
char char16_t char16_t
int int32_t int32_t
long int64_t int64_t
float float float
double double double
String android::String16 std::string
android.os.Parcelable android::Parcelable N/A
IBinder android::IBinder ndk::SpAIBinder
T[] std::vector<T> std::vector<T>
byte[] std::vector<uint8_t> std::vector<int8_t>1
List<T> vector<T>2 N/A
FileDescriptor android::base::unique_fd N/A
ParcelFileDescriptor android::os::ParcelFileDescriptor ndk::ScopedFileDescriptor
interface type (T) android::sp<T> std::shared_ptr<T>
parcelable type (T) T T

1. In Android S or higher, byte arrays use uint8_t instead of int8_t for compatibility reasons.

2. The C++ backend only supports List<String> and List<IBinder>. In general, it is recommended to use array types like T[], since they work in all backends.

UTF8/UTF16

The C++ backend lets you to choose whether strings are utf-8 or utf-16. Declare strings as @utf8InCpp String in AIDL to automatically convert them to utf-8. The NDK backend always uses utf-8 strings. For more information about the utf8InCpp annotation, see Annotations in AIDL.

Nullability

For types which can be null in Java, they can be annotated with @nullable to expose null values to C++/NDK. By default native servers reject null values. The only exceptions to this are interface and IBinder types which can always be null. For more information about the nullable annotation, see Annotations in AIDL.

Custom Parcelables

In the C++ and Java backends in the core build system, you can declare a parcelable that is implemented manually in a target backend (in C++ or in Java).

    package my.package.Foo;
    parcelable Foo;

or with C++ header declaration:

    package my.package.Foo;
    parcelable Foo cpp_header "my/package/Foo.h";

Then, you can use this parcelable as a type in AIDL files, but it will not be generated by AIDL.

Exceptions

In Java, exceptions are used to propagate errors between services, such as android.os.RemoteException. However, native code in Android doesn't use exceptions, so the CPP backend uses android::Status, and the NDK backend uses ndk::ScopedAStatus to represent the exceptions that a method can return. Every method generated by AIDL returns one of these, representing the status of the method which is returned.

Using various backends

These instructions are specific to Android platform code. These examples use a defined type, my.package.IFoo.

Importing types

Whether the defined type is an interface or parcelable, you can import it in Java:

    import my.package.IFoo;

Or in the CPP backend:

    #include <my/package/IFoo.h>

Or in the NDK backend (notice extra aidl namespace):

    #include <aidl/my/package/IFoo.h>

Implementing services

To implement a service, you must inherit from the native stub class. This class reads commands from the binder driver and executes the methods that you implement. In Java, you must extend from this class:

    import my.package.IFoo;
    public class MyFoo extends IFoo.Stub { ... }

In the CPP backend:

    #include <my/package/BnFoo.h>
    class MyFoo : public my::package::BnFoo { ... }

In the NDK backend (notice extra aidl namespace):

    #include <aidl/my/package/BnFoo.h>
    class MyFoo : public aidl::my::package::BnFoo { ... }

Registering and getting services

Services in platform Android are usually registered with the servicemanager process. In addition to the APIs below, there are some APIs which check the service (meaning they return immediately if the service isn't available). Check the corresponding servicemanager interface for exact details. These operations can only be done when compiling against platform Android.

In Java:

    import android.os.ServiceManager;
    // registering
    ServiceManager.addService("service-name", myService);
    // getting
    myService = IFoo.Stub.asInterface(ServiceManager.getService("service-name"));
    // waiting until service comes up (new in Android 11)
    myService = IFoo.Stub.asInterface(ServiceManager.waitForService("service-name"));
    // waiting for declared (VINTF) service to come up (new in Android 11)
    myService = IFoo.Stub.asInterface(ServiceManager.waitForDeclaredService("service-name"));

In the CPP backend:

    #include <binder/IServiceManager.h>
    // registering
    defaultServiceManager()->addService(String16("service-name"), myService);
    // getting
    status_t err = getService<IFoo>(String16("service-name"), &myService);
    // waiting until service comes up (new in Android 11)
    myService = waitForService<IFoo>(String16("service-name"));
    // waiting for declared (VINTF) service to come up (new in Android 11)
    myService = waitForDeclaredService<IFoo>(String16("service-name"));

In the NDK backend (notice extra aidl namespace):

    #include <android/binder_manager.h>
    // registering
    status_t err = AServiceManager_addService(myService->asBinder().get(), "service-name");
    // getting
    myService = IFoo::fromBinder(SpAIBinder(AServiceManager_getService("service-name")));

Dynamically getting interface descriptor

The interface descriptor identifies the type of an interface. This is useful when debugging or when you have an unknown binder.

In Java, you can get the interface descriptor with code such as:

    service = /* get ahold of service object */
    ... = service.asBinder().getInterfaceDescriptor();

In the CPP backend:

    service = /* get ahold of service object */
    ... = IInterface::asBinder(service)->getInterfaceDescriptor();

The NDK backend doesn't support this functionality.

Statically getting interface descriptor

Sometimes (such as when registering @VintfStability services), you need to know what the interface descriptor is statically. In Java, you can get the descriptor by adding code such as:

    import my.package.IFoo;
    ... IFoo.DESCRIPTOR

In the CPP backend:

    #include <my/package/BnFoo.h>
    ... my::package::BnFoo::descriptor

In the NDK backend (notice the extra aidl namespace):

    #include <aidl/my/package/BnFoo.h>
    ... aidl::my::package::BnFoo::descriptor

Thread management

Every instance of libbinder in a process maintains one threadpool. For most usecases, this should be exactly one threadpool, shared across all backends. The only exception to this is when vendor code might load another copy of libbinder to talk to /dev/vndbinder. Since this is on a separate binder node, the threadpool isn't shared.

For the Java backend, the threadpool can only increase in size (since it is already started):

    BinderInternal.setMaxThreads(<new larger value>);

For the C++ backend, the following operations are available:

    // set max threadpool count (default is 15)
    status_t err = ProcessState::self()->setThreadPoolMaxThreadCount(numThreads);
    // create threadpool
    ProcessState::self()->startThreadPool();
    // add current thread to threadpool (adds thread to max thread count)
    IPCThreadState::self()->joinThreadPool();

Similarly, in the NDK backend:

    bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
    ABinderProcess_startThreadPool();
    ABinderProcess_joinThreadPool();