AOSP-FDBUS通信中间件编译、配置、通信测试

智能座舱域主流采用Android+QNX组合的方式,其中QNX作为仪表信息系统,而Android作为IVI车载娱乐系统。两者不可避免需要进行通信。

基于安全性和独立性要求,不同的系统之间数据不能直接访问。但是不同域下的不同系统之间,同一域下的不同系统之间的通信需求现实存在。现有的解决方案主要是基于TCP/IP协议栈的Socket(套接字)通讯。

大众在FDBUS基础上开发通信中间件完成系统间的通信。

FDBUS介绍

FDBus:https://gitee.com/jeremyczhen/fdbus
Fast Distributed Bus,基于Socket(Unix Domain和TCP)的快速分布式总线。
UDS:Unix Domain Socket,专用于IPC。Zygote中的Local Socket即为UDS。

FDBUS为了更方便的进行寻址,允许不通过IP+端口或者固定UDS地址,而是采用域名的方式进行寻址。类似于在浏览器输入 www.baidu.com ,与百度完成通信,则需要进行DNS解析,将域名转化为IP地址。
FDBUS同样提供了 name-server ,负责管理server名字(域名)与地址(IP)的映射,和DNS服务器一样,完成
server名字到IP的解析。

image

编译

将FDBUS源码下载后放入Android AOSP源码 /frameworks/native/services 中:

不是必须放入该目录,这里以此目录为例。

image

配置fdbus的android.bp文件

打开Android.bp文件,将其修改为如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
//=====================================================================================
// makefile to build fdbus in aosp source tree |
//=====================================================================================

//=====================================================================================
// build libfdbus.so |
//=====================================================================================

SRC_FILES = [
"fdbus/CBaseClient.cpp",
"fdbus/CFdbBaseObject.cpp",
"fdbus/CFdbMessage.cpp",
"fdbus/CFdbSimpleSerializer.cpp",
"fdbus/CBaseEndpoint.cpp",
"fdbus/CFdbCJsonMsgBuilder.cpp",
"fdbus/CFdbSessionContainer.cpp",
"log/CLogProducer.cpp",
"fdbus/CBaseServer.cpp",
"fdbus/CFdbContext.cpp",
"fdbus/CFdbBaseContext.cpp",
"fdbus/CFdbSession.cpp",
"fdbus/CFdbMsgDispatcher.cpp",
"fdbus/CEventSubscribeHandle.cpp",
"fdbus/CFdbUDPSession.cpp",
"fdbus/CBaseSession.cpp",
"fdbus/CFdbWatchdog.cpp",
"fdbus/CFdbEventRouter.cpp",
"platform/CEventFd_eventfd.cpp",
"platform/linux/CBaseMutexLock.cpp",
"platform/linux/CBasePipe.cpp",
"platform/linux/CBaseSysDep.cpp",
"platform/linux/CBaseThread.cpp",
"platform/socket/CBaseSocketFactory.cpp",
"platform/socket/linux/CLinuxSocket.cpp",
"platform/socket/sckt-0.5/sckt.cpp",
"platform/socket/CGenericClientSocket.cpp",
"platform/socket/CGenericServerSocket.cpp",
"platform/socket/CGenericSession.cpp",
"platform/socket/CGenericSocket.cpp",
"platform/socket/CGenericTcpSession.cpp",
"platform/socket/CGenericUdpSession.cpp",
"platform/socket/CGenericUdpSocket.cpp",
"security/CApiSecurityConfig.cpp",
"security/CFdbToken.cpp",
"security/CFdbusSecurityConfig.cpp",
"security/CHostSecurityConfig.cpp",
"security/CServerSecurityConfig.cpp",
"utils/fdb_option_parser.cpp",
"worker/CBaseEventLoop.cpp",
"worker/CBaseWorker.cpp",
"worker/CFdEventLoop.cpp",
"worker/CThreadEventLoop.cpp",
"worker/CSysFdWatch.cpp",
"utils/CBaseNameProxy.cpp",
"fdbus/CIntraNameProxy.cpp",
"server/CAddressAllocator.cpp",
"log/CLogPrinter.cpp",
"log/CFdbLogCache.cpp",
"utils/cJSON/cJSON.c",
"fdbus/CFdbAFComponent.cpp",
"datapool/CDataPool.cpp",
"datapool/CDpClient.cpp",
"datapool/CDpServer.cpp",
]

