rk3566 安卓11 - 从驱动到 app

    由于一直从事驱动开发, 一直想对整体流程有个了解, 刚好看到这篇文章 AndroidQ 从app到驱动 第一章 编写Linux内核驱动程序. 于是参考这篇文章在 rk3566 上面完成了从驱动到 app 的实验验证. 文章记录用到的知识点以及遇到的问题和解决方法.

     整体框架大致分为如下 5 层.

一、添加 kernel 驱动

1. 驱动编写

    驱动部分写一个 misc 设备就行了, 提供简单的读写功能. 由于只是简单的验证功能所以没有越界处理和错误处理.

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
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/uaccess.h>

#define DEVICE_NAME "hello"

static char my_data[100] = "Hello, this is my_misc_device!\n";

static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{

printk("baron %s\n", __func__);

if (copy_to_user(buf, my_data, count))
return -EFAULT;

return count;
}

static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{

printk("baron %s\n", __func__);

if (copy_from_user(my_data, buf, count))
return -EFAULT;

return count;
}

static const struct file_operations my_fops = {
.owner = THIS_MODULE,
.read = my_read,
.write = my_write,
};

static struct miscdevice my_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &my_fops,
};

static int __init my_init(void)
{

int ret = misc_register(&my_misc_device);
if (ret) {
pr_err("Failed to register misc device\n");
return ret;
}

return 0;
}

static void __exit my_exit(void)
{
misc_deregister(&my_misc_device);
}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("baron");
MODULE_DESCRIPTION("A simple misc driver");

对应的 makefile 部分直接将驱动编进内核.

1
obj-y += hello.o

修改的文件如下所示

编译下载查看成功创建节点

1
2
rk3566_rgo:/ # ls /dev/hello
/dev/hello

2. 验证驱动

编写一个简单的应用程序验证驱动是 ok 的, 创建 external/test/test.c, 应用程序的内容如下.

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
#include <stdio.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>

int main(int argc, char* argv[])
{

char* buff = (char*)malloc(100);
int fd = -1;

buff[99] = '\0';

if(argc < 2)
return 0;

fd = open("/dev/hello", O_RDWR);
if(fd < 0){
printf("open /dev/mycdev err\n");
return -1;
}

if(!strcmp("write", argv[1])){
write(fd, argv[2], strlen(argv[2]));
printf("write %s to /dev/hello buf\n\n", argv[2]);
}else if(!strcmp("read", argv[1])){
read(fd, buff, 99);
printf("read data form /dev/hello : %s\n\n", buff);
}else {
printf("please use write or read cmd\n");
}

close(fd);

return 0;
}

添加 external/test/Android.bp 内容如下, 用来编译 bin 文件.

1
2
3
4
5
6
7
8
9
10
cc_binary {
name: "mytest",
srcs: ["test.c"],
shared_libs: [
"libbase",
"libcutils",
"liblog",
"libutils",
],
}

添加完成之后进入 external/test/ 运行 mmm . 编译. 编译完成之后如图, 得到 my_test

将其 push 到机器的 cache/ 目录. 验证结果如图所示, 驱动正常运行.

二、hall 层

    对 linux 驱动程序进行封装,其主要设计意图是向下屏蔽设备以及其驱动的实现细节,向上为系统服务以及 Framework 提供提供统一的设备访问接口。就是 linux 驱动只提供硬件读写接口, 业务逻辑通过 hall 封装成 so 库. 这样就不用遵循 kernel 的 gpl 开源协议, 从而保护厂商的利益. 不过也因为这个原因安卓被 linux 踢出了内核主线程.

1. 数据结构

1) hw_module_t

用来表示硬件的抽象

  • 每一个模块都必须自定义一个硬件抽象层模块结构体,而且他的第一个成员变量的类型必须为 hw_module_t
  • 硬件抽象层每一个模块都必须声明为HAL_MODULE_INFO_SYM
  • 结构体 hw_module_t 的成员变量 tag 的值必须设置为HARDWARE_MODULE_TAG
  • dso 用来保存加载硬件抽象层模块后得到的句柄值.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// libhardware/include/hardware/hardware.h
typedef struct hw_module_t {
uint32_t tag; // 值必须声明为 HARDWARE_MODULE_TAG

uint16_t module_api_version; // 模块 API 版本
#define version_major module_api_version

uint16_t hal_api_version; // HAL API 版本
#define version_minor hal_api_version

const char *id; // 模块的唯一标识符, 通过该标识符查找该 module
const char *name; // 模块的名称
const char *author; // 模块的作者
struct hw_module_methods_t* methods; // 模块的方法集合
void* dso; // 模块的共享对象(动态共享库)

#ifdef __LP64__
uint64_t reserved[32-7]; // 保留字段,64 位系统上使用 64 位整数数组
#else
uint32_t reserved[32-7]; // 保留字段,32 位系统上使用 32 位整数数组
#endif
} hw_module_t;

2) hw_module_methods_t

封装 open 函数, 通过 open 函数获取 hw_device_t

1
2
3
4
5
6
// libhardware/include/hardware/hardware.h
typedef struct hw_module_methods_t {
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);

} hw_module_methods_t;

3) hw_device_t

open 函数返回的结构, 硬件设备结构的第一个结构.

  • tag 的值必须设置为HARDWARE_DEVICE_TAG
  • close 回调接口用来关闭设备
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// libhardware/include/hardware/hardware.h
typedef struct hw_device_t {

uint32_t tag; // 赋值为 HARDWARE_DEVICE_TAG
uint32_t version; // 版本号
struct hw_module_t* module; // 属于哪个 hw_module_t

#ifdef __LP64__
uint64_t reserved[12];
#else
uint32_t reserved[12];
#endif

int (*close)(struct hw_device_t* device); // close 方法

} hw_device_t;

有了这几个接口就可以用来封装我们驱动接口了.

2. 程序编写

创建头文件: hardware/libhardware/include/hardware/hello.h 内容如下.

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
// include/hardware/hello.h
#ifndef ANDROID_INCLUDE_HARDWARE_HELLO_H
#define ANDROID_INCLUDE_HARDWARE_HELLO_H

#include <stdbool.h>
#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>

#include <hardware/hardware.h>
#include <hardware/hw_auth_token.h>

#define HELLO_HARDWARE_MODULE_ID "hello"

