Android系统开发入门-9.添加native系统服务

上一小节我们学习了 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