I am a new to AIDL interface. I would like to implement AIDL interface between Java(Application layer) which is build using gradle and C++(Native layer) which is build using cmake. I need to pass a chunk of data between these two processes using AIDL interface. I am able to implement a .aidl file in application layer and able to create a service. I need to implement a aidl client in native layer and need to pass data. Can anyone suggest me how to implement AIDL which is to be build cmake.
To obtain the aidl-cpp binary, you would have to set up the AOSP source code using the instructions here and the first few instructions here. Once you have set up the build environment, you can build the binary with make aidl-cpp. The binary can then be found e.g. in out/host/linux-x86/bin/aidl-cpp. You only have to do this once, after you have obtained the binary you no longer need the AOSP code (though it is nice to have the code around to quickly search for examples).
Regarding the CMake part, as discussed in the comment, once you have build the aidl-cpp binary, you can use the CMake command add_custom_target for code generation.
The aidl-cpp command provides the following instructions:
usage: aidl-cpp INPUT_FILE HEADER_DIR OUTPUT_FILE
OPTIONS:
-I<DIR> search path for import statements
-d<FILE> generate dependency file
-ninja generate dependency file in a format ninja understands
INPUT_FILE:
an aidl interface file
HEADER_DIR:
empty directory to put generated headers
OUTPUT_FILE:
path to write generated .cpp code
So when you call the command from cmake, you would have to provide your AIDL, then a directory in which the generated header files should be stored, and the name under which the generated cpp file should be stored. E.g. aidl-cpp ISomeInterface.aidl ./generated-headers ./generated-cpp/ISomeInterface.cpp. If you want to use a custom data type (see the last section of this answer), you would also have to pass the -I flag to point to the declaration of your custom data types.
Once you have generated the header files and the cpp file with aidl-cpp, you can then connect to your service from C++. Here is an example from the AOSP source code:
There is an AIDL file called IDropboxManagerService.
The interface defined by the AIDL is implemented as DropboxManagerService in Java.
The service is called from C++ here:
Note the include of <com/android/internal/os/IDropBoxManagerService.h at the beginning of the file (link). This is the header file that has been generated by aidl-cpp from the AIDL file.
The default service manager is used to look-up the service called "dropbox":
defaultServiceManager()->getService(android::String16("dropbox"))
The result from getService is cast to the interface defined in the generated header file:
sp<IDropBoxManagerService> service = interface_cast<IDropBoxManagerService>(...)
The service object now holds a proxy object on which you can call the methods defined in the AIDL. The proxy object will then forward these calls via Binder to the Java service (this is implemented in the cpp file generated by aidl-cpp).
For example, the AIDL defines a method void add(in DropBoxManager.Entry entry);. The C++ code calls this method with Status status = service->add(entry);. You can check the result of the transaction with status.isOk().
Regarding data types, you can find a table with the mapping of C++ types to Java types here.
If you want to send custom objects, you would have to define a C++ class and a Java class, which both implement the Parcelable interface. This interface requires to implement two methods writeToParcel / readFromParcel in C++ and writeToParcel / createFromParcel in Java. You would have to provide matching implementations on each side, so that calling e.g. writeToParcel on the C++ object produces a flat binary encoding that can be read by readFromParcel on the Java side.
You would also have to provide an AIDL file that points to the header for your custom object using the cpp_header directive. See the following example:
In the DropBoxManagerService example from the previous section, we saw an object of type Entry.
On the C++ side, the class is declared here and implemented here.
On the Java side, the class is defined here.
In AIDL, a parcelable is declared here. Note this does not define any implementation details, it just declares there is a class of this name. It also points to the C++ header, which will be included by aidl-cpp.
The AIDL for the DropBoxManagerService that we looked at in the previous example imports the AIDL for the parcelable here. This allows to use this data type in the AIDL method declarations.
Based on these steps, the Entry class defined in C++ can then be send from C++ to Java. When passing the Entry object to the proxy object in C++, the cpp that was generated by aidl-cpp will use the writeToParcel function implemented for the Entry class to serialize the class into a flat binary encoding. On the Java side, the encoding is received and an Entry Java object is constructed from the encoding.
This example consist of an Activity, who binds a Service (Java) and passes the IBinder object to C++ JNI layer. Communication with the Service happens in JNI layer using NDK Binder APIs.
AIDL.
src/main/aidl/com/example/IMyService.aidl
package com.example;
import com.example.ComplexType;
interface IMyService
{
ComplexType returnComplexType(int anInt,
long aLong, boolean aBoolean,
float aFloat, double aDouble,
String aString);
}
src/main/aidl/com/example/ComplexType.aidl
package com.example;
parcelable ComplexType cpp_header "ComplexType.h";
build.gradle file has a Gradle task (compileAidlNdk) to auto-generate NDK C++ binder source files for IMyService.aidl.
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion '29.0.3'
defaultConfig {
minSdkVersion 29
externalNativeBuild {
cmake {
cppFlags "-std=c++17"
}
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
...
}
task compileAidlNdk() {
doLast {
def aidlCpp = [android.sdkDirectory,
'build-tools',
android.buildToolsVersion,
'aidl'].join(File.separator)
def outDir = [projectDir.absolutePath,
'src', 'main', 'cpp', 'aidl'].join(File.separator)
def headerOutDir = [projectDir.absolutePath,
'src', 'main', 'cpp', 'includes'].join(File.separator)
def searchPathForImports = [projectDir.absolutePath,
'src', 'main', 'aidl'].join(File.separator)
def aidlFile = [projectDir.absolutePath,
'src', 'main', 'aidl',
'com', 'example', 'IMyService.aidl'].join(File.separator)
exec {
executable(aidlCpp)
args('--lang=ndk',
'-o', outDir,
'-h', headerOutDir,
'-I', searchPathForImports,
aidlFile)
}
}
}
afterEvaluate {
preBuild.dependsOn(compileAidlNdk)
}
src/main/java/com/example/ndkbinderclient/MainActivity.java
package com.example.ndkbinderclient;
public class MainActivity extends AppCompatActivity implements ServiceConnection
{
static
{
System.loadLibrary("native-lib");
}
private volatile boolean mIsServiceConnected = false;
private final ConditionVariable mServiceConnectionWaitLock = new ConditionVariable();
public native void onServiceConnected(IBinder binder);
public native void onServiceDisconnected();
public native void talkToService();
@Override
protected void onResume()
{
super.onResume();
Intent intent = new Intent();
intent.setClassName("com.example",
"com.example.javabinderservice.MyService");
bindService(intent, this, BIND_AUTO_CREATE);
new Thread(new Runnable()
{
@Override
public void run()
{
// Not connected to service yet?
while(!mIsServiceConnected)
{
// waits for service connection
mServiceConnectionWaitLock.block();
}
talkToService();
}
}).start();
}
@Override
protected void onPause()
{
unbindService(this);
mIsServiceConnected = false;
onServiceDisconnected();
super.onPause();
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder)
{
onServiceConnected(iBinder);
mIsServiceConnected = true;
// breaks service connection waits
mServiceConnectionWaitLock.open();
}
@Override
public void onServiceDisconnected(ComponentName componentName)
{
mIsServiceConnected = false;
onServiceDisconnected();
}
}
src/main/cpp/native-lib.cpp
#include <jni.h>
#include <aidl/com/example/IMyService.h>
#include <android/binder_ibinder_jni.h>
std::shared_ptr<IMyService> g_spMyService;
extern "C" JNIEXPORT void JNICALL
Java_com_example_ndkbinderclient_MainActivity_onServiceConnected(
JNIEnv* env,
jobject /* this */,
jobject binder)
{
AIBinder* pBinder = AIBinder_fromJavaBinder(env, binder);
const ::ndk::SpAIBinder spBinder(pBinder);
g_spMyService = IMyService::fromBinder(spBinder);
}
extern "C" JNIEXPORT void JNICALL
Java_com_example_ndkbinderclient_MainActivity_onServiceDisconnected(
JNIEnv* env,
jobject /* this */)
{
g_spMyService = nullptr;
}
extern "C" JNIEXPORT void JNICALL
Java_com_example_ndkbinderclient_MainActivity_talkToService(
JNIEnv* env,
jobject /* this */)
{
ComplexType returnedComplexObject;
ScopedAStatus returnComplexTypeResult = g_spMyService->returnComplexType(2021,
65535000, true, 3.14f, 3.141592653589793238,
"Hello, World!", &returnedComplexObject);
}
src/main/cpp/includes/ComplexType.h
#pragma once
#include <android/binder_status.h>
class ComplexType
{
public:
int i_Int;
long l_Long;
bool b_Boolean;
float f_Float;
double d_Double;
std::string s_String;
public:
binder_status_t readFromParcel(const AParcel* pParcel)
{
int32_t iNotNull;
AParcel_readInt32(pParcel, &iNotNull);
AParcel_readInt32(pParcel, &i_Int);
AParcel_readInt64(pParcel, &l_Long);
AParcel_readBool(pParcel, &b_Boolean);
AParcel_readFloat(pParcel, &f_Float);
AParcel_readDouble(pParcel, &d_Double);
ndk::AParcel_readString(pParcel, &s_String);
return STATUS_OK;
}
binder_status_t writeToParcel(AParcel* pParcel) const
{
int32_t iNotNull = 1;
AParcel_writeInt32(pParcel, iNotNull);
AParcel_writeInt32(pParcel, i_Int);
AParcel_writeInt64(pParcel, l_Long);
AParcel_writeBool(pParcel, b_Boolean);
AParcel_writeFloat(pParcel, f_Float);
AParcel_writeDouble(pParcel, d_Double);
ndk::AParcel_writeString(pParcel, s_String);
return STATUS_OK;
}
};
src/main/cpp/CMakeLists.txt
cmake_minimum_required(VERSION 3.10.2)
add_library (
native-lib
SHARED
native-lib.cpp
aidl/com/example/IMyService.cpp
)
target_include_directories (
native-lib
PRIVATE
includes
)
target_link_libraries (
native-lib
binder_ndk
)
src/main/java/com/example/ndkbinderservice/MyService.java
package com.example.javabinderservice;
public class MyService extends Service
{
private IBinder mBinder;
@Override
public void onCreate()
{
super.onCreate();
mBinder = new MyServiceBinder();
}
@Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
private static class MyServiceBinder extends IMyService.Stub
{
@Override
public ComplexType returnComplexType(int anInt,
long aLong, boolean aBoolean,
float aFloat, double aDouble,
String aString) throws RemoteException
{
return new ComplexType(anInt, aLong, aBoolean, aFloat,
aDouble, aString);
}
}
}
src/main/java/com/example/ComplexType.java
public class ComplexType implements Parcelable
{
public final int mInt;
public final long mLong;
public final boolean mBoolean;
public final float mFloat;
public final double mDouble;
public final String mString;
protected ComplexType(Parcel in)
{
mInt = in.readInt();
mLong = in.readLong();
mBoolean = in.readBoolean();
mFloat = in.readFloat();
mDouble = in.readDouble();
mString = in.readString();
}
@Override
public void writeToParcel(Parcel parcel, int i)
{
parcel.writeInt(mInt);
parcel.writeLong(mLong);
parcel.writeBoolean(mBoolean);
parcel.writeFloat(mFloat);
parcel.writeDouble(mDouble);
parcel.writeString(mString);
}
...
}
src/main/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example">
<application>
<activity android:name=".ndkbinderclient.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".ndkbinderservice.MyService"
android:exported="true">
</service>
</application>
</manifest>
Complete source code can be found at https://github.com/lakinduboteju/AndroidNdkBinderExamples
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With