// 创建一个 hello_module_t 类用来描述硬件的抽象
// 它的第一个结构必须是 hw_module_t
// 它必须被实例化为 HAL_MODULE_INFO_SYM
typedef struct hello_module {
struct hw_module_t common;

}hello_module_t;

// 硬件设备结构 hello_device
// 它的第一个结构必须为 hw_device_t, 因为这样就可以通过 hw_device_t 拿到 hello_device
typedef struct hello_device {
struct hw_device_t common;
int fd;
int (*write_string)(struct hello_device* dev, const char *str);
int (*read_string)(struct hello_device* dev, char* str);
}hello_device_t;

#endif /* ANDROID_INCLUDE_HARDWARE_HELLO_H */

创建 hardware/libhardware/modules/hello/hello.c 文件

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
// hardware/libhardware/modules/hello/hello.c`
#define LOG_TAG "Legacy HelloHAL"

#include <malloc.h>
#include <stdint.h>
#include <log/log.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cutils/atomic.h>
#include <stdlib.h>
#include <unistd.h>
#include <hardware/hardware.h>
#include <hardware/hello.h>

#define DEVICE_NAME "/dev/hello"
#define MODULE_NAME "Default Hello HAL"
#define MODULE_AUTHOR "The Android Open Source Project"

// 接口声明
static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);
static int hello_device_close(struct hw_device_t* device);

static int hello_write_string(struct hello_device* dev, const char * str);
static int hello_read_string(struct hello_device* dev, char* str);

// 创建 hw_module_methods_t 用来提供 open 方法
static struct hw_module_methods_t hello_module_methods = {
.open = hello_device_open,
};

// 实例化 hello_module_t 硬件抽象模块为 HAL_MODULE_INFO_SYM
// ******** 必须实例化为 HAL_MODULE_INFO_SYM 这个名字不能变
hello_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG, // 必须设置为这个 tag
.module_api_version = 1,
.hal_api_version = 1,
.id = HELLO_HARDWARE_MODULE_ID, // 通过这个查找对应的 moduel
.name = MODULE_NAME,
.author = MODULE_AUTHOR,
.methods = &hello_module_methods, // 设置 open 方法
},
};

// open 方法的具体实现
static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device)
{
// 创建一个 hello_device_t 结构
hello_device_t *dev = malloc(sizeof(hello_device_t));
memset(dev, 0, sizeof(hello_device_t));

ALOGE("Hello: hello_device_open name = %s",name);
dev->common.tag = HARDWARE_DEVICE_TAG; // 必须设置为 HARDWARE_DEVICE_TAG
dev->common.version = 0;
dev->common.module = (hw_module_t*)module; // 设置 module
dev->common.close = hello_device_close; // 设置关闭设备接口
dev->write_string = hello_write_string; // 设置 write 方法
dev->read_string = hello_read_string; // 设置 read 方法

// 打开设备
if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
ALOGE("Hello: open /dev/hello fail-- %s.", strerror(errno));free(dev);
return -EFAULT;
}

// 返回 hw_device_t 结构
*device = &(dev->common);

ALOGE("Hello: open /dev/hello successfully.");
return 0;
}

// 关闭设备释放资源
static int hello_device_close(struct hw_device_t* device)
{
// 通过 device 可以拿到 hello_device
struct hello_device* hello_device = (struct hello_device*)device;

if(hello_device) {
close(hello_device->fd);
free(hello_device);
}
return 0;
}

// 写方法实现
static int hello_write_string(struct hello_device* dev,const char * str)
{
ALOGE("Hello:write string: %s", str);
write(dev->fd, str, sizeof(str));
return 0;
}

// 读方法实现
static int hello_read_string(struct hello_device* dev, char* str)
{
ALOGE("Hello:read hello_read_string");
read(dev->fd,str, sizeof(str));
return 0;
}

3. 编译程序

创建 hardware/libhardware/modules/hello/Android.bp 添加内容如下.

1
2
3
4
5
6
7
8
9
10
11
12
13
cc_library_shared {
name: "hello.default",
relative_install_path: "hw",
proprietary: true,
srcs: ["hello.c"],
cflags: ["-Wall", "-Werror"],
header_libs: ["libhardware_headers"],
shared_libs: [
"liblog",
"libcutils",
"libutils",
],
}

build/make/target/product/full_base.mk 中添加如下内容, 将我们的添加的 hall 库编译进系统.对应的位置为 /vendor/lib/hw/hello.default.so.

1
2
3
4
5
6
7
8
9
10
11
diff --git a/target/product/full_base.mk b/target/product/full_base.mk
index ffd3cde11a..9f7270bd2f 100644
-- a/target/product/full_base.mk
++ b/target/product/full_base.mk
@@ -32,7 +32,8 @@ PRODUCT_PACKAGES += \
# audio.a2dp.default is a system module. Generic system image includes
# audio.a2dp.default to support A2DP if board has the capability.
PRODUCT_PACKAGES += \
- audio.a2dp.default
+ audio.a2dp.default \
+ hello.default

运行 ./build.sh -UKAup 编译代码. 编译完成之后可以在 out 目录下发现 hello.default

1
2
$ find out/ -name "hello\.default"
out/soong/.intermediates/hardware/libhardware/modules/hello/hello.default

4. 验证程序

添加验证代码 frameworks/base/services/core/jni/com_android_server_AlarmManagerService.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
#include <hardware/hardware.h>
#include <hardware/hello.h>

struct hello_device* hello_device = NULL;

static inline int hello_device_open(const hw_module_t* module, struct hello_device** device)
{
return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
}

static jint HelloServiceInit()
{
ALOGE("HelloServiceInit HelloServiceInit");
const hw_module_t *hw_module = NULL;
ALOGE("Hello JNI: initializing......");

// 通过 HELLO_HARDWARE_MODULE_ID 找到对应的 hw_module
if(hw_get_module(HELLO_HARDWARE_MODULE_ID, &hw_module) == 0) {
ALOGE("Hello JNI: hello Stub found.");

// 调用 open 接口获取到 hello_device
if(hello_device_open(hw_module, &hello_device) == 0) {
ALOGE("Hello JNI: hello device is open.");
return 0;
}

ALOGE("Hello JNI: failed to open hello device.");
return -1;
}

ALOGE("Hello JNI: failed to get hello stub hw_module.");
return -1;
}