cc_library_shared {
name: "libfdbus",
vendor_available: true,

cppflags: [
"-Wno-non-virtual-dtor",
"-Wno-format-extra-args",
"-Wno-infinite-recursion",
"-Wno-unused-private-field",
"-Wno-unreachable-code-loop-increment",
"-frtti",
"-fexceptions",
"-Wno-unused-parameter",
"-D__LINUX__",
"-DFDB_CFG_SOCKET_PATH=\"/data/misc/fdbus\"",
"-DCONFIG_DEBUG_LOG",
"-DCONFIG_SOCKET_CONNECT_TIMEOUT=0",
"-DCONFIG_LOG_TO_STDOUT",
"-DCONFIG_FDB_NO_RTTI",
"-DCONFIG_FDB_MESSAGE_METADATA",
"-DFDB_CONFIG_UDS_ABSTRACT",
"-DCFG_ALLOC_PORT_BY_SYSTEM",
],
cflags: [
"-Wno-non-virtual-dtor",
"-Wno-format-extra-args",
"-Wno-infinite-recursion",
"-Wno-unused-private-field",
"-Wno-unreachable-code-loop-increment",
"-Wno-unused-parameter",
"-D__LINUX__",
"-DFDB_CFG_SOCKET_PATH=\"/data/misc/fdbus\"",
"-DCONFIG_DEBUG_LOG",
"-DCONFIG_SOCKET_CONNECT_TIMEOUT=0",
"-DCONFIG_LOG_TO_STDOUT",
"-DCONFIG_FDB_MESSAGE_METADATA",
"-DFDB_CONFIG_UDS_ABSTRACT",
"-DCFG_ALLOC_PORT_BY_SYSTEM",
],

shared_libs: [
"liblog",
"libutils",
],

srcs: SRC_FILES,

export_include_dirs: ["public"],

local_include_dirs: [],
}

//=====================================================================================
// build libfdbus-jni.so |
//=====================================================================================
cc_library_shared {
name: "libfdbus-jni",

cppflags: [
"-Wno-non-virtual-dtor",
"-Wno-format-extra-args",
"-Wno-infinite-recursion",
"-Wno-unused-private-field",
"-Wno-unreachable-code-loop-increment",
"-frtti",
"-fexceptions",
"-Wno-unused-parameter",
"-D__LINUX__",
"-DFDB_CFG_SOCKET_PATH=\"/data/misc/fdbus\"",
"-DCONFIG_DEBUG_LOG",
"-DCFG_JNI_ANDROID",
"-DFDB_CFG_KEEP_ENV_TYPE",
],
cflags: [
"-Wno-non-virtual-dtor",
"-Wno-format-extra-args",
"-Wno-infinite-recursion",
"-Wno-unused-private-field",
"-Wno-unreachable-code-loop-increment",
"-Wno-unused-parameter",
"-D__LINUX__",
"-DFDB_CFG_SOCKET_PATH=\"/data/misc/fdbus\"",
"-DCONFIG_DEBUG_LOG",
"-DCFG_JNI_ANDROID",
],
srcs: [
"jni/src/cpp/CJniClient.cpp",
"jni/src/cpp/CJniMessage.cpp",
"jni/src/cpp/CJniServer.cpp",
"jni/src/cpp/FdbusGlobal.cpp",
"jni/src/cpp/CJniAFComponent.cpp",
],

shared_libs: ["libfdbus"],

include_dirs: [
"frameworks/base/core/jni",
"frameworks/base/core/jni/include",
],
header_libs: ["jni_headers"],
}


