在上一篇博客中,我介绍了如何在Android HAL层中实现GPIO接口的驱动。在这篇博客中,将继续探讨如何在JNI层中实现GPIO接口的函数,以及如何在Java层中添加Server调用JNI层的函数。
JNI层的GPIO接口函数
JNI层是Java层和Native层之间的桥梁,它提供了一组标准的接口和数据类型,用于在Java层和Native层之间传递参数和返回值。JNI层的主要作用是使得Java层可以使用Native层的功能,例如访问硬件设备、执行系统命令等。
为了在JNI层中实现GPIO接口的函数步骤:
- 定义Java类和Native方法
- 实现Native方法
- 注册Native方法
下面我们来具体看一下每个步骤的细节。
实现Native方法
我们需要在frameworks/base/services/core/jni/com_android_server_GpioService.cpp
文件中实现上述定义的Java类中的Native方法。这些Native方法主要是通过调用Android HAL层提供的GPIO接口函数来与底层的GPIO驱动进行通信。为了实现一个Native方法,我们需要遵循以下的格式:
JNIEXPORT 返回值类型 JNICALL Java_完整类名_方法名(JNIEnv *env, jobject obj, ...);
env
是一个指向JNI环境的指针,它提供了一些JNI相关的函数和变量,例如获取和设置Java对象的字段和方法等。obj
是一个指向Java对象的引用,它可以用于访问Java对象的属性和方法。...
是可选的参数列表,它们对应了Java方法的参数。在我们的例子中,我们实现了以下几个Native方法:
#include "jni.h" #include <nativehelper/JNIHelp.h> #include "android_runtime/AndroidRuntime.h" #include <utils/misc.h> #include <utils/Log.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <hardware/gpio_hal.h> namespace android { static struct gpio_device_t* gpioDevice; jint gpioOpen(JNIEnv *env, jobject cls) { jint err; hw_module_t* module; hw_device_t* device; ALOGI("native gpioOpen ..."); // hw_get_module finds the library by "gpio" (this is the id of hal) err = hw_get_module("gpio", (hw_module_t const**)&module); if(err == 0) { // Get device : module->methods->open err = module->methods->open(module, NULL, &device); if(err == 0) { // Call gpio_open gpioDevice = (gpio_device_t *)device; return gpioDevice->gpio_open(gpioDevice); } else { return -1; } } return -1; } void gpioClose(JNIEnv *env, jobject cls) { ALOGI("native gpioClose ..."); } jint gpioWrite(JNIEnv *env, jobject cls, jint gpio, jint value) { ALOGI("native gpioWrite gpio=%d, value=%d", gpio, value); return gpioDevice->gpio_write(gpioDevice, gpio, value); } jint gpioRead(JNIEnv *env, jobject cls, jint gpio) { ALOGI("native gpioRead gpio=%d", gpio); return gpioDevice->gpio_read(gpioDevice, gpio); } jint gpioDirection(JNIEnv *env, jobject cls, jint gpio, jint direction, jint value) { ALOGI("native gpioRead gpio=%d", gpio); return gpioDevice->gpio_direction(gpioDevice, gpio, direction, value); } jint gpioRegKeyEvent(JNIEnv *env, jobject cls, jint gpio) { ALOGI("native gpioRegKeyEvent gpio=%d", gpio); return gpioDevice->gpio_reg_key_event(gpioDevice, gpio); } jint gpioUnregKeyEvent(JNIEnv *env, jobject cls, jint gpio) { ALOGI("native gpioUnregKeyEvent gpio=%d", gpio); return gpioDevice->gpio_unreg_key_event(gpioDevice, gpio); } jint gpioGetNumber(JNIEnv *env, jobject cls) { ALOGI("native gpioGetNumber"); return gpioDevice->gpio_get_number(gpioDevice); } // Register native methods static const JNINativeMethod methods[] = { {"native_gpioOpen", "()I", (void *)gpioOpen}, {"native_gpioClose", "()V", (void *)gpioClose}, {"native_gpioWrite", "(II)I", (void *)gpioWrite}, {"native_gpioRead", "(I)I", (void *)gpioRead}, {"native_gpioDirection", "(III)I", (void *)gpioDirection}, {"native_gpioRegKeyEvent", "(I)I", (void *)gpioRegKeyEvent}, {"native_gpioUnregKeyEvent", "(I)I", (void *)gpioUnregKeyEvent}, {"native_gpioGetNumber", "()I", (void *)gpioGetNumber}, }; int register_android_server_GpioService(JNIEnv *env) { // The Java method corresponding to the local method GpioService return jniRegisterNativeMethods(env, "com/android/server/GpioService", methods, NELEM(methods)); } }
注册Native方法
我们需要在frameworks/base/services/core/jni/onload.cpp
文件中注册Native方法,以便Java层可以通过JNI接口来调用Native层的函数。这一步主要是通过调用jniRegisterNativeMethods
函数,传入Java类的完整名字和Native方法的数组,来完成注册。代码如下:
//--------这一步可以参考其他jni的添加 , 看源码就懂了。 int register_android_server_GpioService(JNIEnv* env); register_android_server_GpioService(env);
Service层的GPIO接口服务
Service层是Android系统中提供各种系统服务的层级,它主要是通过Binder机制来与应用层进行通信。Binder机制是一种跨进程通信(IPC)的方式,它可以实现不同进程之间的数据传输和方法调用。Binder机制的主要优点是高效、安全和灵活。
为了在Service层中实现GPIO接口的服务,我们需要遵循以下几个步骤:
- 定义GPIO服务的AIDL接口
- 实现GPIO服务的Java类
定义GPIO服务的AIDL接口
首先,需要在frameworks/base/core/java/android/btf/IGpioService.aidl
文件中定义一个GPIO服务的AIDL接口IGpioService
,它声明了一些GPIO相关的方法,例如打开、关闭、读写、方向设置等。AIDL接口是一种用于描述Binder通信协议的语言,它可以自动地生成Java和Native层之间的代理和存根代码,从而简化了IPC的过程。代码如下:
package android.btf; interface IGpioService { int gpioWrite(int gpio, int value); int gpioRead(int gpio); int gpioDirection(int gpio, int direction, int value); int gpioRegKeyEvent(int gpio); int gpioUnregKeyEvent(int gpio); int gpioGetNumber(); }
实现GPIO服务的Java类
我们需要在frameworks/base/services/core/java/com/android/server/GpioService.java
文件中实现一个GPIO服务的Java类GpioService
,它继承自IGpioService.Stub
类,并实现了IGpioService
接口。这个类主要是用于提供给应用层调用的服务类,它包含了一些GPIO相关的Native方法,例如打开、关闭、读写、方向设置等。这些Native方法都是在Native层的C代码中完成的,而不是在Java代码中。为了声明一个Native方法,我们需要在方法前加上native
关键字,并且不需要提供方法体。代码如下:
package com.android.server; import android.btf.IGpioService; public class GpioService extends IGpioService.Stub { private static final String TAG = "GpioService"; /* call native c function to access hardware */ public int gpioWrite(int gpio, int value) throws android.os.RemoteException { return native_gpioWrite(gpio, value); } public int gpioRead(int gpio) throws android.os.RemoteException { return native_gpioRead(gpio); } public int gpioDirection(int gpio, int direction, int value) throws android.os.RemoteException { return native_gpioDirection(gpio, direction, value); } public int gpioRegKeyEvent(int gpio) throws android.os.RemoteException { return native_gpioRegKeyEvent(gpio); } public int gpioUnregKeyEvent(int gpio) throws android.os.RemoteException { return native_gpioUnregKeyEvent(gpio); } public int gpioGetNumber() throws android.os.RemoteException { return native_gpioGetNumber(); } public GpioService() { native_gpioOpen(); } public static native int native_gpioOpen(); public static native void native_gpioClose(); public static native int native_gpioWrite(int gpio, int value); public static native int native_gpioRead(int gpio); public static native int native_gpioDirection(int gpio, int direction, int value); public static native int native_gpioRegKeyEvent(int gpio); public static native int native_gpioUnregKeyEvent(int gpio); public static native int native_gpioGetNumber(); }
总结
本篇文章介绍了如何在Android native中添加JNI 对Hardware的接口调用 , 基本到这我们就可以在Android应用中利用我们添加的Gpio Server来调用 , 最后一篇作为补充 简单讲解一下封装和应用如何调用。