修改 system/sepolicy/vendor/file_contexts 添加库的位置让系统能够找到, 该正则表达式指定了库的位置为 /vendor/lib64/hw/hello.default.so

1
2
3
4
5
6
7
8
9
diff --git a/vendor/file_contexts b/vendor/file_contexts
index 1b2bc2357..92c166b6c 100644
-- a/vendor/file_contexts
++ b/vendor/file_contexts
@@ -86,6 +86,7 @@
/(vendor|system/vendor)/lib(64)?/hw/android\.hardware\.graphics\.mapper@4\.0-impl\.so u:object_r:same_process_hal_file:s0
/(vendor|system/vendor)/lib(64)?/hw/android\.hardware\.renderscript@1\.0-impl\.so u:object_r:same_process_hal_file:s0
/(vendor|system/vendor)/lib(64)?/hw/gralloc\.default\.so u:object_r:same_process_hal_file:s0
+/(vendor|system/vendor)/lib(64)?/hw/hello\.default\.so u:object_r:same_process_hal_file:s0

刷机开机打印 log 如下

1
2
3
4
5
6
7
01-18 07:32:55.775   419   419 E AlarmManagerService: HelloServiceInit HelloServiceInit
01-18 07:32:55.776 419 419 E AlarmManagerService: Hello JNI: initializing......
01-18 07:32:55.778 419 419 E AlarmManagerService: Hello JNI: hello Stub found.
01-18 07:32:55.778 419 419 E Legacy HelloHAL: Hello: hello_device_open name = hello
01-18 07:32:55.778 419 419 E Legacy HelloHAL: Hello: open /dev/hello fail-- Permission denied.
01-18 07:32:55.778 419 419 E AlarmManagerService: Hello JNI: failed to open hello device.

提示没没有权限

5. 添加权限

1) 设备节点添加权限

给我们的设备节点添加权限修改路径 system/core/rootdir/ 如下

1
2
3
4
5
6
7
8
9
10
11
12
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 1550894ce..428cc1ec2 100644
-- a/rootdir/ueventd.rc
++ b/rootdir/ueventd.rc
@@ -39,6 +39,7 @@ subsystem sound
/dev/vndbinder 0666 root root

/dev/pmsg0 0222 root log
+/dev/hello 0666 root root