//=====================================================================================
// build name-server |
//=====================================================================================
cc_binary {
name: "name-server",
vendor_available: true,
//该行注释不用,使用init.rc中进行启动配置
//init_rc: ["fdbus-name-server.rc"],
cppflags: [
"-Wno-non-virtual-dtor",
"-Wno-format-extra-args",
"-Wno-infinite-recursion",
"-Wno-unused-private-field",
"-Wno-unreachable-code-loop-increment",
"-frtti",
"-fexceptions",
"-Wno-unused-parameter",
"-D__LINUX__",
"-DFDB_CFG_SOCKET_PATH=\"/data/misc/fdbus\"",
"-DCONFIG_DEBUG_LOG",
],
cflags: [
"-Wno-non-virtual-dtor",
"-Wno-format-extra-args",
"-Wno-infinite-recursion",
"-Wno-unused-private-field",
"-Wno-unreachable-code-loop-increment",
"-Wno-unused-parameter",
"-D__LINUX__",
"-DFDB_CFG_SOCKET_PATH=\"/data/misc/fdbus\"",
"-DCONFIG_DEBUG_LOG",
],
srcs: [
"server/main_ns.cpp",
"server/CNameServer.cpp",
"server/CInterNameProxy.cpp",
"server/CIntraHostProxy.cpp",
"server/CBaseHostProxy.cpp",
"server/CSvcAddrUtils.cpp",
"server/CNameProxyContainer.cpp",
"security/CServerSecurityConfig.cpp",
],

shared_libs: [
"libfdbus",
"liblog",
"libutils",
],

}

上述脚本会编译出libfdbus.so、libfdbus-jni.so与name-server可执行文件。

libfdbus.so、libfdbus-jni.so的输出目录为:

/home/jia/AOSP/aosp/out/target/product/emulator_x86_64/system/lib

name-server的输出目录为:

/home/jia/AOSP/aosp/out/target/product/emulator_x86_64/system/bin

部署启动与权限配置

配置product

在Android12中在 /build/target/product/base_system.mk 中配置:

1
2
3
4
5
6
7
8
# Base modules and settings for the system partition.
PRODUCT_PACKAGES += \
......
surfaceflinger \
name-server \
libfdbus-jni \
libfdbus \
......

name-server启动配置

为了让name-server开机启动,需要在 init.rc 中配置启动脚本。

打开 /system/core/rootdir/init.rc 在文件最后加入:

1
2
3
4
5
6
7
8
#1、在挂载 /data 后创建/data/misc/fdbus 并设置system用户组权限
on post-fs-data
mkdir /data/misc/fdbus 0755 system system
#2、启动name-server
service name-server /system/bin/name-server -u tcp://139.224.136.101:60000 -n android
class core
group inet
writepid /dev/cpuset/system-background/tasks

1、因为FDBUS支持uds与tcp。在当前系统中其他进程需要连接name-server会采用UDS的方式,/data/misc/fdbus 则为name-server的uds固定地址。

1
mkdir /data/misc/fdbus 0755 system system:创建文件并设置权限。

2、-u 参数后文解释,-n 参数为当前name-server的别名可以随意传递。

1
2
class core:当前服务属主,系统能根据属主统一管理同属主下所有的进程,同一个class的所有服务必须同时启动或者停止。
group inet:当前服务的用户组。 inet 组表示允许网络访问!

/framework/base/data/etc/platform.xml 中可以查看权限对应的用户组:

image

name-server的SELinux权限配置

完成上述配置后,系统启动就会拉起name-server,但是此时name-server还无法正常工作。Android是建立在标准
的Linux Kernel基础上,通过Linux的 SELinux(SEAndroid)进行访问权限控制。比如name-server需要访问
tcp_socket、unix_stream_socket则必须开启SElinux的访问限制。

修改 system/sepolicy/prebuilts/api/31.0/privatesystem/sepolicy/private 目录下同
样的:file_contexts、netd.te与untrusted_app_all.te文件,并在两个目录下都创建name-server.te文件。

在file_contexts的最后一行加入:

1
/system/bin/name-server 	u:object_r:name-server_exec:s0

新建name-server.te 文件中内容为:

