使用aidl-cpp生成c++ Binder接口
背景
"aidl"表达了几个相关但是不同的概念
- AIDL接口定义语言
- .aidl文件(含有AIDL)
- 将aidl转换为client/server ipc接口
aidl生成器是一个命令行工具,它从一个以.aidl结尾的文件中生成了client和server stub的binder接口。对于java接口,调用的可行性文件为aidl,而对于c++文件来说,则调用的是aidl-cpp。在本文档中,我们将使用AIDL来描述.aidl文件,aidl生成器使用代码生成工具,解析.aidl文件中的AIDL,然后输出代码;
以前aidl生成器只会生成interface/proxy/stub的java代码,c++的binder接口是手工写的,与java对应兼容。Brillo项目增加了aidl生成器对c++的支持,生成的c++实现了跨语言兼容(例如:经过测试,java client可以与native server端进行交互)。
总览
本文档重点描述了c++是如何生成的:
- 构建接口
- 跨语言类型映射
- 实现生成的接口
- c++打包
- 跨语言错误报告
- 跨语言空引用处理
- 跨语言整数常量
设计细节
建立接口
在以.aidl为后缀的文件中编写AIDL,然后添加到Android.mk的LOCAL_SRC_FILES中,如果构建目标是二进制文件(例如,include $(BUILD_SHARED_LIBRARY)),则生成的代码将是C ++,而不是Java。
AIDL的定义应该与实现位于同一资源库中,任何需要定义的系统都需要实现(包括parcelables和interface)。如果有多种实现(一种c++,一种java),则保持定义与native实现一致,Android系统现在具有无需java即可运行的native组件。
如果在AIDL中使用import语句,即使是在同一个package,也需要在LOCAL_AIDL_INCLUDES中添加所import文件的路径,此路径应该是相对于Android tree一个路径。例如:一个定义com.example.IFoo的IFoo.aidl文件,其路径层级结构为something/something-else/com/example/IFoo.aidl。然后我们就应该写:
LOCAL_AIDL_INCLUDES := something/something-else
最终生成的C++位于对应于接口包的嵌套名称空间中,生成的header也对应于接口包。例如:package com.example.IFoo,对应于namespace ::com::example::IFoo,头文件路径对应在“com/example/IFoo.h”。
类似于Java的工作方式,以.aidl结尾得文件路径必须匹配对应得package。如果IFoo.aidl声明自己在com.example的package中,则目录结构(如添加于LOCAL_SRC_FILES)必须类似于:/some/prefix/com/example/IFoo.aidl
如果需要从其他build target(例如其他二进制文件或者java)中.aidl文件生成代码,只需要添加.aidl的相对路径到LOCAL_SRC_FILES。其他目录中的代码导入AIDL的操作也是一样:添加其root路径(.aidl文件的root路径)到LOCAL_AIDL_INCLUDES。
补充(重要)
以上讲述全部基于Android.mk文件,那对应到Android.bp文件如何呢?以举例方式比较如下
Android.bp类似于一种json描述;Android.mk看上去就是一种环境变化的赋值过程;
假设目前/some/prefix/com/example/IFoo.aidl待应用,IFoo在com/example包中,Bn端继承的时候需要public com::example::BnIFoo,bp端就是讲iBinder转为com::example::IIFoo,实际上调用的是bpIFoo,最后通过bpBinder进行IPC,具体使用细节参考binder知识。
上述描述的Android.mk中的LOCAL_SRC_FILES用法:
LOCAL_SRC_FILES := /some/path
LOCAL_SRC_FILES += /some/prefix/com/example/IFoo.aidl
对应于Android.bp中会是:
src: [
"/some/path",
] + [
"/some/prefix/com/example/IFoo.aid",
],
上述描述中android.mk中的LOCAL_AIDL_INCLUDES的用法:
LOCAL_AIDL_INCLUDES += /some/prefix
对应于android.bp中会是:
aidl: {
include_dirs: ["/some/prefix"]
}
通过以上示例,细品,不多赘述了!
类型映射
下表总结了c++类型对应的Java类型,在AIDL文件中可能会用于in/out/inout参数。
| Java type | c++ type | inout | Notes |
|---|---|---|---|
| boolean | bool | in | "前8种类型被认为是基本类型" |
| byte | int8_t | in | |
| char | char16_t | in | |
| int | int32_t | in | |
| long | int64_t | in | |
| float | float | in | |
| double | double | in | |
| String | String16 | in | 支持空引用 |
| android.os.Parcelable | android::Parcelable | inout | |
| T extends IBinder | sp | in | |
| Arrays( T[ ] ) | vector | inout | |
| List | vector | inout | |
| PersistableBundle | PersistableBundle | inout | binder/PersistableBundle.h |
| List | vector<sp> | inout | |
| FileDescriptor | ScopedFd | inout | nativehelper/ScopedFd.h |
请注意,java.util.Map和java.utils.List并不是跨语言通信的良好选择,因为它们在Java端可能包含任意类型。例如,将Map强制转换为Map <String,Object>,然后动态检查对象值并将其序列化为类型/值对。支持发送任意Java可序列化文件,Android捆绑软件等。
实现生成的接口
给定一个接口声明,例如:
package foo;
import bar.IAnotherInterface;
interface IFoo {
IAnotherInterface DoSomething(int count, out List<String> output);
}
aidl-cpp 将会生成c++接口:
namespace foo {
// Some headers have been omitted for clarity.
#include <android/String16.h>
#include <cstdint>
#include <vector>
#include <bar/IAnotherInterface.h>
// Some class members have been omitted for clarity.
class IFoo : public android::IInterface {
public:
virtual android::binder::Status DoSomething(
int32_t count,
std::vector<android::String16>* output,
android::sp<bar::IAnotherInterface>* returned_value) = 0;
};
请注意,这aidl-cpp将导入.aidl文件中使用的导入的包类型。对于导入的类型(例如:包裹和接口),它将导入与导入的包/类名称相对应的标头。例如,import bar.IAnotherInterface。aidl-cpp生成#include <bar/IAnotherInterface.h>。
当编写service端的是实现时:
#include "foo/BnFoo.h"
namespace unrelated_namespace {
class MyFoo : public foo::BnFoo {
public:
android::binder::Status DoSomething(
int32_t count,
std::vector<android::String16>* output,
android::sp<bar::IAnotherInterface>* returned_value) override {
for (int32_t i = 0; i < count; ++i) {
output->push_back(String16("..."));
}
*returned_value = new InstanceOfAnotherInterface;
return Status::ok();
}
}; // class MyFoo
} // namespace unrelated_namespace
注意输出值output和returned_value通过指针传递,并且始终有效。
C++ Parcelables
parcelables可理解为:binder传输中的可打包对象。
在Java中,一个parcelables应该扩展android.os.Parcelable,并提供static final CREATOR的实现,另外如果需要做out参数的话,还需要提供一个readFormPacel方法。
在C++中,parcelables必须实现android::Parcelable,该原文件位于libbinder中的binder/Parcelable.h。Parcelables必须定义一个无参构造器,为了在数组中使用,一个parcelable必须实现一个拷贝构造器或者移动构造(在vector中隐式调用)。
C++ AIDL生成器需要知道parcelables定义在那个头文件中,他会从如下的cpp_header指令而得知。生成器会能拿到此字串bar/foo.h,并将其用include包含。
// ExampleParcelable.aidl
package com.example.android;
// Native types must be aliased at their declaration in the appropriate .aidl
// file. This allows multiple interfaces to use a parcelable and its C++
// equivalent without duplicating the mapping between the C++ and Java types.
// Generator will assume bar/foo.h declares class
// com::example::android::ExampleParcelable
parcelable ExampleParcelable cpp_header "bar/foo.h";
空引用处理
异常报告
整数常量
(后面三项暂时先不看了,用到再说)