# kms driver for drm based gpu
/dev/dri/* 0666 root graphics

添加权限之后报错如下

1
2
3
4
5
6
7
8
9
10
01-17 09:52:50.271   265   265 E MtpDeviceJNI: HelloServiceInit HelloServiceInit
01-17 09:52:50.272 265 265 E MtpDeviceJNI: Hello JNI: initializing......

// 多了这个信息需要增加 selinux 权限
01-17 09:52:50.276 265 265 W main : type=1400 audit(0.0:17): avc: denied { read write } for name="hello" dev="tmpfs" ino=11079 scontext=u:r:zygote:s0 tcontext=u:object_r:device:s0 tclass=chr_file permissiv
e=0
01-17 09:52:50.280 265 265 E MtpDeviceJNI: Hello JNI: hello Stub found.
01-17 09:52:50.280 265 265 E Legacy HelloHAL: Hello: hello_device_open name = hello
01-17 09:52:50.281 265 265 E Legacy HelloHAL: Hello: open /dev/hello fail-- Permission denied.
01-17 09:52:50.281 265 265 E MtpDeviceJNI: Hello JNI: failed to open hello device.

2) 添加 shell linux 权限

  1. system/sepolicy/public/device.te 中添加类别为 hello_device 的对象, 并且设置 attribute 为 dev_type. 修改如下
1
2
3
4
5
6
7
8
9
10
11
12
diff --git a/public/device.te b/public/device.te
index 32563d67c..cc6fc2881 100644
-- a/public/device.te
++ b/public/device.te
@@ -39,6 +39,7 @@ type serial_device, dev_type;
type socket_device, dev_type;
type owntty_device, dev_type, mlstrustedobject;
type tty_device, dev_type;
+type hello_device, dev_type;
type video_device, dev_type;
type zero_device, dev_type, mlstrustedobject;
type fuse_device, dev_type, mlstrustedobject;

修改的文件如下,修改的内容和上面是一样的.:

  1. 将 /dev/hello 设备节点和 SELinux 类别为 hello_device 的对象进行关联, 即我们前面创建的. 如下所示
1
2
3
4
5
6
7
8
9
10
11
12
13
zhaosheng@YF-zhaosheng:~/work2/ad500/system/sepolicy$ gf private/file_contexts
diff --git a/private/file_contexts b/private/file_contexts
index a5763bdf4..4475b1598 100755
-- a/private/file_contexts
++ b/private/file_contexts
@@ -102,6 +102,7 @@
/dev/input(/.*)? u:object_r:input_device:s0
/dev/iio:device[0-9]+ u:object_r:iio_device:s0
/dev/ion u:object_r:ion_device:s0
+/dev/hello u:object_r:hello_device:s0
/dev/keychord u:object_r:keychord_device:s0
/dev/loop-control u:object_r:loop_control_device:s0
/dev/modem.* u:object_r:radio_device:s0
  •  /dev/hello: 表示规则适用于 /dev/hello 这个设备节点
  •  u:object_r:hello_device:s0: 指定 SELinux 上下文,hello_device 是 SELinux 类别,s0 表示 SELinux 安全等级为 0

需要修改的文件如下, 修改的内容和上面是一模一样的.

编译完成后报错如下

1
2
3
4
5
6
7
8
9
01-18 07:07:17.681   421   421 E AlarmManagerService: HelloServiceInit HelloServiceInit
01-18 07:07:17.681 421 421 E AlarmManagerService: Hello JNI: initializing......
01-18 07:07:17.683 421 421 W system_server: type=1400 audit(0.0:17): avc: denied { read write } for name="hello" dev="tmpfs" ino=3901 scontext=u:r:system_server:s0 tcontext=u:object_r:hello_device:s0 tclass=c
hr_file permissive=0
01-18 07:07:17.683 421 421 E AlarmManagerService: Hello JNI: hello Stub found.
01-18 07:07:17.683 421 421 E Legacy HelloHAL: Hello: hello_device_open name = hello
01-18 07:07:17.683 421 421 E Legacy HelloHAL: Hello: open /dev/hello fail-- Permission denied.
01-18 07:07:17.683 421 421 E AlarmManagerService: Hello JNI: failed to open hello device.
01-18 07:07:17.685 421 421 E UsbAlsaJackDetectorJNI: Can't register UsbAlsaJackDetector na
  1. 增加 avc 权限

关键信息是这句话缺少 avc 权限, 请注意前面的报错是 tcontext=u:object_r:device 这里是 tcontext=u:object_r:hello_device 说前面的 hello_device 修改已经生效.

1
2
01-18 07:07:17.683   421   421 W system_server: type=1400 audit(0.0:17): avc: denied { read write } for name="hello" dev="tmpfs" ino=3901 scontext=u:r:system_server:s0 tcontext=u:object_r:hello_device:s0 tclass=c
hr_file permissive=0
  • 缺少什么权限: denied { read write } ==> 缺少 rw_file_perms 权限
  • 那个文件缺少权限: scontext=u:r:system_server:s0 ==> system_server.te 这个文件
  • 谁缺少权限: tcontext=u:object_r:hello_device:s0 ==> hello_device 这个对象
  • 文件类型: tclass=chr_file ==> chr_file 字符设备

这里的知识详细请参考: 浅谈SEAndroid安全机制及应用方法

于是在 system/sepolicy/private/system_server.te 增加

1
2
3
4
5
6
7
8
9
10
11
12
13
diff --git a/private/system_server.te b/private/system_server.te
index 3c1d192d7..d742471b1 100644
-- a/private/system_server.te
++ b/private/system_server.te
@@ -372,6 +372,7 @@ allow system_server video_device:chr_file rw_file_perms;
allow system_server adbd_socket:sock_file rw_file_perms;
allow system_server rtc_device:chr_file rw_file_perms;
allow system_server audio_device:dir r_dir_perms;
+allow system_server hello_device:chr_file rw_file_perms;

# write access to ALSA interfaces (/dev/snd/*) needed for MIDI
allow system_server audio_device:chr_file rw_file_perms;

修改的文件如下, 修改的内容和上面是一样的.

修改完成之后再次烧录验证查看 log, 正常发现设备正常打开 hall 层添加成功. 真不容易啊 =-=.

1
2
3
4
5
6
01-18 07:44:49.688   415   415 E AlarmManagerService: HelloServiceInit HelloServiceInit
01-18 07:44:49.688 415 415 E AlarmManagerService: Hello JNI: initializing......
01-18 07:44:49.690 415 415 E AlarmManagerService: Hello JNI: hello Stub found.
01-18 07:44:49.691 415 415 E Legacy HelloHAL: Hello: hello_device_open name = hello
01-18 07:44:49.691 415 415 E Legacy HelloHAL: Hello: open /dev/hello successfully.
01-18 07:44:49.691 415 415 E AlarmManagerService: Hello JNI: hello device is open.

验证 hall 需要修改的 selinux 相关文件如下. 全部都要改到不要偷懒.

参考: https://blog.csdn.net/Luoshengyang/article/details/6567257
参考: https://developer.aliyun.com/article/651348

三、添加 HelloManager 服务.

hall 层是啥, 就是 so 库, 这个 so 库是 c++ 写的的, 而我们的系统服务和 app 有部分是 java 写的. 没法直接用啊, 于是 JNI 闪亮登场. JNI 是干啥的, 很简单, 就是将我们 hall 层的 c++ 接口转换成 java 接口. 然后我们的服务再将这个 java 接口导出到服务. app 就可以直接通过服务的接口操作我们的 hello 设备啦.

正常的顺序应该是添加 JNI, 但是添加之后没法直接验证, 所以先加服务方便验证和理解. 服务是啥, 服务就是长期在后台运行的进程, 我们也称之为任务组件. 它不需要用户界面进行操作. 服务的本质是进程, 因此服务与服务之间, 服务与app之间的接口调用的本质是进程间通讯. 在安卓中就是 binder.

1. 添加 aidl 接口

AIDL(Android Interface Definition Language)是 Android 中用于定义进程间通信(IPC)接口的一种语言,我们可以借助 AIDL 工具给我们自动生成继承 binder 方法的类. 来添加我们的 AIDL 吧.

创建文件 frameworks/base/core/java/android/os/IHelloService.aidl 文件的内容如下所示.

1
2
3
4
5
6
7
package android.os;

/** {@hide} */
interface IHelloService {
void setVal(String value);
String getVal();
}