1
2
3
4
5
6
7
8
9
10
11
12
type name-server, domain, coredomain, mlstrustedsubject;
type name-server_exec, system_file_type, exec_type, file_type;

allow name-server self:tcp_socket { read write getattr getopt setopt shutdown create bind connect name_connect };
allow name-server self:netlink_route_socket {create write read nlmsg_readpriv nlmsg_read};
allow name-server fwmarkd_socket:sock_file {write};
allow name-server port:tcp_socket {name_connect};
allow name-server netd:unix_stream_socket {connectto};
allow name-server self:capability {net_raw};
allow name-server node:tcp_socket {node_bind};

init_daemon_domain(name-server)

同时name-server需要与netd进程(网络管理进程)交互,还需要在netd.te 增加:

1
2
3
4
5
6
7
8
9
typeattribute netd coredomain;
typeattribute netd domain_deprecated;
#增加==============
allow netd name-server:fd {use};
allow netd name-server:tcp_socket {getopt};
allow netd name-server:tcp_socket {setopt};
allow netd name-server:tcp_socket {read write};
#==============
#......

如果需要普通APP使用name-server, 还需要在 untrusted_app_all.te 中加入:

1
2
3
4
5
allow untrusted_app_all app_api_service:service_manager find;
allow untrusted_app_all vr_manager_service:service_manager find;
allow untrusted_app_all lance_service:service_manager find;
# 加入此处规则
allow untrusted_app_all name-server:unix_stream_socket{connectto};

FDBUS-API Framework-SDK集成

在上一步,我们不仅完成了name-server的部署,同时编译出libfdbus-jni.so,该动态库为FDBUS的Java层API的JNI封装。为了在Java中完成FDBUS的使用,可以将FDBUS集成进入Framework与SDK中。

将FDBUS中的 jni\src\java 下的Java代码放入AOSP源码中的 /frameworks/base/core/java 中

image

image

修改AOSP源码中的 build/soong/scripts/check_boot_jars/package_allowed_list.txt 。

image

修改 /framework/base/Android.bp中的metalava_framework_docs_argspackages_to_document 。前者表示忽略我们的Java代码中的lint检查;后者则代表将我们的Java API打包进入文档,这样才能在SDK中可见,也就是说APP能够调用新增的Java API。

1
2
3
4
5
6
7
8
metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) "+
"--api-lint-ignore-prefix ipc. "
//......

packages_to_document = [
//......
"ipc.fdbus"
]

编译与集成过程中的错误及解决

clang 编译错误

image

在CBaseClient.cpp的118行存在如下代码:

1
LOG_E("CClientSocket: client %s shutdown but try to request address of and connect again...\n",client->name().c_str(), client->nsName().c_str());

Android Log库输出日志,但是代码中传递了两个参数 client->name().c_str() 与 client->nsName().c_str() ,
然而只存在一个 %s 接收,此时参数不匹配。解决该问题的方式有两种:

修改源码

在第一个参数中加入一个 %s ,让参数与占位符数量匹配。

1
LOG_E("CClientSocket: client %s shutdown but try to request address of and connect %s again...\n",client->name().c_str(), client->nsName().c_str());

配置android.bp

在Android.bp中的name-server配置处的cflags与cppflags处加入:”-Wno-format-extra-args”,表示忽略该问题。

其他clang错误

继续编译,会出现很多与上一个类似的问题日志,都是因为Android12在编译时cflags/cppflags设置了-Werror,将
原本默认为警告的类型指定为了错误。

1
2
3
4
5
6
-Werror:把警告当作错误,出现任何警告就放弃编译;
-Werror=【type】:将指定类型的警告设置为error,如-Werror=unused-variable(当存在未使用的变量时
中断编译);
-Wno-error:忽略所有报错;
-Wno-error=【type】:忽略指定类型错误;
-Wno-【type】:忽略指定类型的警告;

按照日志提示,在Android.bp中所有模块的cflag与cppflags中加入:

1
2
3
4
5
"-Wno-non-virtual-dtor",
"-Wno-format-extra-args",
"-Wno-infinite-recursion",
"-Wno-unused-private-field",
"-Wno-unreachable-code-loop-increment",

