我们编写的hello设备驱动是运行在内核层的。我们写的测试程序运行在用户空间,但却是用C语言编写。Android应用使用JAVA编写,想要在Android中实现应用和驱动的交互,也就是实现JAVA代码和c语言的交互,这其中必然会使用到JNI技术。
由于服务单独运行在一个进程中,而且会为运行在不同进程的服务或者应用提供接口。那么他们之间的交互,必然会用到IPC,具体到Android中,就是Binder了,而Binder在Android体系的JAVA层的具体体现,就是我们经常听说或者使用的AIDL。
下面我们将首先编写jni部分的代码,用作JAVA层的服务和Native层交互的桥梁。然后写JAVA层的服务,最后为了让这个服务用起来方便一点,我们再写一个Manager并将它注册到系统中。
JNI编写
到frameworks/base/services/core/jni目录,新com_android_server_HelloService.cpp文件文件如此命名,表示它能被frameworks/base/services/java/com/android/server目录的HelloService调用,这个HelloSrvice就是我们稍后要编写的服务。
驱动只提供了read和write两个方法,那么对应的jni层也需要实现这两个方法。在与设备交互之前需要打开设备,再实现一个打开设备的init方法就OK了。
namespace android
{
int fd = -1;
int val = -1;
// 初始化设备
static jint hello_init(JNIEnv* env, jobject clazz)
{
fd = open("/dev/hello",O_RDWR);
}
static jint hello_read(JNIEnv* env, jobject clazz)
{
return read(fd, &val, sizeof(val));
}
static void hello_write(JNIEnv* env, jobject clazz,jint w_val)
{
write(fd, &w_val, sizeof(w_val));
}
三个方法的实现,只是用JNI的方式对操作驱动的方法做了一层包装。实现方式和上一篇文章写的C测试程序大同小异。
然后再把我们实现的方法列出来,并注册到JNI。
/*JNI方法表*/
static const JNINativeMethod method_table[] = {
{"native_init", "()I", (void*)hello_init},
{"native_read", "(I)V", (void*)hello_read},
{"native_write", "()I", (void*)hello_write},
};
/*注册JNI方法*/
int register_android_server_HelloService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));
}
至此,JNI部分就写完了,至于为什么这样写,还有没有其他的实现方法,我们后续再研究。
然后修改同目录下的onload.cpp文件,增加register_android_server_HelloService的函数声明
namespace android{
...
int register_android_server_HelloService(JNIEnv* env);
在JNI_onLoad函数增加调用
extern "C" jint JNI_onLoad(JavaVM* vm, void* reserved)
{
register_android_server_HelloService(env)
}
这样,在Android系统初始化时,就会自动加载该JNI方法调用表,进而被HelloService调用到。
服务
在frameworks/base/core/java/android/os目录新建IHelloService.aidl接口定义文件。然后书写如下代码
package android.os
interface IHelloService
{
void writeVal(int val);
int readVal();
}
我们要实现writeVal和readVal两个方法。
然后返回到 frameworks/base目录,在Android.mk文件LOCAL_SRC_FILES += /后边部分增加一行,使IHelloService.aidl被编译到。
core/java/android/os/IHelloService.aidl /
在aosp/frameworks/base/services/java/com/android/server新建HelloService.java文件
public class HelloService extends IHelloService.Stub {
HelloService() {
native_init();
}
public void writeVal(int val) {
native_write(val);
}
public int readVal() {
int rVal = native_read();
return rVal;
}
private static native int native_init();
private static native void native_write(int val);
private static native int native_read();
};
HelloService继承自IHelloService.Stub,以具有使用Binder进行IPC的能力。IHelloService.Stub会在IHelloService.aidl被编译后自动生成。然后就是对JNI方法进行包装了。
最后在/home/windcake/Documents/aosp/frameworks/base/services/java/com/android/server中的
SystemServer.java文件下的startOtherServices方法里加上如下代码
Slog.i(TAG, "Hello Service");
ServiceManager.addService("hello", new HelloService());
这样在Android系统启动的过程中就会加载这行代码,并把名称为hello的HelloService注册到ServiceManager中,提供给应用层调用了。
进一步封装
还记得刚入行的时候,看《第一行代码》学ListView的使用,要在相应Adapter中的getView方法中加载布局。会使用到如下语句拿到LayoutInflater,然后调用LayoutInflater的inflate方法去加载。
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
或者需要手动干预一下键盘,让它出现或者隐藏地更聪明一点,就需要调用InputMethodManager的方法。而获取InputMethodManager实例的代码如下
InputMethodManager imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
可见Android系统是提供了getSystemService方法来帮我们快速拿到系统服务的。假如HelloService也能被应用层如此调用。那么代码应该是这样写的。
HelloManager helloManager = (HelloManager)context.getSystemService(Context.HELLO_SERVICE);
那么,为了让HelloService也能被应用层如此方便的使用到,我们还需要多写几行代码了。
首先去Context.java文件里边添加HELLO_SERVICE静态变量,它所在目录是
/aosp/frameworks/base/core/java/android/content
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.app.HelloManager} for accessing the helloservice.
*
* @see #getSystemService
* @hide
*/
public static final String HELLO_SERVICE = "hello";
然后在/aosp/frameworks/base/core/java/android/app目录下新建HelloManager.java文件
public class HelloManager
{
IHelloService mService;
public HelloManager(Context context, IHelloService service)
{
mService = service;
}
public void writeVal(int val)
{
try
{
mService.writeVal(val);
} catch (RemoteException e)
{
e.printStackTrace();
}
}
public int readVal()
{
try
{
return mService.readVal();
} catch (RemoteException e)
{
e.printStackTrace();
}
return -1;
}
}
在同级目录下SystemServiceRegistry.java中的静态代码块里增加如下代码
static
{
registerService(Context.HELLO_SERVICE, HelloManager.class,new CachedServiceFetcher<HelloManager>() {
@Override
public HelloManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.HELLO_SERVICE);
if (b == null) {
return null;
}
return new HelloManager(ctx,IHelloService.Stub.asInterface(b));
}});
}
这样,当Android系统启动之后,我们就能用上述方式拿到HelloManager实例,并通过HelloManager调用HelloService的对应方法了。