注意了在安卓 11 之后要加上 /** {@hide} */ 这个注释, 不加会报错的. 我在这里卡了一天 =-= , 查不出任何问题. 这个问题请参考这篇文章, 增加aidl 文件提示Methods calling system APIs should rethrow `RemoteException` as `RuntimeException.

添加之后 AIDL 工具会帮我们生成支持 binder 的方法的类, 但我们还需扩展这个类并且实现对应的方法. 创建文件 frameworks/base/core/java/com/android/server/HelloService.java. 内容如下

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
package com.android.server;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.IHelloService;
import android.os.RemoteException;
import android.os.TokenWatcher;
import android.os.UpdateLock;
import android.os.UserHandle;
import android.util.Slog;

import com.android.internal.util.DumpUtils;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;

// HelloService 继承由 AIDL 自动生成的 Binder Stub 类
public class HelloService extends IHelloService.Stub {

static final String TAG = "HelloService";
Context mContext;

// 构造函数
public HelloService(Context context) {
Slog.i(TAG, "HelloService init");
mContext = context;
}

// setVal 方法
@Override
public void setVal(String value) throws RemoteException {
Slog.i(TAG, "setVal value = "+value);
}

// getVal 方法实现
@Override
public String getVal() throws RemoteException {
Slog.i(TAG, "getVal ");
return "getVal";
}
}

这里 setVal 方法和 getVal 方法直接打印 log 就行了, 后面会替换为 jni 转换 hall 接口 之后的接口. 打开 frameworks/base/Android.bp 我们可以发现

1
2
3
4
5
6
7
8
filegroup {
name: "framework-core-sources",
srcs: [
"core/java/**/*.java",
"core/java/**/*.aidl",
],
path: "core/java",
}

安卓 11 之后默认就会添加 core/java/**/ 下的 java 和 aidl 文件因此就不再需要我们修改 bp 文件了.

2. 添加 HelloService

添加服务之前首先需在 Context 中添加我们 HELLO_SERVICE 常量, 以后通过这个常量我们就能获取到对应的服务了.修改如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5caf70aa72e0..210220bb315c 100644
-- a/core/java/android/content/Context.java
++ b/core/java/android/content/Context.java
@@ -3400,6 +3400,7 @@ public abstract class Context {
STORAGE_STATS_SERVICE,
WALLPAPER_SERVICE,
TIME_ZONE_RULES_MANAGER_SERVICE,
+ HELLO_SERVICE,
VIBRATOR_SERVICE,
//@hide: STATUS_BAR_SERVICE,
CONNECTIVITY_SERVICE,
@@ -5018,6 +5019,8 @@ public abstract class Context {
*/
public static final String TIME_ZONE_RULES_MANAGER_SERVICE = "timezone";

+ public static final String HELLO_SERVICE = "hello";
+
/**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.content.pm.CrossProfileApps} for cross profile operations.

接下来就是在 SystemServer 中注册 HelloService , 修改文件 frameworks/base/services/java/com/android/server/SystemServer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8cb3753c0ed6..57e31c5677de 100755
-- a/services/java/com/android/server/SystemServer.java
++ b/services/java/com/android/server/SystemServer.java
@@ -1226,6 +1226,9 @@ public final class SystemServer {
mSystemServiceManager.startService(PinnerService.class);
t.traceEnd();

+ Slog.i(TAG, "start hello Service");
+ ServiceManager.addService(Context.HELLO_SERVICE, new HelloService(context));
+
t.traceBegin("IorapForwardingService");
mSystemServiceManager.startService(IorapForwardingService.class);
t.traceEnd();

我们的 HelloService 也是在这里实例化的. 注册之后我们就可以通过 ServiceManager.getServiceOrThrow(Context.HELLO_SERVICE) 拿到 HelloService 的 binder, 然后通过这个 binder 获取到这里注册的 IHelloService 了.

3. 添加 HelloManager 服务

为了 app 能够获取到服务, 我们需要对 HelloService 再一次进行封装, 添加文件 frameworks/base/core/java/android/app/HelloManager.java

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
package android.app;

import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.IHelloService;
import android.util.Log;

public class HelloManager {
IHelloService mService;

/**
* @hide
*/
public HelloManager(Context ctx,IHelloService service){
mService = service;
}

/**
* @hide
*/
public void setVal(String value){
try{
Log.e("HelloManager","HelloManager setVal");
mService.setVal(value);
}catch(Exception e){
Log.e("HelloManager",e.toString());
e.printStackTrace();
}

}

/**
* @hide
*/
public String getVal(){
try{
Log.e("HelloManager","HelloManager getVal");
return mService.getVal();
}catch(Exception e){
Log.e("HelloManager",e.toString());
e.printStackTrace();
}
return null;
}
}

注意了 @hide 相关的注释不能去掉不然会报错. 接下来实例化这个服务并注册进系统

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
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 0e184fcc618b..cdafd815bb9f 100644
-- a/core/java/android/app/SystemServiceRegistry.java
++ b/core/java/android/app/SystemServiceRegistry.java
@@ -169,6 +169,7 @@ import android.os.image.IDynamicSystemService;
import android.os.incremental.IIncrementalService;
import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
+import android.os.IHelloService;
import android.os.IRnService;
import android.permission.PermissionControllerManager;
import android.permission.PermissionManager;
@@ -1233,6 +1234,23 @@ public final class SystemServiceRegistry {
}
});

+ registerService(Context.HELLO_SERVICE, HelloManager.class,
+ new CachedServiceFetcher<HelloManager>() {
+ @Override
+ public HelloManager createService(ContextImpl ctx) {
+ try {
+ IBinder b = ServiceManager.getServiceOrThrow(Context.HELLO_SERVICE);
+ Log.i(TAG, "registerService HELLO_SERVICE b = "+b);
+ IHelloService service = IHelloService.Stub.asInterface(b);
+ Log.i(TAG, "registerService HELLO_SERVICE service = "+service);
+ return new HelloManager(ctx, service);
+ } catch (ServiceNotFoundException e) {
+ Log.i(TAG, "registerService ServiceNotFoundException e = "+e);
+ onServiceNotFound(e);
+ return new HelloManager(ctx,null);
+ }
+ }});
+
registerService(Context.TIME_DETECTOR_SERVICE, TimeDetector.class,
new CachedServiceFetcher<TimeDetector>() {
@Override

修改的全部文件如下所示

运行 make update-api 更新接口, 运行之后会增加下面两个文件, 就是编译器自动给我们新增对应的接口.

之后再运行 ./build.sh -UKAup 整编. 刷机报错如下所示.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
01-18 11:26:10.933   429   429 I SystemServer: start hello Service
01-18 11:26:10.934 429 473 D PinnerService: pinRangeStream: null
01-18 11:26:10.935 429 429 I HelloService: HelloService init
01-18 11:26:10.935 429 473 D PinnerService: pinRangeStream: null
01-18 11:26:10.936 133 133 E SELinux : avc: denied { add } for pid=429 uid=1000 name=hello scontext=u:r:system_server:s0 tcontext=u:object_r:default_android_service:s0 tclass=service_manager permissive=0
01-18 11:26:10.936 429 473 D PinnerService: pinRangeStream: null
01-18 11:26:10.937 429 467 I DropBoxManagerService: add tag=system_server_strictmode isTagEnabled=true flags=0x2
01-18 11:26:10.940 429 429 E System : ******************************************
01-18 11:26:10.941 429 429 E System : ************ Failure starting core service
01-18 11:26:10.941 429 429 E System : ******************************************
01-18 11:26:10.947 429 473 D PinnerService: pinRangeStream: null
01-18 11:26:10.948 429 473 I chatty : uid=1000(system) android.bg identical 1 line
01-18 11:26:10.949 429 473 D PinnerService: pinRangeStream: null
01-18 11:26:10.955 429 429 E System : ************ Failure starting system services
01-18 11:26:10.955 429 429 E System : java.lang.SecurityException:

4. 增加 selinux 权限

请参考 SELinux权限问题解决参考

  1. 修改 system/sepolicy/public/service.te b/public/service.te
1
2
3
4
5
6
7
8
9
10
11
12
diff --git a/public/service.te b/public/service.te
index f27772eab..45d368f20 100644
-- a/public/service.te
++ b/public/service.te
@@ -146,6 +146,7 @@ type permissionmgr_service, app_api_service, ephemeral_app_api_service, system_s
type persistent_data_block_service, system_api_service, system_server_service, service_manager_type;
type pinner_service, system_server_service, service_manager_type;
type power_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;
+type hello_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;
type print_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;
type processinfo_service, system_server_service, service_manager_type;
type procstats_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;

需要改的文件如下, 每个都要改

  1. 修改 system/sepolicy/private/service_contexts b/private/service_contexts
1
2
3
4
5
6
7
8
9
10
11
12
diff --git a/private/service_contexts b/private/service_contexts
index 5c6f1a476..157737f81 100644
-- a/private/service_contexts
++ b/private/service_contexts
@@ -166,6 +166,7 @@ phone2 u:object_r:radio_service:s0
phone u:object_r:radio_service:s0
pinner u:object_r:pinner_service:s0
power u:object_r:power_service:s0
+hello u:object_r:hello_service:s0
print u:object_r:print_service:s0
processinfo u:object_r:processinfo_service:s0
procstats u:object_r:procstats_service:s0

需要修改的文件如下, 每个都要改

改完之后编译下载. log 如下成功添加服务.

1
2
3
4
5
01-18 12:02:30.899   429   429 D SystemServerTiming: PinnerService took to complete: 12ms
01-18 12:02:30.899 429 429 I SystemServer: start hello Service
01-18 12:02:30.900 429 464 I DropBoxManagerService: add tag=system_server_strictmode isTagEnabled=true flags=0x2
01-18 12:02:30.900 429 473 D PinnerService: pinRangeStream: null
01-18 12:02:30.901 429 429 I HelloService: HelloService init

我们 adb shell 之后运行 service list 在系统里面找见我们添加的服务.

四、Android studio 环境搭建

编写 app 调用 HelloManager 提供的服务. app 开发自然离不开 Android Studio. 这个工具是专门用来开发 app 的. 而不同版本之间对各个 api 的支持差异很大. 以下是我尝试使用过的不同版本的 Android Studio . 还有些被我删掉了. 总共试了十几个版本. 各版本下载地址.

本次实验成功使用的版本信息如下, 为 Android Studio 4.2 Beta 6

1
2
3
4
5
6
7
8
9
Android Studio 4.2 Beta 6
Build #AI-202.7660.26.42.7188722, built on March 6, 2021
Runtime version: 11.0.8+10-b944.6842174 amd64
VM: OpenJDK 64-Bit Server VM by N/A
Windows 10 10.0
GC: G1 Young Generation, G1 Old Generation
Memory: 1280M
Cores: 20
Registry: external.system.auto.import.disabled=true

对应的名字为 android-studio-ide-202.7188722-windows.exe, 重要的事情说三遍, 一定要用这个版本, 一定要用这个版本, 一定要用这个版本 !!!. 这个破软件向下兼容性极差. =-=

1. 安装软件

双击 exe 文件, 会出现下面安装提示, 点击 next

这里记得勾选 android vitual device.

接着设置安装路径, 我设置在 d 盘, 根据实际情况设置即可

接下来一直点 next 就行了

出现这个界面点击 cancel, 之后又是点击 next. 之后会提示配置 sdk 路径, 这里我选择默认 c 盘

接下来等待 sdk 的安装, 安装完成后界面如下.

到这里软件安装完成.

2. 配置软件

在打开新项目(project)之前要先配置 SDK manager. 这一点很重要, 因为直接打开默认的 api 接口是 34 版本的, 而且使用的工具是也是 34 版本的.

如图所示在左下角打开 SDK manager, 打开之后如下所示.

如图首先去掉 android api 34 然后勾选上 android 11. 然后点击 SDK Tools, 配置对应的工具

首先点击右下角 show package details 显示出详细的版本, 然后同样的把 34.0.0 去掉, 勾选上 30.0.230.0.3. 之后点击 apply.

如图点击 next 进入安装. 等待安装完成

  • SKD Platforms: 表示使用哪个 sdk , 安卓 11 对应的 sdk 是 30
  • SDK Tools: 和前面的版本需要对应

3. 构建项目

下载完成之后回到前面, 点击创建新项目 project

选择一个 empty activity.

语言选择 java, sdk 选择 Android 11, 之后点击 finish

这里选择 project 方便看代码

接下来会报错如下

到这里项目构建就结束了, 接下来就是配置项目解决报错

3. 配置项目

对目录结构有个了解. 此部分请参考 Android studio项目目录结构. 其中需要关注的目录如下.

目录 功能
libs 存放第三方 jar 包的目录, 我们要用我们自己的sdk编译出来包替换掉 as 中默认的包
MainActivity.java 程序从这里开始运行, 可以理解为 c 语言的 main 函数
app/build.gradle app 模块的构建配置, 包括 sdk 版本, 以及包含哪些 jar 包等
/build.gradle 根目录下的 build.gradle 是整个项目的配置文件, 用来指定整具体使用哪个 gradle 插件
gradle-wrapper.properties 指定了那个 gradle 工具
  • gradle 插件: 插件提供一些默认的 api 版本, 比如这里使用的安卓 11 就需要使用 gradle:4.2.0
  • gradle 工具: 用于整个项目的构建, 测试和部署.

    gradle 插件, gradle 工具, 和 java 版本(jdk版本) 之间的对应关系请参考 Android Studio Gradle插件版本与Gradle 版本对应关系.

1) 解决超时的报错

有了上述知识之后我们开始配置我们的项目, 首先解决超时报错, 修改文件 gradle\-wrapper.properties, 首先更换 gradle 工具的下载地址.

1
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-6.7.1-bin.zip

具体修改如图所示

然后打开根目录下 build.gradle 做如下修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
buildscript {
repositories {
maven { url 'https://maven.aliyun.com/repository/jcenter' }
maven { url 'https://maven.aliyun.com/repository/google'}
maven { url 'https://maven.aliyun.com/repository/gradle-plugin'}
maven { url 'https://maven.aliyun.com/repository/public'}
google()
mavenCentral()
}
dependencies {
//classpath "com.android.tools.build:gradle:4.2.0-beta06"
classpath "com.android.tools.build:gradle:4.2.0"

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

具体修改如图所示

完成之后点击右上角的 try again , 这个选项只有选中这个文件才会出现, 所以要构建项目要先选中这个文件, 不然右上角是不会出现这个选项的. 点击之后等待项目构建完成, 这个构建的时间比较长. 编译完成之后会有如下警告.

这个警告没有任何影响, 假装没看见就行了, 不要去注释掉 jcenter(), 注释掉会报错. 插上机器, 点击运行, 如下所示显示 hello world 就是编译成功了.

2) 导入本地 sdk 的 jar

虽然编译成功了但是调用的接口是, gradle 工具提供的默认接口, 现在还没法调用, 我们需要我们的 app 调用我们 sdk 中添加的 hello 服务能编译通过, 得加入 sdk 中编译出来的 jar 包. 对应的路径

1
2
$ ls out/target/common/obj/JAVA_LIBRARIES/framework-minus-apex_intermediates/
classes-header.jar classes.jar javalib.jar

我们需要 classes.jar , 记得在编译之前要把这个删掉再编译, 不然这个可能不会更新的, 导致这个包里面没有更新接口. 把这个拷贝到 app 的 libs 目录, 并且改名为 framework.jar, 然后在 app/gradle 中添加以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
Set<File> fileSet = options.bootstrapClasspath.getFiles()
List<File> newFileList = new ArrayList<>();
//JAVA语法,可连续调用,输入参数建议为相对路径
newFileList.add(new File("libs/framework.jar"))
//最后将原始参数添加
newFileList.addAll(fileSet)
options.bootstrapClasspath = files(
newFileList.toArray()
)
}
}

dependencies {

implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
compileOnly files('libs/framework.jar')
}

修改如图所示

注意了设置里面需要将 “generate *imx” 勾选上用于创建自动创建 imx 文件, 这个文件用来调整使用的 sdk 的顺序. 可以在这里将我们的 jar 提前.接着在根目录下的 build_gradle 中添加下面的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
allprojects {
repositories {
google()
mavenCentral()
jcenter() // Warning: this repository is going to shut down soon
}

tasks.withType(JavaCompile) {
options.compilerArgs.add('-Xbootclasspath/p:app\\libs\\framework.jar')
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}

gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs.add('-Xbootclasspath/p:app\\libs\\framework.jar')
}
}

具体修改内容如图所示

然后点击右上角的 sync now 再次构建. 构建完之后可以看到 framework.jar 已经是可以打开的状态. 同时自动为我们创建了 My_Application.app.imx 文件, 现在需要修改编译顺序, 将默认的sdk 放到最后面, 这样就会优先使用我们的 framework.jar 中的内容了.

到这里 Android studio 的环境就搭建完成了, 终于可以开始 app 的编写了.

5、 编写 app

这一步就比较简单了, 修改文件

1
MyApplication\app\src\main\java\com\example\myapplication\MainActivity.java

修改内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.app.HelloManager;
import android.content.Context;
import android.util.Slog;

public class MainActivity extends AppCompatActivity {

private HelloManager mHelloManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 通过 HELLO_SERVICE 这个静态变量获取到 HelloManager
mHelloManager = (HelloManager)getSystemService(Context.HELLO_SERVICE);
mHelloManager.setVal("Hello"); // 调用 setVal 方法
// 调用 getVal方法并打印 log
Slog.i("HelloServiceTest", "HelloService TEST getVal = "+mHelloManager.getVal());
}
}

修改完之后点击运行, 如下图所示.

同步 adb shell 之后 logcat 查看 log 如下, 系统服务接口调佣成功 ∧_∧

1
2
3
4
5
6
7
01-26 02:27:10.359  2251  2251 I SystemServiceRegistry: registerService HELLO_SERVICE b = android.os.BinderProxy@48b7bd8
01-26 02:27:10.360 2251 2251 I SystemServiceRegistry: registerService HELLO_SERVICE service = android.os.IHelloService$Stub$Proxy@2fbb31
01-26 02:27:10.360 2251 2251 E HelloManager: HelloManager setVal
01-26 02:27:10.361 434 1429 I HelloService: setVal value = Hello
01-26 02:27:10.362 2251 2251 E HelloManager: HelloManager getVal
01-26 02:27:10.362 434 1429 I HelloService: getVal
01-26 02:27:10.363 2251 2251 I HelloServiceTest: HelloService TEST getVal = getVal

6、添加 jni

这个部分的内容照搬 AndroidQ 从app到驱动 第五章 编写J NI 层完成 HelloService与 Hal 层的对接

创建文件 services/core/jni/com_android_server_HelloService.cpp, 将 hall 接口转换为 java 接口.

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
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "Hello_JNI"
#include "jni.h"
#include "core_jni_helpers.h"

#include <utils/Log.h>
#include <openssl/crypto.h>
#include <hardware/hardware.h>
#include <hardware/hello.h>

namespace {

struct hello_device* hello_device = NULL;

// 封装一下 open 方法
static inline int hello_device_open(const hw_module_t* module, struct hello_device** device) {
return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
}

// 初始化函数打开并返回 hello_device 结构
static jint HelloServiceInit(JNIEnv* env, jobject /* clazz */) {
ALOGE("com_android_server_HelloService HelloServiceInit");

const hw_module_t *hw_module = NULL;

ALOGE("Hello JNI: initializing......");

if(hw_get_module(HELLO_HARDWARE_MODULE_ID, &hw_module) == 0) {
ALOGE("Hello JNI: hello Stub found.");
if(hello_device_open(hw_module, &hello_device) == 0) {
ALOGE("Hello JNI: hello device is open.");
return 0;
}

ALOGE("Hello JNI: failed to open hello device.");
return -1;
}

ALOGE("Hello JNI: failed to get hello stub hw_module.");
return -1;
}


// SetVal 方法, 调用 hall 库中的 write_string 方法
static void HelloServiceNativeSetVal(JNIEnv* env, jobject clazz,jstring nativeValue) {
ALOGE("com_android_server_HelloService HelloServiceNativeSetVal");
if(!hello_device) {
ALOGI("Hello JNI: hello_device is not open.");
return;
}

const char* local_value = (nativeValue) ? env->GetStringUTFChars(nativeValue, NULL) : NULL;
ALOGE("com_android_server_HelloService HelloServiceNativeSetVal local_value = %s",local_value);
hello_device->write_string(hello_device, local_value);
ALOGI("Hello JNI: write string %s to Hello hello_device.", local_value);
env->ReleaseStringUTFChars(nativeValue, local_value);
}

// GetVat 方法, 调用 hall 库中的 read_string 方法
static jstring HelloServiceNativeGetVal(JNIEnv* env,jobject clazz) {
ALOGE("com_android_server_HelloService HelloServiceNativeGetVal");
if(!hello_device) {
ALOGE("Hello JNI: hello_device is not open.");
return NULL;
}

char read_str[100];
read_str[99] = '\0';
hello_device->read_string(hello_device,read_str);
ALOGE("Hello JNI: read string %s from Hello hello_device.", read_str);
jstring result = (env)->NewStringUTF(read_str);
return result;
}

// 第一个参数就是对应的 java 方法
// 映射随影的接口 void* HelloServiceInit ---> 映射为对应的 int HelloServiceInit() java 方法
// 映射随影的接口 void* HelloServiceNativeSetVal ---> 映射为对应的 void HelloServiceNativeSetVal(string) java 方法
// 映射随影的接口 void* HelloServiceNativeGetVal ---> 映射为对应的 string HelloServiceNativeGetVal() java 方法
static const JNINativeMethod methods[] = {
{"HelloServiceInit", "()I", (void*) HelloServiceInit},
{"HelloServiceNativeSetVal", "(Ljava/lang/String;)V",(void*) HelloServiceNativeSetVal},
{"HelloServiceNativeGetVal", "()Ljava/lang/String;",(void*) HelloServiceNativeGetVal},
};

}

namespace android {

// 注册回调函数
int register_android_server_HelloService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/HelloService", methods, NELEM(methods));
}

}

修改 services/core/jni/Android.bp 编译这个文件

1
2
3
4
5
6
7
8
9
10
11
12
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index d329e303e713..1fdb8654f5f0 100755
-- a/services/core/jni/Android.bp
++ b/services/core/jni/Android.bp
@@ -62,6 +62,7 @@ cc_library_static {
"com_android_server_rkdisplay_RkDisplayModes.cpp",
"com_android_server_audio_RkAudioSetting.cpp",
"com_android_server_RnService.cpp",
+ "com_android_server_HelloService.cpp",
"onload.cpp",
":lib_networkStatsFactory_native",
],

修改 services/core/jni/onload.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 987c8aca0a1f..34e41d2a7273 100755
-- a/services/core/jni/onload.cpp
++ b/services/core/jni/onload.cpp
@@ -67,6 +67,7 @@ int register_android_server_GpuService(JNIEnv* env);
int register_com_android_server_rkdisplay_RkDisplayModes(JNIEnv* env);
int register_com_android_server_audio_RkAudioSetting(JNIEnv* env);
int register_android_server_RnService(JNIEnv *env);
+int register_android_server_HelloService(JNIEnv *env);
};

using namespace android;
@@ -128,6 +129,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_com_android_server_rkdisplay_RkDisplayModes(env);
register_com_android_server_audio_RkAudioSetting(env);
register_android_server_RnService(env);
+ register_android_server_HelloService(env);

return JNI_VERSION_1_4;
}

最后在 core/java/com/android/server/HelloService.java 中调用转换之后的 java 接口

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
diff --git a/core/java/com/android/server/HelloService.java b/core/java/com/android/server/HelloService.java
index d6bb6d8d2b2e..e49255d666b5 100644
-- a/core/java/com/android/server/HelloService.java
++ b/core/java/com/android/server/HelloService.java
@@ -24,20 +24,26 @@ public class HelloService extends IHelloService.Stub {
static final String TAG = "HelloService";
Context mContext;

+ private native int HelloServiceInit();
+ private native void HelloServiceNativeSetVal(String value);
+ private native String HelloServiceNativeGetVal();
+
public HelloService(Context context) {
Slog.i(TAG, "HelloService init");
+ HelloServiceInit();
mContext = context;
}

@Override
public void setVal(String value) throws RemoteException {
Slog.i(TAG, "setVal value = " + value);
+ HelloServiceNativeSetVal(value);
}

@Override
public String getVal() throws RemoteException {
Slog.i(TAG, "getVal ");
- return "getVal";
+ return HelloServiceNativeGetVal();
}
}

需要修改的文件如下所示:

整编然后刷机打印 log 调用成功, app 成功调用驱动 hello 的接口.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
01-26 06:59:38.281  2402  2402 I SystemServiceRegistry: registerService HELLO_SERVICE b = android.os.BinderProxy@f30b5b3
01-26 06:59:38.283 2402 2402 I SystemServiceRegistry: registerService HELLO_SERVICE service = android.os.IHelloService$Stub$Proxy@a8c1770
01-26 06:59:38.283 2402 2402 E HelloManager: HelloManager setVal
01-26 06:59:38.284 434 502 I HelloService: setVal value = Hello
01-26 06:59:38.284 434 502 E Hello_JNI: com_android_server_HelloService HelloServiceNativeSetVal
01-26 06:59:38.284 434 502 E Hello_JNI: com_android_server_HelloService HelloServiceNativeSetVal local_value = Hello
01-26 06:59:38.284 434 502 E Legacy HelloHAL: Hello:write string: Hello
01-26 06:59:38.284 434 502 I Hello_JNI: Hello JNI: write string Hello to Hello hello_device.
01-26 06:59:38.405 0 0 W : baron my_write
01-26 06:59:38.285 2402 2402 E HelloManager: HelloManager getVal
01-26 06:59:38.285 434 502 I HelloService: getVal
01-26 06:59:38.285 434 502 E Hello_JNI: com_android_server_HelloService HelloServiceNativeGetVal
01-26 06:59:38.285 434 502 E Legacy HelloHAL: Hello:read hello_read_string
01-26 06:59:38.285 434 502 E Hello_JNI: Hello JNI: read string Hello from Hello hello_device.
01-26 06:59:38.406 0 0 W : baron my_read
01-26 06:59:38.286 2402 2402 I HelloServiceTest: HelloService TEST getVal = Hello