最终Android.bp中所有的cflag与cpflags为:

image

jni.h not found

由于libfdbus-jni中需要使用jni,此时在Android12中编译会报出:

1
2
3
4
5
6
In file included from frameworks/base/core/jni/fdbus/CJniAFComponent.cpp:17:
frameworks/base/core/jni/fdbus/FdbusGlobal.h:19:10: fatal error: 'jni.h' file not found
#include <jni.h>
^~~~~~~
1 error generated.
22:15:22 ninja failed with: exit status 1

通过学习 framework/base/core/jni 下的Android.bp(编译libandroid_runtime.so)发现,其中存在如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
//......
static_libs: [
"libnativehelper_lazy",
"libziparchive_for_incfs",
],
//......
//导出头文件:谁依赖我,就能使用我导出的头文件
export_static_lib_headers: [
// AndroidRuntime.h depends on nativehelper/jni.h
"libnativehelper_lazy",
],
//......

可以发现libandroid_runtime.so依赖于libnativehelper_lazy.a,同时会导出libnativehelper_lazy的头文件。在
源码根目录下找到libnativehelper中的Android.bp,其中部分内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//头文件模块:jni_headers,包含include_jni下所有的头文件(该目录下存在一个头文件jni.h)
cc_library_headers {
name: "jni_headers",
export_include_dirs: ["include_jni"],
//......
}
//头文件模块:libnativehelper_header_only,包含header_only_include下所有的头文件
cc_library_headers {
name: "libnativehelper_header_only",
export_include_dirs: [
"header_only_include",
],
header_libs: ["jni_headers"], //依赖jni_headers模块
//......
}
//头文件模块:jni_platform_headers,包含include_platform_header_only下所有的头文件
cc_library_headers {
name: "jni_platform_headers",
export_include_dirs: [
"include_platform_header_only",
],
header_libs: ["jni_headers"],
//......
}
//动态库模块:libnativehelper.so
cc_library_shared {
name: "libnativehelper",
//导出头文件
export_include_dirs: [
"header_only_include",
"include",
"include_jni",
"include_platform",
"include_platform_header_only",
],
//......
}
//静态库:libnativehelper_lazy.a
cc_library_static {
name: "libnativehelper_lazy",
export_include_dirs: [
"header_only_include",
"include",
"include_jni",
"include_platform",
"include_platform_header_only",
],
//......
}
//......

很显然, framework/base/core/jni 下的libandroid_runtime.so 依赖静态库libnativehelper_lazy。同时在libnativehelper_lazy的配置中会导出 include_jni ,因此能使用 jni.h 。而我们的libfdbus-jni.so不需要依赖nativehelper,因此可以直接在libfdbus-jni的编译配置中加入:

1
header_libs: ["jni_headers"]

如果需要依赖nativehelper可以写成:

1
2
3
4
5
6
7
8
//依赖libnativehelper_lazy.a
static_libs: [
"libnativehelper_lazy",
],
//依赖libnativehelper.so
shared_libs:[
"libnativehelper",
],

因为在静态库和动态库中都将 include_jni 目录导出,因此依赖这些库即可使用 jni.h 。

package_allowed_list.txt

由于我们在Framework中增加了对应的Java API,此时编译报错如下错误: ipc.fdbus 包不允许打包进入Framework。

image

按照提示,在 build/soong/scripts/check_boot_jars/package_allowed_list.txt 中加入我们的包名:

image

selinux file_contexts错误

1
2
Error: could not load context file from out/target/product/emulator_x86_64/obj/ETC/plat_file_contexts_intermediates/plat_file_contexts
16:31:55 ninja failed with: exit status 1

这个错误最终定位到原因,挺扯淡的。是因为在file_contexts最后一行增加name_server上下文后没有增加空行引起的……

1
2
3
# ......
/system/bin/name-server u:object_r:name-server_exec:s0
# 最后一行配置之后必须预留一行,要不然编译时会报找不到最后一个file_contexts错误

