上一小节我们学习了 java 层的系统服务,但有时候出于性能方面的考虑,有些服务是需要使用 c++ 来实现的,比如音视频编解码,图形绘制等。 Android 系统原生的 MediaPlayerService 和 SurfaceFlinger 就是使用 c++ 实现的 Native 系统服务。这一小节我们就来学习一下如何添加一个 HelloNativeService。
1. 声明服务接口 我们先在 $device 目录下创建 HelloNativeService 目录
1 2 cd device/qiushao/pure mkdir -p models/HelloNativeService
然后创建 service 的头文件 HelloNativeService.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #ifndef ANDROID_10_HELLONATIVESERVICE_H #define ANDROID_10_HELLONATIVESERVICE_H #include <binder/IInterface.h> #include <binder/Parcel.h> #include <utils/Errors.h> using namespace android; class HelloNativeService: public BBinder { public: HelloNativeService(); static int instantiate(); virtual status_t onTransact(uint32_t, const Parcel&, Parcel*, uint32_t); }; #endif //ANDROID_10_HELLONATIVESERVICE_H
这里我们直接继承的是 BBinder, 而不是像其他教程一样又 BpInterface, 又 BnInterface 的,还把 BpInterface 跟 BnInterface 的实现放到同一个 cpp 文件中, 把 client 跟 server 的代码放到同一个文件中,个人感觉把简单的事情复杂化了。
2. 实现服务功能 创建文件 HelloNativeService.cpp
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 #include <android/log.h> #include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> #include "HelloNativeService.h" #define LOG_TAG "HelloNativeService" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) enum { CMD_SAY_HELLO = 1, CMD_CAL_SUM = 2 }; static void sayHello(const char *name) { LOGD("hello %s from HelloNativeService", name); } static int sum(int a, int b) { return a + b; } HelloNativeService::HelloNativeService() { LOGD("HelloNativeService created"); } int HelloNativeService::instantiate() { int r = defaultServiceManager()->addService(String16("HelloNativeService"),new HelloNativeService()); LOGD("add HelloNativeService r = %d", r); return r; } status_t HelloNativeService::onTransact(uint32_t code, const Parcel &request, Parcel *reply, uint32_t flag) { switch (code) { case CMD_SAY_HELLO: sayHello(request.readCString()); return NO_ERROR; case CMD_CAL_SUM: int a = request.readInt32(); int b = request.readInt32(); reply->writeInt32(sum(a, b)); return NO_ERROR; } return BBinder::onTransact(code, request, reply, flag); }
其中最重要的是 onTransact 函数的实现,这是一个回调函数,当 client 端通过 binder 调用 server 功能的时候, server 端的 onTransact 回调函数就会被调用。 各参数理解如下:
code : 表示要执行的动作,类似Handler发送的Message的what。code指示了当前远程操作的命令,IBinder定义了像INTERFACE_TRANSACTION、PING_TRANSACTION 这样的几个通用命令。自己使用的命令的标识值需要在FIRST_CALL_TRANSACTION和LAST_CALL_TRANSACTION之间。
request, reply : request 和 reply 参数相当于普通函数里的调用参数和返回值。Parcel类型是可以跨进程的数据。
flag : 参数 flags 只有 0 和 FLAG_ONEWAY 两种。0 表示阻塞调用, FLAG_ONEWAY 表示异步调用。这个参数不需要我们在服务端处理, Binder 框架会自动处理。
3. 添加 main.cpp 前面我们定义了服务接口而已,这个服务也是需要依赖在某个进程才能运行的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> #include "HelloNativeService.h" using namespace android; int main(int argc, char *argv[]) { sp<ProcessState> proc(ProcessState::self()); HelloNativeService::instantiate(); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); return 0; }
以上都是样板代码,具体 ProcessState 的功能作用是什么这里就不展开了。
4. Android.bp 1 2 3 4 5 6 7 8 9 10 11 cc_binary { name: "HelloNativeService", srcs: ["main.cpp", "HelloNativeService.cpp"], shared_libs: [ "liblog", "libcutils", "libutils", "libbinder", ], }
5. selinux 规则设置 为了简单起见,我们直接修改系统默认的 sepolicy 规则路径了。
5.1 添加 HelloNativeService.te system/sepolicy/private/HelloNativeService.te 和 system/sepolicy/prebuilts/api/29.0/private/HelloNativeService.te 文件内容保持一致
1 2 3 4 5 6 7 type HelloNativeService, domain; typeattribute HelloNativeService coredomain; type HelloNativeService_exec, system_file_type, exec_type, file_type; binder_use(HelloNativeService) init_daemon_domain(HelloNativeService) allow HelloNativeService HelloNativeService_service:service_manager { add find };
5.2 修改 file_contexts system/sepolicy/private/file_contexts 和 system/sepolicy/prebuilts/api/29.0/private/file_contexts 文件内容保持一致 添加以下配置
1 /system/bin/HelloNativeService u:object_r:HelloNativeService_exec:s0
5.3 修改 service_contexts system/sepolicy/private/service_contexts 和 system/sepolicy/prebuilts/api/29.0/private/service_contexts 文件内容保持一致 添加以下配置
1 HelloNativeService u:object_r:HelloNativeService_service:s0
5.4 修改 service.te system/sepolicy/private/service.te 和 system/sepolicy/prebuilts/api/29.0/private/service.te 添加以下配置
1 type HelloNativeService_service, service_manager_type;
6. 开机自动运行 在 device/qiushao/pure 目录添加rc文件 init.ranchu.rc
1 2 3 4 5 service HelloNativeService /system/bin/HelloNativeService class core user root group root seclabel u:r:HelloNativeService:s0
在 pure.mk 中添加以下配置, 这个配置需要添加在 $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_x86_64.mk) 之前。
1 PRODUCT_COPY_FILES += device/qiushao/pure/init.ranchu.rc:root/init.ranchu.rc
7. 编写 client 调用服务 我们把测试代码跟 service 放在同一个目录,直接在 HelloNativeService 目录下创建 HelloNativeTest.cpp 文件:
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 #include <binder/IServiceManager.h> #include <binder/Parcel.h> #include <utils/Errors.h> #include <stdio.h> using namespace android; enum { CMD_SAY_HELLO = 1, CMD_CAL_SUM = 2 }; static sp<IBinder> service; static void test_sayHello() { Parcel request, reply; request.writeCString("qiushao"); service->transact(CMD_SAY_HELLO, request, &reply); } static void test_calSum() { Parcel request, reply; request.writeInt32(2); request.writeInt32(3); service->transact(CMD_CAL_SUM, request, &reply); int sum = reply.readInt32(); printf("sum of 2 + 3 = %d\n", sum); } int main () { service = defaultServiceManager()->getService(String16("HelloNativeService")); test_sayHello(); test_calSum(); return 0; }
Android.bp 中添加
1 2 3 4 5 6 7 8 9 10 11 cc_binary { name: "HelloNativeTest", srcs: ["HelloNativeTest.cpp"], shared_libs: [ "liblog", "libcutils", "libutils", "libbinder", ], }
8. 编译运行 经过以上步骤,我们的 native 服务和测试 demo 就完成了,目录结构如下:
1 2 3 4 5 6 7 8 9 qiushao@qiushao-pc:~/source/android-10/device/qiushao/pure/models$ tree HelloNativeService/ HelloNativeService/ ├── Android.bp ├── HelloNativeService.cpp ├── HelloNativeService.h ├── HelloNativeTest.cpp └── main.cpp 0 directories, 5 files
我们还需要把刚刚添加的这两个模块添加到 PRODUCT_PACKAGES
1 PRODUCT_PACKAGES += HelloNativeService HelloNativeTest
然后编译运行,查看服务列表,可以看到我们的 HelloNativeService 已经在运行状态了。 然后执行 HelloNativeTest,服务端正确返回了计算结果。 查看一下 logcat, HelloNativeService 也正确收到了 HelloNativeTest 传递过来的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 qiushao@qiushao-pc:~/source/android-10$ adb shell pure:/ # service list | grep -i hello 15 HelloService: [android.pure.IHelloService] 161 HelloNativeService: [] pure:/ # pure:/ # HelloNativeTest sum of 2 + 3 = 5 pure:/ # pure:/ # logcat -s HelloNativeService --------- beginning of main --------- beginning of system 01-05 00:03:19.788 1661 1661 D HelloNativeService: HelloNativeService created 01-05 00:03:19.788 1661 1661 D HelloNativeService: add HelloNativeService r = 0/n 01-05 00:05:50.011 1661 1670 D HelloNativeService: hello qiushao from HelloNativeService