基于Android 6.0源码,基于实操在Android系统中如何添加一个framework层的服务访问驱动程序。
1. 概述
在Android添加系统服务访问驱动程序系列:添加HAL层中我们为 驱动 mychar 是实现一个HAL层模块 operatechar。开发好 HAL 层模块后,我们需要在framework 层实现一个硬件访问服务 operate_char。硬件访问服务operate_char通过HAL 层模块的operatechar 为上层应用程序提供硬件设备的读写操作。 Android 系统的硬件访问服务operate_char 跟Android 系统的AMS 、PMS等系统核心服务一样运行在系统进程System 中,而使用 operate_char 服务则运行在另外的进程中,因此需要 Binder来进行进程间通信。接下来我们分别详细讲解实现硬件访问服务operate_char的步骤。
2. 定义硬件访问服务接口
Android 系统提供了一种描述语言来定义具有跨进程访问能力的服务接口,这种描述语言称为Android接口描述语言(AIDL)。以AIDL定义的服务接口文件是以adil为后缀的,在编译时,编译系统会将它们转换成Java 文件,然后在对它们进程编译。在本节中我们将使用AIDL来定义硬件访问服务接口 IOperateCharService。
[->frameworks/base/core/java/android/operatechar/IOperateCharService.aidl]
package android.operatechar;
/**
* {@hide}
*/
interface IOperateCharService {
String read(int maxLength);
void write(String mString);
}
IOperateCharService 服务接口只定义了两个成员函数,分别是 read 和 write ,作用分别是对虚拟设备节点 /dev/mychar 进行读写。(/dev/mychar有内核驱动程序创建,详情见Android添加系统服务访问驱动程序系列:添加驱动程序) 在下面的文件中修改:
[->/frameworks/base/Android.mk]
LOCAL_SRC_FILES += \
...
core/java/android/operatechar/IOperateCharService.aidl \
3. 实现硬件访问服务
[->/frameworks/base/services/core/java/com/android/server/operatechar/OperateCharService.java]
package com.android.server.operatechar;
import android.content.Context;
import android.os.Handler;
import android.operatechar.IOperateCharService;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.util.Slog;
import android.os.RemoteException;
public class OperateCharService extends IOperateCharService.Stub {
private static final String TAG = "OperateCharService";
private Context mContext;
private int mNativePointer;
public OperateCharService(Context context) {
super();
Slog.i(TAG, "Start to init OperateChar Service");
mContext = context;
mNativePointer = init_native();
if(mNativePointer ==0) {
Slog.i(TAG, "Failed to init OperateChar Service");
}
}
protected void finalize() throws Throwable {
if(mNativePointer ==0) {
return;
}
finalize_native(mNativePointer);
super.finalize();
}
public String read(int maxLength) throws RemoteException
{
if(mNativePointer ==0) {
Slog.e(TAG, "OperateChar Service is not init");
return null;
}
int length;
byte[] buffer = new byte[maxLength];
length = read_native(mNativePointer, buffer);
try {
return new String(buffer, 0, length, "UTF-8");
} catch (Exception e) {
Slog.e(TAG, "read buffer error!");
return null;
}
}
public void write(String mString) throws RemoteException
{
if(mNativePointer ==0) {
Slog.e(TAG, "OperateChar Service is not init");
}
byte[] buffer = mString.getBytes();
write_native(mNativePointer, buffer);
}
private static native int init_native();
private static native void finalize_native(int ptr);
private static native int read_native(int ptr, byte[] buffer);
private static native void write_native(int ptr, byte[] buffer);
}
硬件访问服务 OperateCharService 继承了 IOperateCharService.Stub类,并且实现了IOperateCharService 接口的 read 和 write 函数。
4. 实现硬件访问服务的JNI方法
[->frameworks/base/services/core/jni/com_android_server_operatechar_OperateCharService.cpp]
#define LOG_TAG "OperateCharServiceJNI"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/operate_char.h>
#include <stdio.h>
namespace android
{
operatechar_device_t* operatechar_dev;
static inline int operatechar_device_open(const hw_module_t* module, struct operatechar_device_t** device) {
return module->methods->open(module, OPERATE_CHAR_HARDWARE_DEVICE_ID, (struct hw_device_t**)device);
}
static jint init_native(JNIEnv *env, jobject /* clazz */)
{
int err;
opetatechar_module_t* module;
operatechar_device_t* dev = NULL;
err = hw_get_module(OPERATE_CHAR_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == 0) {
if(operatechar_device_open(&(module->common), &dev) ==0) {
ALOGE("Device mychar is open.");
return (jint)dev;
}
ALOGE("Failed to open device mychar!!!");
return 0;
}
ALOGE("Failed to get HAL mychar!!!");
return 0;
}
static void finalize_native(JNIEnv *env, jobject /* clazz */, int ptr)
{
operatechar_device_t* dev = (operatechar_device_t*)ptr;
if (dev == NULL) {
return;
}
dev->close();
free(dev);
}
static int read_native(JNIEnv *env, jobject /* clazz */, int ptr, jbyteArray buffer)
{
operatechar_device_t* dev = (operatechar_device_t*)ptr;
if(!dev) {
ALOGE("Device mychar is not open.");
return 0;
}
int length;
jbyte* my_byte_array;
my_byte_array = env->GetByteArrayElements(buffer, NULL);
length = dev->read(dev, (char*) my_byte_array, env->GetArrayLength(buffer));
ALOGI("read data from hal: %s", (char *)my_byte_array);
env->ReleaseByteArrayElements(buffer, my_byte_array, 0);
return length;
}
static void write_native(JNIEnv *env, jobject /* clazz */, int ptr, jbyteArray buffer)
{
operatechar_device_t* dev = (operatechar_device_t*)ptr;
if(!dev) {
ALOGE("Device mychar is not open.");
return;
}
jbyte* my_byte_array;
my_byte_array = env->GetByteArrayElements(buffer, NULL);
dev->write(dev, (char*) my_byte_array);
ALOGI("write data to hal: %s", (char *)my_byte_array);
env->ReleaseByteArrayElements(buffer, my_byte_array, 0);
}
static JNINativeMethod method_table[] = {
{ "init_native", "()I", (void*)init_native },
{ "finalize_native", "(I)V", (void*)finalize_native },
{ "read_native", "(I[B)I", (void*)read_native },
{ "write_native", "(I[B)V", (void*)write_native }
};
int register_android_server_operatechar_OperateCharService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/operatechar/OperateCharService",
method_table, NELEM(method_table));
};
}
在init_native 函数中,首先通过Android HAL 层提供的hw_get_module 函数来加载模块ID为OPERATE_CHAR_HARDWARE_MODULE_ID 的HAL 层模块,最终返回一个hw_module_t接口给init_native函数,这个hw_module_t接口实际上指向的是自定义的一个HAL 层模块对象,即一个opetatechar_module_t对象。接着调用函数operatechar_device_open 开打开设备ID为OPERATE_CHAR_HARDWARE_DEVICE_ID 的硬件设备,而后者又是通过前面获取的hw_module_t 接口的操作方法列表中的open函数来打开指定的硬件设备的。HAL 层operatechar模块中的open函数设置为operatechar_open,这个函数最终返回一个operatechar_device_t接口,最后将operatechar_device_t接口转换成一个整型句柄值返回给调用者。 method_table 是一个JNI 方法表,将init_native等函数注册为相应的函数。最后通过jniRegisterNativeMethods 函数把JNI 方法表的函数注册到Java 虚拟机中。 还需要修改frameworks/base/services/core/jni/onload.cpp文件,在里面添加com_android_server_operatechar_OperateCharService函数的声明和调用。
[->frameworks/base/services/core/jni/onload.cpp]
namespace android {
...
int register_android_server_operatechar_OperateCharService(JNIEnv* env);
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
...
register_android_server_operatechar_OperateCharService(env);
}
onload.cpp 文件实现在 libandroid_servers 模块中,当系统加载 libandroid_servers 模块时,就会调用实现在onload.cpp文件中的JNI_Onload 函数,这样就可以将前面定义的JNI 方法注册到Java虚拟机中了。 最后还需要修改frameworks/base/services/core/jni/Android.mk,将新添加的jni文件加入编译。
LOCAL_SRC_FILES += \
...
$(LOCAL_REL_DIR)/com_android_server_operatechar_OperateCharService.cpp \
...
5. 实现硬件访问服务客户端
前面实现的OperateCharService服务与Android系统的其他核心服务如AMS都是运行在system_server 进程中的,其他进程如app想访问该服务,需要通过Binder进行进程间通信,因此该小节实现OperateCharService服务的客户端:OperateCharManager。
[->frameworks/base/core/java/android/operatechar/OperateCharManager.java]
package android.operatechar;
import android.content.Context;
import android.os.RemoteException;
import android.operatechar.IOperateCharService;
import android.util.Slog;
public class OperateCharManager
{
private static final String TAG = "OperateCharManager";
IOperateCharService mService;
public String read(int maxLength) {
try {
return mService.read(maxLength);
} catch (RemoteException e) {
Slog.e(TAG, "read error!");
return null;
}
}
public void write(String mString) {
try {
mService.write(mString);
} catch (RemoteException e) {
Slog.e(TAG, "write error!");
}
}
public OperateCharManager(Context context, IOperateCharService service) {
mService = service;
}
}
6. 启动访问硬件服务
[->frameworks/base/core/java/android/app/SystemServiceRegistry.java]
import android.operatechar.OperateCharManager;
import android.operatechar.IOperateCharService;
...
final class SystemServiceRegistry {
...
static {
...
registerService(Context.OPERATECHAR_SERVICE, OperateCharManager.class,
new CachedServiceFetcher<OperateCharManager>() {
@Override
public OperateCharManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.OPERATECHAR_SERVICE);
IOperateCharService service = IOperateCharService.Stub.asInterface(b);
if (service == null) {
return null;
}
return new OperateCharManager(ctx, service);
}});
}
...
registerService方法在 Android 系统起来后注册了 OperateCharService 服务,并且将 OperateCharManager 与OperateCharService 服务关联了起来,意味着 APP 调用 OperateCharManager 的 read 或 write 方法都会跨进程调用 OperateCharService 服务的 read 或 write 方法,然后通过 JNI 调用到 HAL 层,再到 内核的驱动程序访问 设备节点 /dev/mychar。
[->frameworks/base/services/java/com/android/server/SystemServer.java]
import com.android.server.operatechar.OperateCharService;
...
public final class SystemServer {
...
private void startOtherServices() {
...
try {
Slog.i(TAG, "OperateChar Service");
operatechar = new OperateCharService(context);
Slog.i(TAG, "Add OperateChar Service");
ServiceManager.addService(Context.OPERATECHAR_SERVICE, operatechar);
Slog.i(TAG, "OperateChar Service Succeed!");
} catch (Throwable e) {
Slog.e(TAG, "Failure starting OperateChar Service", e);
}
...
Android 系统起来后,会调用 SystemServer.java 的相关方法启动Android系统的核心服务,在 startOtherServices 方法中会启动优先级低一点的系统服务,因此可以在 startOtherServices 方法中将 OperateCharService 启动并且添加到 ServiceManager 进行管理。
7. 添加Selinux策略权限
到目前为止已经实现了访问硬件服务的整套流程了,最后还需要修改 Selinux 策略,如果不修改,即使/dev/mychar设备节点有读写权限,但由于Selinux策略,访问硬件服务依然是不能访问/dev/mychar设备节点的。 由于我使用的是qcom高通的芯片平台,因此在高通平台下相关的文件进行修改。
[->device/qcom/sepolicy/common/device.te]
type mychar_device, dev_type;
定义一个mychar_device类型,其类型继承于dev_type。
[->device/qcom/sepolicy/common/file_contexts]
/dev/mychar u:object_r:mychar_device:s0
将设备节点 /dev/mychar 的类型转为mychar_device,这一步的目的是只针对/dev/mychar设备节点开放相应的Selinux权限。
[->device/qcom/sepolicy/common/system_server.te]
allow system_server mychar_device:chr_file { open read write ioctl };
允许 system_server 进程对 mychar_device 类型的设备节点有 open read write ioctl 的操作权限。访问硬件服务是运行在system_server 进程中的。
[->device/qcom/sepolicy/common/service_contexts]
operate_char u:object_r:operate_char_service:s0
operate_char 就是 添加的访问硬件服务 OperateCharService,将 operate_char 转为 operate_char_service 类型。
[-> device/qcom/sepolicy/common/service.te]
type operate_char_service, app_api_service, system_server_service, service_manager_type;
如果还有报错,可以在logcat抓取的log搜avc关键字,查看缺少什么权限,就开放什么权限就行,
8. 更新系统API和编译
由于在framework 中添加了系统的 public 级别的方法,因此在全编译前需要更新API,执行指令如下:
make update-api
然后再执行全编译指令。
9. 编写APP测试验证
核心代码如下:
import android.operatechar.OperateCharManager;
...
// 获取OperateCharManager
OperateCharManager oc = (OperateCharManager)getSystemService(Context.OPERATECHAR_SERVICE);
try {
oc.write("Hello OperateChar");
Log.d("vane", "Service returned: " + om.read(32));
}
catch (Exception e) {
Log.d("vane", "FAILED to call service");
e.printStackTrace();
}
10. 总结
到此为止,已经将这个系列完成了,调用链如下:
APP调用->访问硬件服务客户端OperateCharManager->访问硬件服务OperateCharService->HAL 层模块operatechar->内核驱动程序mychar。
该系列主要参考罗升阳的《Android系统源代码情景分析》的第二章:硬件抽象层。