抓取开机日志排查问题

  1. 先用命令adb reboot重启设备模拟器
  2. 然后立即输入命令adb root
  3. 然后adb wait-for-device shell dmesg >dmesg.txt

FDBUS部署与跨系统通信测试

车载系统中,由于Hypervisor的采用,有的域内可能会有多个节点。如智能座舱域,一个SOC芯片上可能会同时运行QNX和Android,虽然位于同一个SOC上,但还是被认为是两个节点。这两个节点必须打通,才能相互通信!

image

在Android与QNX系统中各自部署name-server,Android与QNX的name-server只能管理系统自身内部服务,为了
打通两个系统,FDBUS提供了host-server。
就好像DNS解析,本地DNS服务器无法解析,就会请求远程服务器。如果说name-server是本地DNS服务器,那么
host-server就是远程DNS服务器。

image

host-server部署

host-server可以搭建在域内,也可以搭建在域外,只要保证Android与QNX都能够访问即可。

为了方便测试,我们模拟一个通信环境,大致描述一下环境:

在ubuntu系统上跑一个android模拟器。

android模拟器可以理解为车载的android系统,内部运行有name-server

ubuntu系统可以理解为车载的QNX系统,内部运行了name-server、host-server和一个真正处理业务的service

我们需要在Ubuntu上搭建name-server和host-server服务,然后启动一个测试的service server

fdbus的完整编译参照文章 https://blog.csdn.net/u012739527/article/details/124011570

编译完成后,启动name-server、host-server、fdbxserver(fdbus提供的测试服务)

image

1
2
3
4
5
jia@jia-virtual-machine:~/work/fdbus/fdbus/build$ ./host_server &
[5] 13494
jia@jia-virtual-machine:~/work/fdbus/fdbus/build$ ./name_server &
[6] 13497
jia@jia-virtual-machine:~/work/fdbus/fdbus/build$ ./fdbxserver

客户端测试app

需要先将上文中aosp集成fdbus后编译出的SDK更新添加到studio的platform中以便app compile使用。

然后编写测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.example.myapplication

import android.util.Log
import ipc.fdbus.FdbusClient
import ipc.fdbus.FdbusClientListener
import ipc.fdbus.FdbusMessage


class QnxClient : FdbusClientListener {
private val mClient: FdbusClient

init {
mClient = FdbusClient()
mClient.setListener(this)
}

val client: FdbusClient
get() = mClient

override
fun onOnline(sid: Int, i1: Int) {
Log.i(TAG, "onOnline: $sid")
}
override
fun onOffline(sid: Int, i1: Int) {
Log.i(TAG, "onOffline: $sid")
}
override
fun onReply(fdbusMessage: FdbusMessage) {
Log.i(TAG, "onReply: " + fdbusMessage.toString())
}
override
fun onGetEvent(fdbusMessage: FdbusMessage) {
Log.i(TAG, "onGetEvent: " + fdbusMessage.toString())
}
override
fun onBroadcast(fdbusMessage: FdbusMessage) {
Log.i(TAG, "onGetEvent: " + fdbusMessage.toString())
}

companion object {
private const val TAG = "QnxClient"
}
}

调用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.example.myapplication

import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import ipc.fdbus.Fdbus
import ipc.fdbus.FdbusAppListener

class MainActivity : AppCompatActivity() {

private val TAG = "MainActivity"
private var qnxClient: QnxClient? = null

@SuppressLint("WrongConstant")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Fdbus()
qnxClient = QnxClient()

// 跨系统通信
qnxClient!!.client.connect("svc://org.fdbus.xtest-server")
}

fun sendQnx(view: View?) {
val fdbusMessage = qnxClient!!.client.invokeSync(1, "xxxxxx".toByteArray())
Log.i(TAG, "qnx响应: " + String(fdbusMessage.byteArray()))
}

fun disconnectQnx(view: View?) {
qnxClient!!.client.disconnect()
}
}

运行app并点击测试

image

控制台输出

1
qnx响应: xxxxxx

fdbxserver收到响应并回复

image