为设备hello增加JNI方法并在Frameworks层增加硬件访问服务

我们编写的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的对应方法了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值